SLF4J,即简单日志门面(Simple Logging Facade for Java)。从设计模式的角度考虑,它是用来在log和代码层之间起到门面的作用。配置SLF4J是非常简单的一件事,只要将你打算使用的日志系统对应的jar包加入到项目中,SLF4J就会自动选择使用你加入的日志系统。使用slf4j提供的接口,可隐藏日志的具体实现。这与jdbc相似,使用jdbc也就避免了不同的具体数据库。
SLF4J所提供的核心API是一些接口以及一个LoggerFactory的工厂类,SLF4J提供了统一的记录日志的接口,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过具体日志系统的配置来实现,因为在代码实现中用的是接口,因此可以在应用中灵活切换日志系统。
目前来说,Logback是SLF4J的最佳实现。
SLF4J如何做到自动选择日志框架?以log4j为例:
1、首先,通过slf4j-api.jar的Logger log = LoggerFactory.getLogger(xxx);获取日志对象,作为日志的入口,可以通过log.info()等写日志;
2、LoggerFactory类中调用了org.slf4j.impl.StaticLoggerBinder类的getLoggerFactory()方法获取日志工厂ILoggerFactory(slf4j提供的接口)的实现,然后通过日志工厂的实现类的getLogger()方法返回Logger的实现实例,这里的关键就是org.slf4j.impl.StaticLoggerBinder(该类是接口LoggerFactoryBinder的实现类),虽然slf4j-api.jar中的LoggerFactory类使用了这个类,但这个类并不存在于slf4j-api.jar中,所以,只导入了slf4j-api.jar时,编译是会报错的,因为org.slf4j.impl.StaticLoggerBinder根本就不存在;
3、slf4j-log4j12.jar作为适配器,提供了org.slf4j.impl.StaticLoggerBinder类,所以,当我们导入了slf4j-log4j12.jar后,slf4j-api.jar会直接调用slf4j-log4j12.jar中的org.slf4j.impl.StaticLoggerBinder类。logback日志框架实现了slf4j,所以不需要额外的适配器就能与slf4j结合,logback-classic.jar里有org.slf4j.impl.StaticLoggerBinder类,所以,如果同时导入logback-classic.jar和slf4j-log4j12.jar会报错,因为不能确定使用哪个org.slf4j.impl.StaticLoggerBinder类;
4、具体的写日志操作,由适配器调用log4j.jar来完成
因为Logback自身就实现了slf4j,所以不需要像slf4j-log4j12.jar这种适配器,其它日志框架也一样,如果本身就实现了slf4j,则可直接被slf4j使用,如果还没实现slf4j,则需要额外的适配器包,如slf4j-jdk14.jar用于适配java.util.logging。
当然,我们可以不结合slf4j而单独使用log4j,这样只需要导入log4j.jar即可,这样做的坏处有:1、当想替换成其它日志框架时,整个系统的写日志代码都要修改,因为不同的日志框架的实现不同,包括类名、方法名等;2、语法设计角度,slf4j有{}占位符,而log4j需要用“+”来连接字符串,既不利于阅读,同时消耗了内存。
logback介绍
Logback是由log4j创始人设计的另一个开源日志组件,意在成为log4j的继承者,Logback比现有的所有日志系统更快,占用内存更小,提供独有且很有用的特性(翻译自官网),官方网站:http://logback.qos.ch。分为下面3个模块:
logback-core:其它两个模块的基础模块,对应logback-core-1.1.11.jar;
logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API,使你可以很方便地更换成其它日志系统如log4j(log4j并不实现slf4j,需要通过适配器slf4j-log4j12.jar,所以,在把logback框架换成log4j时,除了要引入log4j.jar还要引入slf4j-log4j12.jar,java代码不用做任何修改)或JDK14 Logging,对应logback-classic-1.1.11.jar;
logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能。(不理解)
logback取代log4j的理由:
1、更快的实现:Logback的内核重写了,在一些关键执行路径上性能提升10倍以上,而且初始化内存加载也更小了;
2、自动重新加载配置文件,当配置文件修改了,Logback-classic能自动重新加载配置文件,扫描过程快且安全,它并不需要另外创建一个扫描线程;
3、配置文件可以处理不同的情况,开发人员经常需要判断不同的Logback配置文件在不同的环境下(开发,测试,生产),而这些配置文件仅仅只有一些很小的不同,为了避免重复,logback支持配置文件中的条件处理,只需使用
4、Filters(过滤器):有时候,需要诊断一个问题,需要打出日志。在log4j,只有降低日志级别,不过这样会打出大量的日志,会影响应用性能。在Logback,你可以继续保持日志级别而除掉某种特殊情况,如alice这个用户登录,她的日志将打在DEBUG级别而其他用户可以继续打在WARN级别,要实现这个功能只需加4行XML配置(怎样配?);
5、占位符(place holder):如logger.debug(
"Processing trade with id: {} and symbol : {} "
,
id
, symbol);
6、自动清除旧的日志归档文件;
7、自动压缩归档日志文件;
8、Logback-access模块,提供了通过HTTP访问日志的能力,logback-access模块可与Jetty或者Tomcat进行集成,提供了非常丰富而强大的通过HTTP访问日志的功能。
LogBack与Log4J性能测试:
LogBack(1.1.11版本)测试结果:
15:22:41.191 [main] INFO com.LogDemo.TestLogBack - 10000条耗时:[482, 387, 496, 382, 496, 424, 496, 420, 533, 427]ms
15:22:41.191 [main] INFO com.LogDemo.TestLogBack - 平均每10000条耗时:454ms
Log4J(1.2.17版本)测试结果:
16:03:42.962 [main] INFO Log4JTest.Log4JTest.TestLog4J - 10000条耗时:[626, 483, 437, 540, 557, 566, 520, 564, 536, 538]ms
16:03:42.962 [main] INFO Log4JTest.Log4JTest.TestLog4J - 平均每10000条耗时:536ms
看不出有明显的性能差别。
Logback源码分析请看“Logback日志框架”中的文章
因为spring-boot-starter包含了spring-boot-starter-logging,故无需要在maven中添加依赖,Spring Boot默认用Logback来记录日志,并用INFO级别输出到控制台,可以直接使用Spring Boot的默认日志配置。
更多配置及使用请看分类“Logback日志框架”中的文章。
默认日志打印的格式如下:
2017-11-22 10:31:04.283 INFO 6504 --- [nio-8888-exec-1] com.LogDemo.controller.UserController : 用户信息查询,参数:name:活动结束, age:39
• Date and Time—Millisecond precision and easily sortable.
• Log Level—ERROR, WARN, INFO, DEBUG or TRACE.
• Process ID.
• A --- separator to distinguish the start of actual log messages.
• Thread name—Enclosed in square brackets (may be truncated for console output).
• Logger name—This is usually the source class name (often abbreviated).
• The log message.
可以配置的属性有:
1)debug=true#以debug模式启动应用
2)通过logging.level.*做级别控制:
logging.level.root=INFO#所有类日志都以INFO级别输出
logging.level.com.mypackage=DEBUG#com.mypackage包下所有class以DEBUG级别输出
3)文件输出:
logging.path=D:\\log#在D:\log文件夹生成一个日志文件为 spring.log
logging.file=my.log#在项目的当前路径下生成一个my.log日志文件
logging.path和logging.file都可以是相对路径或者绝对路径,如若同时使用,则只有logging.file生效。
文件命名规则???
4)日志格式:
???
根据不同的日志系统,你可以按如下规则组织配置文件名,就能被正确加载:
Logback:logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
Log4j:log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml
Log4j2:log4j2-spring.xml, log4j2.xml
JDK (Java Util Logging):logging.properties
Spring Boot官方推荐优先使用带有-spring的文件名作为你的日志配置(如使用logback-spring.xml,而不是logback.xml),命名为logback-spring.xml的日志配置文件,spring boot可以为它添加一些spring boot特有的配置项。上面是默认的命名规则,并且放在src/main/resources下面即可。如果你即想完全掌控日志配置,但又不想用logback.xml作为Logback配置的名字,可以通过logging.config属性指定自定义的名字:
logging.config=classpath:logging-config.xml
虽然一般并不需要改变配置文件的名字,但是如果你想针对不同运行时Profile使用不同的日志配置,这个功能会很有用,有两种实现方案:
1、logging.config属性在application.properties配置文件中,application.properties配有spring.profiles.active=dev(prod或test),可以通过如下方式获取当前环境对应的配置文件:
logging.config=classpath:${spring.profiles.active}/logging-config.xml#不同环境的配置文件放在不同的文件夹下,如dev/logging-config.xml
也可以通过拼接文件名的方式:logging.config=classpath:logging-${spring.profiles.active}-config.xml
2、有多个application.properties文件,如application-dev.properties、application-prod.properties,每个配置文件都有一个logging.config属性,分别指向该环境对应的日志配置文件,名字为application.properties的配置文件中有一个属性spring.profiles.active=dev(prod或test),该方式,spring boot会根据spring.profiles.active的值选择不同的application.properties文件,如application-dev.properties,从而能确定使用哪个日志配置文件。
1、application.properties:
#配置应用启动环境,这里为开发环境(dev)
spring.profiles.active=dev
2、logback-spring.xml
logback
%d{HH:mm:ss.SSS} %contextName [${app_name:-}] [%thread] %-5level %logger{36} - %msg%n
${log.path}/info.log
${log.path}/info-%d{yyyy-MM-dd_HH-mm}.%i.log
10MB
60
20GB
%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n
ERROR
${log.path}/error.log
${log.path}/error-%d{yyyy-MM-dd_HH-mm}.%i.log
10MB
60
20GB
%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n
说明:
1、
属性contextName用于区分不同应用程序的记录,默认是default,可以通过%contextName来打印日志上下文名称。
属性property用来定义变量值。
2、springProperty 用于获取spring的配置,如上面声明的app_name属性,它的值来自spring.application.name这个配置项,这个配置项可以在配置文件中找到,即应用的名字,然后通过${app_name:-}在日志中输出。
3、子节点appender用来格式化日志输出,有两个属性name和class,class用来指定哪种输出策略,常用就是控制台输出策略和文件输出策略。
4、节点root
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性,用来设置打印级别,大小写无关。
5、节点logger
用来设置某一个包或者具体的某一个类的日志打印级别、以及指定
name:用来指定受此loger约束的某一个包或者具体的某一个类
Level:用来设置打印级别,如果未设置此属性,那么当前loger将会继承上级的级别
Addtivity:是否向上级loger传递打印信息,默认是true
6、节点springProfile,根据不同环境,选择不同的logger节点,同理,也可以套在其它节点的外面
从实际使用来讲,只需要两种级别的日志配置:
生产环境:除了debug级别,其他全开,如果生产环境发生了问题,日志应该能够指明原因。
开发环境:编写新代码或是尝试复现问题时,打开全部级别。
测试环境:与生产环境相同。
更多用法请看分类“Logback日志框架”中的文章
用哪种日志级别记录信息:
debug:任何觉得有利于在调试时更详细的了解系统运行状态的东东,比如变量的值等等,都输出来看看也无妨。
info:这个应该用来反馈系统的当前状态给最终用户的,所以,在这里输出的信息,应该对最终用户具有实际意义,也就是最终用户要能够看得明白是什么意思才行,从某种角度上说,Info 输出的信息可以看作是软件产品的一部分(就像那些交互界面上的文字一样)。
error:捕获到异常或确定执行存在问题时。
使用:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Logger log = LoggerFactory.getLogger(this.getClass());
或
Logger log = LoggerFactory.getLogger(Xxx.class);
记录普通日志信息:
log.info("用户信息查询,参数:name:{}, age:{}", name,age);
记录异常日志信息:
try {
Integer x = null;
++x;
} catch (Exception e) {
log.error(e); // A
log.error(e, e); // B
log.error("xxxx" + e); // C
log.error(e.toString()); // D
log.error(e.getMessage()); // E
log.error(null, e); // F
log.error("xxxx", e); // G
log.error("xxxx{}", e); // H
log.error("xxxx{}", e.getMessage()); // I
log.error("Error reading configuration file: " + e.getMessage()); // K
}
上面的代码,正确输出异常信息的只有G, A和B甚至不能在SLF4J中编译通过,其他的都会丢失异常堆栈信息或者打印了不恰当的信息。这里只要记住一条,在日志中输出异常信息,第一个参数一定是一个字符串,一般都是对问题的描述信息,而不能是异常message(因为堆栈里面会有),第二个参数才是具体的异常实例。
如果结合zipkin,希望获取zipkin的追踪id可以通过以下方式获取traceid和spanid
%X{X-B3-TraceId:-} %X{X-B3-SpanId:-}
重要参考资料:
http://www.cnblogs.com/zheting/category/966890.html
官方文档:
https://logback.qos.ch/manual/index.html
参考资料:
https://www.cnblogs.com/warking/p/5710303.html
https://www.cnblogs.com/rollenholt/p/3525822.html
https://www.cnblogs.com/Sinte-Beuve/p/5758971.html
https://www.cnblogs.com/huayu0815/p/5341712.html
http://blog.csdn.net/mutouyihao/article/details/45723233
http://blog.csdn.net/zhuyouzhi123/article/details/48714079
https://www.cnblogs.com/davidwang456/p/4448011.html
http://blog.csdn.net/zhuyucheng123/article/details/21524549
http://blog.csdn.net/GeekSnow/article/details/53405062