测试:

   @Testpublicvoid 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异常却为空。

但是实际测试结果:

@Testpublicvoid 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源代码请参考:

https://logback.qos.ch/xref/index.html

下面就一起学习和总结下slf4j

slf4j的使用与绑定原理

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的工作原理:

首先,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类。就这样,整个绑定过程就完成了。

Log4j和Slf4j的比较

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()为何为空相关推荐

  1. Maven : Maven和jenkins报错 ClassNotFoundException : org.slf4j.Logger

    1.美图 2.问题 执行一个flnk的jar报错 执行发现报错,然后也看不到错误信息,于是用java -jar去执行试试. 结果发现报错 ClassNotFoundException : org.sl ...

  2. slf4j logger

    slf4j logger 打印日志 1.引入slf4j 接口的logger和loggerFactory (即引入org.slf4j:slf4j-api的jar包以及slf4j-log4j12的jar包 ...

  3. python中的logger模块详细讲解

    logger 提供了应用程序可以直接使用的接口 handler将(logger创建的)日志记录发送到合适的目的输出 filter提供了细度设备来决定输出哪条日志记录 formatter决定日志记录的最 ...

  4. 浅谈slf4j,logger中的{}功能

    slf4j有一个common logger没有的功能,字符串中的{}会被替换,如下: logger.info("Hello {}","world");log.d ...

  5. python logger.debug_python中的logger模块详细讲解

    logger 提供了应用程序可以直接使用的接口 handler将(logger创建的)日志记录发送到合适的目的输出 filter提供了细度设备来决定输出哪条日志记录 formatter决定日志记录的最 ...

  6. org.slf4j.Logger中isTraceEnabled(),isDebugEnabled(),isInfoEnabled(),isWarnEnabled(),isErrorEnabled()

    log4j中log.isDebugEnabled(), log.isInfoEnabled()和log.isTraceEnabled()作用 项目在应用log4j打印Debug,Info和Trace级 ...

  7. spark2-submit在CDH环境下ClassNotFoundException:org.slf4j.logger

    1.视界 2. 背景 从集群的机器上拷贝了一个Spark客户端,结果直接执行spark2-submit命令报错,报错如下图 3.解决 我们,没做任何操作,客户在集群上装了一个spark-gateway ...

  8. Dubbo 源码分析 - 集群容错之 Cluster

    1.简介 为了避免单点故障,现在的应用至少会部署在两台服务器上.对于一些负载比较高的服务,会部署更多台服务器.这样,同一环境下的服务提供者数量会大于1.对于服务消费者来说,同一环境下出现了多个服务提供 ...

  9. lombok插件:Data自动get/set方法, Slf4j实现Logger的调用

    lombok插件:Data自动get/set方法, Slf4j实现Logger的调用 lombok.Data import lombok.Data; import org.hibernate.anno ...

最新文章

  1. 将页脚保持在页面的底部——Javascript+Css实现
  2. 「Python」在虚拟环境中使用pip
  3. java 职责链模式_Java中的责任链模式
  4. 1.5编程基础之循环控制 34 求阶乘的和
  5. 解决sql脚本文件太大无法打开的问题
  6. ATL 开发 COM 过程中的一些经验、问题总结
  7. 方舟生存进化服务器Linux,方舟生存进化官方服务器与私人服务器有什么区别
  8. WINDOWS BAT的命令,双引号位置要注意
  9. 微信下载录音文件(音轨分离 ffmpeg视频合成)
  10. 零基础自学计算机方法大全
  11. 华为手机自带的双系统模式,你知道吗?一部手机当两部使用
  12. 直播实录|百度大脑EasyDL·NVIDIA专场 部署专家
  13. “博客之星”年度评选
  14. 腾讯又一长达 8 年的服务下架。。。
  15. pap.er for mac高清壁纸下载后的储存位置在哪里?小编告诉你!!
  16. 洛谷日报 2020年3月前索引
  17. 唱情歌的人和听的人为何那么认真?
  18. Ionic2 WARNING: sanitizing HTML stripped some content when no content stripped
  19. 简易留言簿系统-ASP.NET MVC (后台基本完成)
  20. python官网选择哪个_python官网安装挑选哪个?

热门文章

  1. spring 是如何保证一个事务内获取同一个Connection?
  2. java 简易的闹钟设计,用java设计智能闹钟
  3. 第一章 企业信息化战略有实施
  4. 极智Coding | C 和 C++ 读存 bin 文件方法
  5. 2021年 Java 面试题集锦 -- 持续更新
  6. NC14745 Hungry!
  7. android端富文本编辑器HRichEditor
  8. ubuntu系统无法识别到U盘
  9. linux4.4 grub2文件在,grub2
  10. Stream.reduce()合并流 例BigDecimal 的add求和