日志现状
线上问题追踪,基于日志的统计分析等都离不开日志。Java领域常见的日志框架有:
1、commons-logging是一套日志接口
log4j apache log4j是一个基于java的日志工具,log4j2是其升级产品,但二者并不兼容。
2、slf4j是另一套日志接口
logback 是slf4j的一个实现
所以现在java领域的日志分为两大阵营:commons logging和slf4j,前者有apache背景,虽然一度让sfl4j市场领先,但新的log4j2也具有了slf4j的所有特性。
3、常用的组合方式为:slf4j与logback组合使用,commons logging与log4j组合使用。
两者相比更建议使用前者组合,原因:性能更高;开销更低;logback文档免费;
4、有些依赖如Spring本身的日志实现使用了Commons Logging,而我们指定的方案是slf4j+logback,这时需要在项目中将Commons Logging排除掉,一般采用maven的exclusion排除。
5、桥接包和实现包对应关系如下图,需要注意的是,连接层的jar包和实现的jar的版本要一致。
jar包名 | 说明 |
slf4j-log4j12-1.7.13.jar | Log4j1.2版本的桥接器,需要将Log4j.jar加入Classpath。 |
slf4j-jdk14-1.7.13.jar | java.util.logging的桥接器,Jdk原生日志框架。 |
slf4j-jcl-1.7.13.jar | Jakarta Commons Logging 的桥接器. 这个桥接器将Slf4j所有日志委派给Jcl。 |
logback-classic-1.0.13.jar(logback-core-1.0.13.jar) | Slf4j的原生实现,Logback直接实现了Slf4j的接口,因此使用Slf4j与Logback的结合使用也意味更小的内存与计算开销 |
多个日志jar包形成死循环的条件 | 产生原因 |
log4j-over-slf4j.jar和slf4j-log4j12.jar同时存在 | 由于slf4j-log4j12.jar的存在会将所有日志调用委托给log4j。但由于同时由于log4j-over-slf4j.jar的存在,会将所有对log4j api的调用委托给相应等值的slf4j,所以log4j-over-slf4j.jar和slf4j-log4j12.jar同时存在会形成死循环 |
jul-to-slf4j.jar和slf4j-jdk14.jar同时存在 | 由于slf4j-jdk14.jar的存在会将所有日志调用委托给jdk的log。但由于同时jul-to-slf4j.jar的存在,会将所有对jul api的调用委托给相应等值的slf4j,所以jul-to-slf4j.jar和slf4j-jdk14.jar同时存在会形成死循环 |
在使用commons logging时为了减少构建日志信息的开销,通常的做法是
if(log.isDebugEnabled()){
log.debug("User name: " +
user.getName() + " buy goods id :" + good.getId());
}
而使用slf4j,你只需这么做:
log.debug("User name:{} ,buy goods id :{}", user.getName(),good.getId());
6、一个常用的依赖如下:
jcl-over-slf4j包是为了将一些早期使用commons-logging的桥接到slf4j
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.9.RELEASE</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.8</version>
</dependency>
</dependencies>
6、logback取代log4j的理由:
1) 非常充分的文档、非常充分的测试、更快的实现等
2) 自动重新加载配置文件
3) 自动压缩已经打出来的log,使用RollingFileAppender再产生新文件时会自动压缩,异步处理。
日志使用方法
1、logback.xml配置文件
logback会依次尝试在 classpath 下查找文件 logback-test.xml,logback.xml,如果两个文件都不存在,logback会自动对自己进行配置,导致记录输出到控制台。
2、web.xml加入以下配置
webAppRootKey默认是webapp.root,如果同一个tomcat里启动多个应用,webAppRootKey会指向之前已经启动的项目,会出现启动不了的情况。
解决办法是为每个项目都加上该配置,最好用"工程名.root"来标识
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>admin-web.root</param-value>
</context-param>
<!--配置文件的存放地址,classpath后面不要带*-->
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:log4j.properties</param-value>
</context-param>
<!--开一条监控线程每60秒动态扫描一下配置文件位置的变化。-->
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>60000</param-value>
</context-param>
logback.xml语法
logback.xml语法非灵活,基本结构如下:
以<configuration>开头,后面有零个或多个<appender>元素,有零个或多个<logger>元素,有最多一个<root>元素。
1) 根节点<configuration>,包含下面属性:
scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
2) 子节点<property> :用来定义变量值,它有两个属性name和value
<property resource="application.properties" />
加载外部属性文件
<property name="LOG_HOME" value="/home/admin/logs" />
可以通过${LOG_HOME}在logger中引用
3) 子节点<appender>:负责写日志的组件,它有两个必要属性name和class。name指定appender名称,class指定appender的全限定名
Appender主要用于存放日志对象,也可以定义日志类型、级别。有ConsoleAppender和RollingFileAppender两种
日志对象包括日志输出的目的地,目的地可以是控制台、文件、远程套接字服务器、 MySQL等数据库。
Logger级别:TRACE、DEBUG、INFO、WARN、ERROR。root logger 默认级别是 DEBUG。
级别排序为: TRACE < DEBUG < INFO < WARN < ERROR
3.1 ConsoleAppender 把日志输出到控制台,有以下子节点:
<encoder>:对日志进行格式化。
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder charset="UTF-8">
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
3.2 RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。有以下子节点:
1、<file>:被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。
2、<rollingPolicy>:当发生滚动时,决定RollingFileAppender的行为,涉及文件移动和重命名。
ch.qos.logback.core.rolling.TimeBasedRollingPolicy最常用的滚动策略,设置最大数量;
<fileNamePattern>:必要节点
<maxHistory>:可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每个月滚动,且<maxHistory>是6,则只保存最近6个月的文件,删除之前的旧文件。
//每天生成一个日志文件保存15天的
<appender name="FILE-DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.root.folder}/cls-web-debug.log.%d{yyyy-MM-dd}</fileNamePattern>
<maxHistory>15</maxHistory>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
3、<triggeringPolicy>: 条件满足时,告知 RollingFileAppender 触发滚动。日志文件大于50M时,触发滚动策略
<maxFileSize>50MB</maxFileSize>
常搭配FixedWindowRollingPolicy使用,用于生产环境。
//当文件大于50M时,生成新的日志文件,窗口大小是1~50,当保存了50个文档后,将覆盖最早的日志文件
<appender name="RollingBaseSize" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/home/data/logs/cls/cls-admin/cls-admin-web.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>/home/data/logs/cls/cls-admin/cls-admin-web.%i.log</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>50</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>50MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%date [%thread] %-5level %class{0}.%method - %X{rule} - %msg%n</pattern>
</encoder>
</appender>
4) 子节点<logger>:用来设置某一个包或具体的某一个类的日志打印级别、以及指定<appender>。
<logger>仅有一个name属性,一个可选的level。可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个logger
5) 子节点<root>:它也是<logger>元素,但是它是根logger,是所有<logger>的上级。只有一个level属性,因为name已经被命名为"root",且已经是最上级了。
pattern语法
%d{yyyy-MM-dd HH:mm:ss.SSS} 显示日志记录时间
[%thread] 线程名
%-5level 日志级别
%logger{36}表示logger名字最长36个字符,否则按照句点分割。
%msg 显示输出消息
%n 当前平台下的换行符
%-5p 显示该条日志的优先级
%i 指定生成的索引