slf4j.Logger的全面講解及e.getMessage()為何為空
測試:
@Test publicvoid testDelegateHandleRequestFour2() { Filefile = new File("E:\\study\\text.txt"); FileReaderfr = null; try{ fr= new FileReader(file); }catch (FileNotFoundException e) { //log.error("測試 : ",e.getMessage()); //log.error("測試 :"+e.getMessage()); //log.err(“測試:”+e); //log.error("測試 : ",e); //log.error("測試:%s",e.getMessage(), e); // e.printStackTrace(); } }
1、log.error("測試:" ,e.getMessage()); // e.getMessage()為空,不會列印異常資訊
2、log.error("測試:" +e.getMessage()); // e.getMessage()只是獲取了異常的詳細訊息字串,沒有堆疊資訊。
3、log.error("測試:" +e); //只會打印出異常名稱,不會列印堆疊資訊
4、log.error("測試:%s",e.getMessage(), e); //在slf4j中 %s不是字串轉換符,不起作用 %s會原樣輸出到日誌。 需要刪除。
5、可以使用e.printStackTrace() 列印異常的堆疊資訊,但是後面不要在使用log.error("測試:" ,e); //這樣異常堆疊資訊會列印重複
可以使用log.error("查詢賬戶資產時:" +e.getMessage()); 這種只會列印異常的字串。
6、只認緊挨著的{}符號
log.error("測試 ,姓名:{{}} 年齡:{}"+e,name,age);
網上看到有人說:試了下其他的幾個runtime異常,發現getMessage都是為空的,之後又去試了下SQLException和IOException,發現者兩種異常的在catch的時候getMessage是不為null的。由此覺得runtime異常發生的時候JVM呼叫的是父類無參的構造器。
public Exception() { super(); }
而SQLException和IOException異常發生的時候JVM呼叫的是父類有參的構造器
publicException(String message) { super(message); }
所以SQLException和IOException的getMessage不為null,而runtime異常卻為空。
但是實際測試結果:
@Test publicvoid testDelegateHandleRequestOfSQLException() { Modeluser = null; try{ StringBuffersql = new StringBuffer(); List<v_user_users>v_user_users_list = null; sql.append(SQLTempletes.SELECT); sql.append(SQLTempletes.V_USER_USERS); sql.append("where andt_users.id = ?"); //讓sql語法錯誤 EntityManagerem = JPA.em(); Query query = em.createNativeQuery(sql.toString(),v_user_users.class); query.setParameter(1, 1); query.setMaxResults(1); v_user_users_list = query.getResultList(); if(v_user_users_list.size() > 0){ user = v_user_users_list.get(0); } }catch(Exception e) { //e.printStackTrace(); log.info("使用者setId填充時(lazy=true):",e.getMessage()); } }
使用log.info("使用者setId填充時(lazy=true):",e.getMessage());
使用log.info("使用者setId填充時(lazy=true):",e);
說明在報sql異常或IO異常的時候getMessage在第二個引數時也是null。
這種寫法log.error("測試 : ",e.getMessage()); 為null的原因最終原因其實是:Log的方法檢測最後一個引數是不是一個Throwable ,如果是則列印異常的堆疊資訊,如果不是就當成前面format的引數,如果沒有{}佔位符,則忽略。
error方法的api如下:
public void error(String msg);
public void error(String format, Object arg);
public void error(String format, Object arg1,Object arg2);
public void error(String format, Object...arguments);
public void error(String msg, Throwable t);
public void error(Marker marker, String msg);
public void error(Marker marker, String format,Object arg);
public void error(Marker marker, String format,Object arg1, Object arg2);
public void error(Marker marker, String format,Object... arguments);
public void error(Marker marker, String msg,Throwable t);
參考資料:
Slf4j官方網站:
https://www.slf4j.org/
Slf4j原始碼請參考:
下面就一起學習和總結下slf4jslf4j的使用與繫結原理
slf4J的使用
前文說到了,單獨的slf4j是不能工作的,必須帶上其他具體的日誌實現方案。就以apache的log4j作為具體日誌實現方案為例,如果在工程中要使用slf4j作為介面,並且要用log4j作為具體實現方案,那麼我們需要做的事情如下:(下面的xxx表示具體版本號)
l 將slf4j-api-xxx.jar加入工程classpath中;
l 將slf4j-log4jxx-xxx.jar加入工程classpath中;
l 將log4j-xxx.jar加入工程classpath中;
l 將log4j.properties(log4j.xml)檔案加入工程classpath中。
log4j.rootLogger=DEBUG,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-ddHH:mm:ss,SSS} [%c]-[%p] %m%n
使用 SLF4J 的程式碼:
public class TestSlf4j {
private static finalLogger logger = LoggerFactory.getLogger(TestSlf4j.class);
public static voidmain(String[] args) {
logger.info("Hello{}","SLF4J");
}
}
執行它,控制檯輸出:
2017-05-23 09:14:51,390[com.unmi.TestSlf4j]-[INFO] Hello SLF4J
slf4j的工作原理:
首先,slf4j-api作為slf4j的介面類,使用在程式程式碼中,這個包提供了一個Logger類和LoggerFactory類,Logger類用來打日誌,LoggerFactory類用來獲取Logger;slf4j-log4j是連線slf4j和log4j的橋樑,怎麼連線的呢?我們看看slf4j的LoggerFactory類的getLogger函式的原始碼:
/**
* Return alogger named corresponding to the class passed as parameter, using
* the staticallybound {@link ILoggerFactory} instance.
*
* @paramclazz the returned logger will be named after clazz
* @returnlogger
*/
public static Logger getLogger(Class clazz) {
returngetLogger(clazz.getName());
}
/**
* Return alogger named according to the name parameter using the statically
* bound{@link ILoggerFactory} instance.
*
* @paramname The name of the logger.
* @returnlogger
*/
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
returniLoggerFactory.getLogger(name);
}
publicstatic ILoggerFactory getILoggerFactory() {
if(INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
performInitialization();
}
switch(INITIALIZATION_STATE) {
caseSUCCESSFUL_INITIALIZATION:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
caseNOP_FALLBACK_INITIALIZATION:
return NOP_FALLBACK_FACTORY;
caseFAILED_INITIALIZATION:
thrownew IllegalStateException(UNSUCCESSFUL_INIT_MSG);
caseONGOING_INITIALIZATION:
//support re-entrant behavior.
//See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
return TEMP_FACTORY;
}
throw newIllegalStateException("Unreachable code");
}
追蹤到最後,發現LoggerFactory.getLogger()首先獲取一個ILoggerFactory介面,然後使用該介面獲取具體的Logger。獲取ILoggerFactory的時候用到了一個StaticLoggerBinder類,仔細研究我們會發現StaticLoggerBinder這個類並不是slf4j-api這個包中的類,而是slf4j-log4j包中的類,這個類就是一箇中間類,它用來將抽象的slf4j變成具體的log4j,也就是說具體要使用什麼樣的日誌實現方案,就得靠這個StaticLoggerBinder類。再看看slf4j-log4j包中的這個StaticLoggerBinder類建立ILoggerFactory長什麼樣子:
private final ILoggerFactory loggerFactory;
private StaticLoggerBinder() {
loggerFactory = new Log4jLoggerFactory();
try {
Levellevel = Level.TRACE;
} catch(NoSuchFieldError nsfe) {
Util
.report("This version of SLF4J requires log4j version 1.2.12 orlater. See also http://www.slf4j.org/codes.html#log4j_version");
}
}
public ILoggerFactory getLoggerFactory() {
returnloggerFactory;
}
可以看到slf4j-log4j中的StaticLoggerBinder類建立的ILoggerFactory其實是一個 org.slf4j.impl.Log4jLoggerFactory ,這個類的getLogger函式是這樣的:
public Logger getLogger(String name) {
Loggerslf4jLogger = loggerMap.get(name);
if(slf4jLogger != null) {
returnslf4jLogger;
} else {
org.apache.log4j.Logger log4jLogger;
if(name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME))
log4jLogger = LogManager.getRootLogger();
else
log4jLogger = LogManager.getLogger(name);
LoggernewInstance = new Log4jLoggerAdapter(log4jLogger);
LoggeroldInstance = loggerMap.putIfAbsent(name, newInstance);
returnoldInstance == null ? newInstance : oldInstance;
}
}
就在其中建立了真正的 org.apache.log4j.Logger ,也就是我們需要的具體的日誌實現方案的Logger類。就這樣,整個繫結過程就完成了。
SLF4J是為各種loging APIs提供一個簡單統一的介面,從而使得終端使用者能夠在部署的時候配置自己希望的loging APIs實現。準確的說,slf4j並不是一種具體的日誌系統,而是一個使用者日誌系統的facade,允許使用者在部署最終應用時方便的變更其日誌系統。
在系統開發中,統一按照slf4j的API進行開發,在部署時,選擇不同的日誌系統包,即可自動轉換到不同的日誌系統上。比如:選擇JDK自帶的日誌系統,則只需要將slf4j-api-1.5.10.jar和slf4j-jdk14-1.5.10.jar放置到classpath中即可,如果中途無法忍受JDK自帶的日誌系統了,想換成log4j的日誌系統,僅需要用slf4j-log4j12-1.5.10.jar替換slf4j-jdk14-1.5.10.jar即可(當然也需要log4j的jar及配置檔案)
SLF4J獲得logger物件:
private staticfinal Logger logger = LoggerFactory.getLogger(Test.class);
LOG4J獲得logger物件:
private staticLogger logger = Logger.getLogger(Test.class);
總結:
1. 大部分人在程式裡面會去寫logger.error(exception),其實這個時候log4j會去把這個exception tostring。真正的寫法應該是logger(message.exception);而slf4j就不會使得程式設計師犯這個錯誤。
2. log4j間接的在鼓勵程式設計師使用string相加的寫法,而slf4j就不會有這個問題。
3. 你可以使用logger.error("{}is+serviceid",serviceid);
4. 使用slf4j可以方便的使用其提供的各種具體的實現的jar。(類似commons-logger)
5. 從commons--logger和log4j merge非常方便,slf4j也提供了一個swing的tools來幫助大家完成這個merge。
slf4j的用法:
1. 從org.slf4j包匯入Logger和LoggerFactory
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
2. 宣告日誌類private final Logger logger = LoggerFactory.getLogger(LoggingSample.class);
3. 簡單用法
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public classHelloWorld{
public staticvoid main(String[] args){
Logger logger
= LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
4.輸出帶引數的日誌資訊,使用{}佔位符,方法中傳入引數
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Wombat { final Logger logger = LoggerFactory.getLogger(Wombat.class); Integer t; Integer oldT; public void setTemperature(Integer temperature) { oldT = t; t = temperature; logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT); if(temperature.intValue() > 50) { logger.info("Temperature has risen above 50 degrees."); } } }
相關推薦
slf4j.Logger的全面講解及e.getMessage()為何為空
測試: @Test publicvoid testDelegateHandleRequestFour2() { Filefile = new File("E:\\study\\text.txt"); FileReaderfr =
Java 判斷實體物件及所有屬性是否為空
1、判斷實體物件是否為空 2、判斷物件所有屬性是否為空 3、特別注意,實體類中如果有基本資料型別,會影響判斷 package com.liuxd.object; import org.apache.commons.lang3.StringUtils; import java.lang
SQL註入原理講解及防範
ant htm part 無效 快樂 日常 field users lib 原文地址:http://www.cnblogs.com/rush/archive/2011/12/31/2309203.html 1.1.1 摘要 日前,國內最大的程序員社區CSDN網站
nginxlocation重要語法講解及實踐檢驗
linuxnginx常見日誌收集及分析工具有rsyslog,awstats,flume,elk,storm等1 nginx location作用: location指令的作用是可以根據用戶請求的URL來執行不同的應用,就是根據用戶請求的網站地址URL匹配,匹配成功即進行相關的操作。2 loc
NAT穿透的詳細講解及分析
設置 網通 我會 什麽 報告 pub 後端 火墻 聯系 原文地址:http://bbs.pediy.com/thread-131961.htm 一、什麽是NAT?為什麽要使用NAT?NAT是將私有地址轉換為合法IP地址的技術,通俗的講就是將內網與內網通信時怎麽將內網私有IP
Oozie與Coordinator調度講解及系統時區配置與定時觸發兩種配置方式
-- track eno star es2017 alt coo 之前 res 1:修改本地linux時區 查看時區 - 號代表西 + 號 代表東 北京時間是東八區 設置時區的配置文件所在位置 1 cd /usr/share/zoneinfo/
Android XListView實現原理講解及分析
就是 指定 不同 true -h -name 修改 一個 部分 XListview是一個非常受歡迎的下拉刷新控件,但是已經停止維護了。之前寫過一篇XListview的使用介紹,用起來非常簡單,這兩天放假無聊,研究了下XListview的實現原理,學到了很多,今天分享給大家。
修改org.slf4j.Logger時,修改默認配置文件位置
path trac reset tst try tex exit except new File logbackFile = new File(ConstantsTools.PATH_LOG_CONFIG);if (logbackFile.exists()) { Lo
activeMQ 講解及實戰
安全 持久化數據 sts 路徑 code rtu 默認值 字符 更新 #### 軟件架構項目中需要用到activeMQ 下載地址:http://activemq.apache.org/download.html #### 安裝教程需要安裝jdk環境activeMQ免安裝下載
【Hadoop 分布式部署 八:分布式協作框架Zookeeper架構功能講解 及本地模式安裝部署和命令使用 】
.gz 權限 實現 creat info 應用 data 就是 數據結構 What is Zookeeper 是一個開源的分布式的,為分布式應用提供協作服務的Apache項目 提供一個簡單的原語集合,以便與分布式應用可以在他之上構建更高層次的同步服務
e.getMessage() e.printStackTrace() 和e.printStackTrace() 小結
void equal ktr catch int 執行 [] stat trac 1 e.getMessage() ; 只會獲得異常的名稱。比如說NullPoint 空指針,就告訴你說是空指針 2.e.toString(): 獲得異常種類和錯誤信息 3.e.prin
MongoDB復制集搭建簡單講解及驗證
oca serve aac 復制 color water 介紹 集成 mongod 一.MongoDB復制集搭建介紹 二.復制集搭建搭建環境:Centos 7mongodb-linux-x86_64-3.2.12.tgz 1.解壓安裝mongodb到/usr/local/m
常見數論的講解及程式碼
文章目錄 整除 同餘 質數 Eratosthenes 篩法(埃氏篩法) 線性篩法 唯一分解定理 威爾遜定理 費馬小定理 GCD 拓展歐幾里得定理
AlexNet 講解及pytorch實現 ----1 AlexNet主要技術突破點
一. AlexNet網路結構 2012年,該網路ILSVRC-2012影象分類的冠軍,top-5的識別錯誤率為15.3%, 比第二名高出10個百分點。 下面是論文中的網路結構: 原始網路將模型分為兩部分,分開在兩個GPU上訓練,與下面合併的網路結構等價:
Django後臺管理系統講解及使用
大家在建立Django專案後,在根路由urls.py檔案中,會看到一行程式碼 from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls),]上面看到匯入的admin模組,就是本節所要說的主要內容。 主
全面屏及虛擬鍵適配說明
小米:https://dev.mi.com/console/doc/detail?pId=1160 官方:https://developer.android.com/guide/practices/screens_support#NewQualifiers 1. 前言 自2016年小
全面講解分析基於區鏈塊的實現貨幣中心化的商城報單系統
1、前言: 系統是基於區鏈塊分散式智慧合約技術,實現貨幣的去中心化、點對點無損無痕流通,讓流通產生價值,也可以通證(雙倍積分釋放)提現,對接交易所可用錢包轉出。年賺百萬,只要輕鬆轉動通證。 2、名稱說明: 商城】1、贈送積分套餐購物板塊 2、商城商家版(餘額和通證可以購物) 【註冊】
RecyclerView 全面使用及分析 - 基礎篇(一)
一、RecyclerView 介紹 在 RecyclerView 出來之前,大家都在使用 ListView、GridView,當然 RecyclerView 出來之後,基本上都轉向了 RecyclerView,從名字上可以看出,它能夠實現view 的複用,同樣 ListView
手把手教你ExtJS從入門到放棄——篇四(Ext.window元件API詳細講解及demo演示)
1.開啟API找到Ext--》window --》window 2.灰色齒輪代表是個元件,xtype是別名,就像前面MessageBox的別名是Msg一樣,使用別名是一樣的效果,點進原始碼可以看到是進行了引用的賦值 3.第一個元件:Ext.window.Windo
getCause()、e.getMessage()產生的結果
(1)e.getMessage() e.getMessage(); 只會獲得具體的異常名稱. 比如說NullPoint 空指標,就告訴你說是空指標... (2)e.getCause() (3)e.getCause().getMessage();