一、Java常用日志框架
1)1996年左右,以Ceki Gulcu(切基·古尔库)为首的团队创建了Log4j,后被Apache收归麾下;
2)2002年Sun公司推出了日志库JUL(java util logging),基本照搬Log4j,后来Sun也被Google收购,JUL自然也成为Apache的一员;
3)后来Apache又推出了Commons Logging,组成了日志接口Commons Logging,实现可选Log4j或JUL的架构;
4)2006年Ceki Gulcu离开Apache,先后创建了Slf4j和Logback,组成了日志接口Slf4j,实现以Logback为主的架构;
5)至此,java界两大日志框架阵营形成,主流应用都选这两个组合中的一个,由于Slf4j在各项性能及接入方式上都优于Commons Logging,而且Slf4j完全开源、而Commons Logging部分开源,所以Slf4j大有后来者居上的趋势。
现在,如果接手老项目,就沿用以前的的日志框架;如果是新建项目,建议用Slf4j的组合。
二、Logback应用(Slf4j接口,Logback实现)
1、约定
<!--
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.0.6</version>
</dependency>
-->
<!-- 该依赖包括了上面两个依赖,所以只要引入该依赖即可 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.6</version>
</dependency>
如果是springboot项目,spring-boot-starter这个依赖其中包含了 spring-boot-starter-logging,这种情况下就不用再另外加logback依赖了。
2、配置
1)默认配置
在用户没有对logback进行任何配置的情况下,logback会使用默认配置输出简单的日志到控制台。
但在实际工作中,我们通常对日志的输出有很多要求,比如设定日志的格式、要求日志文件按天分割、将日志文件按大小进行分割、定期删除旧日志等,所以一般都要建专门日志配置文件,用于管理logback的输出格式、级别等;这个文件一般放在resource文件夹下,一般命名为lockback.xml。
项目启动时,如果检测到在项目的classpath路径下存在logback.xml(或者logback-test.xml、logback.groovy),logback框架能够自动扫描到它、并读取配置。
2)根节点configuration
配置文件的根节点是configuration,它有三个属性,scan、scanPeriod、debug,一般都采用默认设置。
<configuration>
...
</configuration>
configuration节点主要又包含appdender、logger、root三个标签,如下图:
3)一级子节点property
property标签,用来定义一些常用的变量,比如日志输出格式、很多地方会用到,而且一般格式都是一样的;用property配置以后,后面就可以用${name值}来引用自定义的配置。
当然,property设置了以后,其他各处还是可以不用。
<configuration>
<!--配置日志文件格式-->
<property name="pattern" value="%d{HH:mm:ss.SSS} [%-5level] [%thread] [%logger] %msg%n"/>
<!--配置日志路径-->
<property name="logPath" value="/backup/logs/jboss/bdp/log"/>
<!--配置日志文件名-->
<property name="appName" value="bdpadmin"/>
<configuration/>
4)一级子节点appender
appender标签,用来配置日志的输出位置、格式等等,标签主要有两个属性:name和class;
根据class不同,appender有不同功效,这里介绍几个常用的appender类型:
a、ch.qos.logback.core.ConsoleAppender
ConsoleAppender类型的appender标签,配置在控制台输出的日志。主要是设置它的二级子标签<encoder>
的两个三级子标签:<pattern>
设置日志输出格式;<charset>
设置字符集,防止乱码
<configuration>
<!-- 输出到控制台 -->
<appender name="print-on-console" class="ch.qos.logback.core.ConsoleAppender" >
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 输出的格式,这里使用property配置的常量 -->
<!-- <pattern>%d{HH:mm:ss.SSS} [%-5level] [%thread] [%logger] %msg%n</pattern> -->
<pattern>${pattern}</pattern>
<!-- 字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
</configuration>
b、ch.qos.logback.core.rolling.RollingFileAppender
RollingFileAppender类型的appender标签,配置输出到文件的日志。
RollingFileAppender还提供了切割日志文件的功能,它首先将日志记录到一个自定义名称的文件中,一旦满足设置条件(一定大小、或者一定时间)之后,它会将新产生的日志记录到新文件中。
<configuration>
<!-- 输出到日志文件 -->
<appender name="print-to-file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 配置日志地址、名称 -->
<file>${logPath}/${appName}.log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>${pattern}</Pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 配置滚动的策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 配置滚动日志名称 -->
<fileNamePattern>${logPath}/${appName}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 每天生成新日志文件,如果文件超过30M,则再生成一个新的日志文件 -->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>3MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
</configuration>
如果还要配置日志文件的保存时间,可以在<rollingPolicy>
标签下加
<!-- 保存的最长时间:天数 -->
<MaxHistory>30</MaxHistory>
5)一级子节点logger、root——配置日志有效级别
a、有效级别
日志是有级别的,从低到高为:TRACE < DEBUG < INFO < WARN < ERROR
logback是否打印某条日志,取决于这条日志的级别:
如果这条日志等于或高于logger、root配置的级别,那这条日志就属于有效级别,logback就会打印这条日志;反之,如果这边日志低于logger、root配置的级别,那这条日志就属于无效级别,logback就不会打印它。
比如root配置有效级别为error,那么
log.info(“…”)就不会被打印,
而log.error(“…”)才会被打印。
那么,都是配置有效级别,root和logger有什么区别呢?
简单来说就是root配置的是大多数日志的有效级别;
而logger配置的是个别部分的日志级别。
需要特殊设置的部分就用logger配置,剩下的都看root配置。
b、logger
logger用来为单独的包配置日志级别。
应用场景:生产环境root一般设置比较高,这样可以降低打印日志的资源消耗。
但是有些功能,又需要很详细的日志、以备将来定位问题;比如mybatis、sql相关的部分。
这时就可以通过logger为这些部分,设置专门的日志级别debug;如此一来,日志文件中就会出现mybatis的debug级别日志, 而其它包则会按root的级别输出日志
<configuration>
<logger name="com.ibatis" level="DEBUG" />
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<logger name="org.springframework" level="ERROR" />
<configuration/>
c、root
生产环境,应该配置合适的级别,以降低消耗
<configuration>
<root level="debug">
<!-- 控制台输出 -->
<appender-ref ref="print-on-console" />
</root>
<root level="info">
<!-- 文件输出 -->
<appender-ref ref="print-to-file"/>
</root>
<configuration/>
6)应用实例
将上述配置总结一下,就是logback.xml的应用实例
<configuration>
<!--配置日志文件格式-->
<property name="pattern" value="%d{HH:mm:ss.SSS} [%-5level] [%thread] [%logger] %msg%n"/>
<!--配置日志路径-->
<property name="logPath" value="/backup/logs/jboss/bdp/log"/>
<!--配置日志文件名-->
<property name="appName" value="bdpadmin"/>
<!-- 输出到控制台 -->
<appender name="print-on-console" class="ch.qos.logback.core.ConsoleAppender" >
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 输出的格式,这里使用property配置的常量 -->
<!-- <pattern>%d{HH:mm:ss.SSS} [%-5level] [%thread] [%logger] %msg%n</pattern> -->
<pattern>${pattern}</pattern>
<!-- 字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 输出到日志文件 -->
<appender name="print-to-file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 配置日志地址、名称 -->
<file>${logPath}/${appName}.log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>${pattern}</Pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 配置滚动的策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 配置滚动日志名称 -->
<fileNamePattern>${logPath}/${appName}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 每天生成新日志文件,如果文件超过30M,则再生成一个新的日志文件 -->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>3MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<logger name="com.ibatis" level="DEBUG" />
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<logger name="org.springframework" level="ERROR" />
<root level="debug">
<!-- 控制台输出 -->
<appender-ref ref="print-on-console" />
</root>
<root level="info">
<!-- 文件输出 -->
<appender-ref ref="print-to-file"/>
</root>
<configuration/>
三、Logback实际应用中遇到的一些问题
1、RollingFileAppender标签,试图对日志文件名动态配置%d{yyyy-MM-dd}不起作用
<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}/${appName}.%d{yyyy-MM-dd}.log</file>
...
</appender>
原因:RollingFileAppender标签下的<file>
标签并不支持日期pattern动态设置,不生效是正常的。应该将其改为
<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}/${appName}.log</file>
...
</appender>
但是,日志文件触发切割条件后,切割日志文件名的动态配置是支持的
最终效果
2、tomcat、日志文件乱码
控制台看到打印日志乱码如下:
而日志文件用记事本打开不乱码:
用sublime打开乱码
关于这个问题,分析如下:
不同的文本编辑器默认的字符集不同,所以才会出现对同一文件,不同编辑器打开有的乱码、有的正常这种现象。
如果控制台logback输出的日志乱码,将ConsoleAppender中的字符集设为utf-8即可解决。
如果日志文件乱码,就将RollingFileAppender中的字符集设为utf-8。
logback.xml字符集都设置好了,再用某个编辑器、比如sublime打开,如果还是乱码,那就看一下sublime的字符集是啥?给改成utf-8,就不乱了。
3、java程序中获取logger日志对象
1)org.slf4j包下有个LoggerFactory类,LoggerFactory有个getLogger()方法,可以方便的获取一个Logger对象(org.slf4j包下的)。
getLogger()方法,入参填当前类的.calss,或者当前类的.class.getName()。
public class Abc {
private static Logger logger = LoggerFactory.getLogger(Abc.class);
}
//或者
public class Abc {
private static Logger logger = LoggerFactory.getLogger(Abc.class.getName());
}
2)接手旧项目、或者中途修改过项目日志框架(由log4j改为logback),注意修改获取日志对象的方法,对象创建工厂不要错用org.apache.log4j包的LogManager,下面这个是log4j的
private static Logger logger = LogManager.getLogger(PushController.class.getName());
4、springboot项目整合logback
1)实际开发中如果是springboot项目,不需要我们添加logback的依赖,这是因为spring-boot-starter这个依赖其中包含了 spring-boot-starter-logging,这个包下有所有日志框架需要的依赖(也包含log4j的依赖);
2)如果项目中使用了lombok插件,写日志时甚至都不用创建日志对象,只要在需要日志的类上加一个@Slf4j,就可以直接在方法中用 log.info();或者log.error();等写日志了,非常简单方便。
@Slf4j注解是lombok的一个注解,它会为类提供一个属性名为log的slf4j日志对象。