@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);
也可以使用代码迁移工具 http://www.slf4j.org/migrator.html,但是有很多局限性。也需要自己手动备份代码,不建议使用。
网上看到有人说:试了下其他的几个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();
Listv_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源代码请参考:
https://logback.qos.ch/xref/index.html
下面就一起学习和总结下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中。
前两个包在 http://www.slf4j.org/download.html 处下载,后一个包在http://logging.apache.org/log4j/1.2/download.html 下载,可能包文件名中的版本号有些差,不要紧。log4j.properties以前该是怎么写,现在还是怎么写,比如一个最简单的内容,只向控制台输出日志信息,如下:
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-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。
1. 从org.slf4j包导入Logger和LoggerFactory
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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");
}
}
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."); } } }