LOG这玩意,对于程序员来说太重要了,刚开始接触软件开发,看到log里密密麻麻的字就头疼,如今却视LOG如珍宝,因为程序的bug都写在里面了。
SLF4J是什么?酸辣粉4斤
S(Simple) L(Logging) F(Facade) 4(for) J(Java)
为java准备的简单日志门面。
SLF4J只是一个接口,要想打印日志,还要给它提供接口的实现,如common logging,log4j,logback等等,这些具体的实现又被称为binding,我们需要把这些实现放入项目的classpath,这样slf4j才能找到这些binding,在compile期间,slf4j会建立与binding的硬连接。这样一来,会有两种异常情况:
- classpath下如果有多于1个binding,需要从classpath中把多余的binding干掉。通常需要用到依赖冲突解决技巧。如果是用maven管理依赖的话,可以使用IDEA插件Dependency analyzer帮助快速找到冲突的依赖项。
- classpath下没有binding,slf4j则不作为,丢弃所有的日志请求
Logback
Logback是完全遵循slf4j标准编写的binding,和slf4j配合使用,效果最佳。像Log4J这样的框架,其实在slf4j诞生之前就已经存在了,slf4j虽然也兼容这些框架,但是需要在slf4j和log4j之间添加一个adapter,如此以一来,性能就会大打折扣。
上图是一些可能的使用SLF4J的方式:左起第一种,是没有提供binding的形态,第二,五,六这三种,使用的是亲儿子。第三,四两种,是兼容模式,需要使用adapter。
这里我们介绍一下logback的使用。
Logback是log4j的继任者,两者的开发者是同一帮人,所以设计的理念是一致的。青出于蓝而胜于蓝,Logback的性能是log4j的10倍,内存消耗也比log4j小得多。
Logback由三部分组成
- logback-core是核心组件
- logback-classic是log4j的改进版本,是logback的经典版本,原生实现了slf4j
- logback-access与serverlet容器(Tomcat/Jetty)集成,提供HTTP访问日志功能。
(另外,slf4j-simple也是原生支持slf4j的日志框架,性能损耗也是0。)
在maven POM中添加对logback的依赖
POM(Project Object Model)
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
Logback分析
Logback有三个主要class组成:Logger,Appender和Layout
- Logger
Logger实例化:
Logger LOG = LoggerFactory.getLogger(Hello.class);
实例化的时候,需要传入一个name,name相当于一个Logger的唯一标识(ID),Logger是有继承关系的:
有一个叫LoggerContext的管理员,它维护了一个树结构来管理这些Logger。在树的根部是一个叫root的Logger。
日志是有级别的,按照优先级为:TRACE<DEBUG<INFO<WARN<ERROR
每一个Logger都被赋予一个等级,如果没有给Logger指定等级:
- Appender
定义日志输出的地方,appender包括:console,file,remote socket server,Mysql,PostgreSQL,Oracle,JMS, remote UNIX Syslog daemons.
一个log可以有多个appender。
Appender有继承叠加性。举个例子:有两个Logger,一个叫做dad,一个叫做dad.son, dad有两个appender:console和file,但是dad.son只有一个appender:console,在这种情况下,dad.son的日志也会被打印到file,因为它继承了dad的appender。Logger有开关属性叫additivity,如果置为false,继承叠加性就失效了。Additivity默认为true。
- Layout
定义日志的格式。是appender的属性
个性化你的logback
回忆一下你工作中见到的log,它通常是一个放在生产环境某个目录中的一个以.log为后缀的文件。里面的内容是以行为单位的,每一行就是一条日志记录,每一行通常包括日期,时间,类名,线程,日志级别,消息实体。随着程序的运行,这个日志文本的尾部会逐行追加。日志体积过大的时候,会被按照时间打包成压缩包。另外有时候日志并没有保存到文件里,而是通过控制台显示的。
所以,所谓个性化你的logback,就是配置这些东西。
Logback配置可以通过java代码,也可以通过xml和groovy配置文件。另外,如果之前你有log4j的配置文件——log4j.properties,也可以通过这个工具一键转化为logback.xml: https://logback.qos.ch/translator/
Logback找配置文件的顺序为:
这里其实比较推荐直接通过第四种方式,指定具体的配置文件,因为有些时候,配置文件不一定放在classpath下。
String filePath = System.getProperty("user.dir");
File logbackConf = new File(filePath + LOGBACK_CONF_PATH);
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(loggerContext);
try {
configurator.doConfigure(logbackConf);
} catch (JoranException e) {
e.printStackTrace();
}
下面分享一个配置文件实例,详细的配置说明,可以参考官方文档:http://www.logback.cn/03%E7%AC%AC%E4%B8%89%E7%AB%A0logback%E7%9A%84%E9%85%8D%E7%BD%AE.html
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="120 seconds" debug="false">
<property name="LOG_HOME" value="/var/log/a/b"/>
<appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="demo_appender">
<File>${LOG_HOME}/b.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/token.%d{yyyy-MM-dd}-%i.log.gz</FileNamePattern>
<TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<MaxFileSize>50MB</MaxFileSize>
</TimeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level-%msg%n</pattern>
</encoder>
</appender>
<logger name="com.neuron.demo" additivity="false" level="INFO">
<appender-ref ref="demo_appender"/>
</logger>
<root level="INFO">
<appender-ref ref="demo_appender"/>
</root>
</configuration>
简单说明一下,我们主要关注三个字段:appender,logger和root。
- appender这里选择的是RollingFileAppender,这个appender会将日志打印到文件里,所以我们在它里面的第一行了打印文件的路径。
RollingPolicy指定了日志轮转策略。要想理解轮转,就要知道日志分为活动日志和归档日志(通常为被打包成压缩包的日志),所谓轮转,就是将活动日志变成归档日志,然后再新建一个活动日志。这里选用的是TimeBasedRollingPolicy。这个policy是最常用的,他是基于时间来定义轮转策略的。我们使用<fileNamePattern>定义了轮转时,为归档日志命名的方式,以及归档路径。
TriggeringPolicy也是FileAppender必须要有的属性,它定义了轮转触发策略。这里我们选择了SizeAndTimeBasedFNATP,然后定义<MaxFileSize>,即日志体积到达50MB时,触发轮转。
- logger是自定义logger
- root是root logger,它只能有一个属性,那就是level,然后通过appender-ref来指定appernder。