2019-12-15-那些年我们用过的Java日志框架

概述

说几个博主我的几个小故事吧,以此来说明日志的重要性

故事1:

那年花开月下,正合佳人约会,怎知技术负责人打电话过来,说后台出bug了,让我排查一下。我慌张的跑回家赶紧排查……当然最后的结局是会也没约成…bug也找了半天,不知所措。长叹一口气,真是赔了夫人又折兵,怪我自己太辣鸡。要是我会日志框架该多好。

故事2:

那天风和日丽,在公司划水正划着开心,结果一哥们说我的接口出了问题。我说不可能,肯定是他的问题,于是我们就争论了起来……这时候我长叹一声,要是我会日志框架该多好。

日志框架的作用

  1. 可以帮助我们快速定位问题,纵使正在约会,给我五分钟就能给你找到问题~,岂能浪费这美好时间?
  2. 可以留下证据,口说无凭,用日志来说话
  3. 学会了这个技术可以装逼,这两个字必须加粗

上面的故事博主真实遇到过,只不过佳人是编出来的。哈哈哈???

接下来我将给大家介绍几大常用框架及其关系

几大框架的关系以及区别

概念1

我们先要熟知两个概念

  1. 日志门面
  2. 日志实现

什么是日志门面呢?简单说它只是定义好了一些接口,由不同的子类实现,类似于JDBC驱动,

日志实现我就不多说了,自然是日志门面的实现。

概念2

再来熟知两个概念

  1. 日志门面不能单独使用
  2. 日志实现可以单独使用

我们都知道光有接口是运行不了的…

分类

日志门面有:SLF4j(simple log facade for java),JUC(commons-logging)jboss-logging

日志实现有:logback,log4j,log4j2,JUC(java.util.logging),等等

几大日志框架的使用

不常用的咱不学,了解一下有这个东西就行。时间是宝贵的,下面只介绍主流。

日志门面的使用格式

日志门面接口的提供,只介绍SLF4j

导入依赖

1
2
3
4
5
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
</dependency>

通用代码,所有使用SLF4j作为接口的日志框架,都是这样获取日志记录器

1
2
3
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Logger logger = LoggerFactory.getLogger("com.hunybei.test.Test01");

下面有一张图可以说明SLF4j和其它框架整合的逻辑

Log4j

这是一个老牌的日志框架,不过性能方面很不好,用不好会出现内存泄漏等问题,这里不推荐使用。

单独使用

单独使用需要导入一个Jar包,比较简单

1
2
3
4
5
6
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<!--版本号随便-->
<version>1.2.17</version>
</dependency>
1
2
3
4
5
6
7
public static void main(String[] args) { 
Logger logger = Logger.getLogger(Log4jDemo.class);
logger.trace("trace...");
logger.info("info...");
logger.warn("warn...");
logger.error("error...");
}

看一下console输出

1
2
3
17:38:17,911  INFO com.hunybei.demo.Log4jDemo:13 - info...
17:38:17,913 WARN com.hunybei.demo.Log4jDemo:14 - warn...
17:38:17,913 ERROR com.hunybei.demo.Log4jDemo:15 - error...

整合SLF4j

需要额外加入两个jar包,一个是接口定义,一个是适配log4j

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
1
2
3
4
5
6
7
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(Log4jDemo.class);
logger.trace("trace...");
logger.info("info...");
logger.warn("warn...");
logger.error("error...");
}

console输出,可以发现,跟单独的log4j输出格式完全一致

1
2
3
17:40:34,072  INFO com.hunybei.demo.Log4jDemo:14 - info...
17:40:34,074 WARN com.hunybei.demo.Log4jDemo:15 - warn...
17:40:34,074 ERROR com.hunybei.demo.Log4jDemo:16 - error...

整合slf4j

加入slf4j的依赖即可

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
</dependencies>
1
2
3
4
5
6
7
public static void main(String[] args) {
Log log = LogFactory.getLog(CommonsLoggingDemo.class);
log.trace("trace...");
log.info("info...");
log.warn("warn...");
log.error("error...");
}

console输出,可以发现,跟单独的log4j输出格式完全一致

1
2
3
17:51:00,986  INFO com.hunybei.demo.CommonsLoggingDemo:11 - info...
17:51:00,987 WARN com.hunybei.demo.CommonsLoggingDemo:12 - warn...
17:51:00,987 ERROR com.hunybei.demo.CommonsLoggingDemo:13 - error...

log4j配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
### 配置根 ###
log4j.rootLogger = debug,console

### 设置输出sql的级别,其中logger后面的内容全部为jar包中所包含的包名 ###
log4j.logger.org.apache=dubug
log4j.logger.java.sql.Connection=dubug
log4j.logger.java.sql.Statement=dubug
log4j.logger.java.sql.PreparedStatement=dubug
log4j.logger.java.sql.ResultSet=dubug

### 配置输出到控制台 ###
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

### 配置输出到文件 ###
log4j.appender.fileAppender = org.apache.log4j.FileAppender
log4j.appender.fileAppender.File = logs/log.log
log4j.appender.fileAppender.Append = true
log4j.appender.fileAppender.Threshold = DEBUG
log4j.appender.fileAppender.layout = org.apache.log4j.PatternLayout
log4j.appender.fileAppender.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

### 配置输出到文件,并且每天都创建一个文件 ###
log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile.File = logs/log.log
log4j.appender.dailyRollingFile.Append = true
log4j.appender.dailyRollingFile.Threshold = DEBUG
log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

### 配置输出到文件,且大小到达指定尺寸的时候产生一个新的文件 ###
log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender
log4j.appender.ROLLING_FILE.Threshold=ERROR
log4j.appender.ROLLING_FILE.File=rolling.log
log4j.appender.ROLLING_FILE.Append=true
log4j.appender.ROLLING_FILE.MaxFileSize=10KB
log4j.appender.ROLLING_FILE.MaxBackupIndex=1
log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n

### 配置输出到邮件 ###
#log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
#log4j.appender.MAIL.Threshold=FATAL
#log4j.appender.MAIL.BufferSize=10
#log4j.appender.MAIL.From=来自哪个邮箱
#log4j.appender.MAIL.SMTPHost=mail.hollycrm.com
#log4j.appender.MAIL.Subject=Log4J Message
#log4j.appender.MAIL.To=发到哪个邮箱
#log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
#log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n

### 配置输出到数据库 ###
#log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender
#log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test
#log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver
#log4j.appender.DATABASE.user=root
#log4j.appender.DATABASE.password=
#log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n')
#log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout
#log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
#log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
#log4j.appender.A1.File=SampleMessages.log4j
#log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j'
#log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout

ConversionPattern 日志信息,符号所代表的含义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-X号: X信息输出时左对齐;
%p: 输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL,
%d: 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:20021018221028921
%r: 输出自应用启动到输出该log信息耗费的毫秒数
%c: 输出日志信息所属的类目,通常就是所在类的全名
%t: 输出产生该日志事件的线程名
%l: 输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main (TestLog4.java:10)
%x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
%%: 输出一个"%"字符
%F: 输出日志消息产生时所在的文件名称
%L: 输出代码中的行号
%m: 输出代码中指定的消息,产生的日志具体信息
%n: 输出一个回车换行符,Windows平台为"\r\n",Unix平台为"\n"输出日志信息换行
可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。如:
1)%20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,默认的情况下右对齐。
2)%-20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,"-"号指定左对齐。
3)%.30c:指定输出category的名称,最大的宽度是30,如果category的名称大于30的话,就会将左边多出的字符截掉,但小于30的话也不会有空格。
4)%20.30c:如果category的名称小于20就补空格,并且右对齐,如果其名称长于30字符,就从左边较远输出的字符截掉。

log4j的日志隔离级别

我们现在要调用logger的方法,不过在这个Logger对象中,有很多方法,所以要先了解log4j的日志级别,log4j规定了默认的几个级别:trace<debug<info<warn<error<fatal等。这里要说明一下:    

  1. 级别之间是包含的关系,意思是如果你设置日志级别是trace,则大于等于这个级别的日志都会输出。

  2. 基本上默认的级别没多大区别,就是一个默认的设定。你可以通过它的API自己定义级别。你也可以随意调用这些方法,不过你要在配置文件里面好好处理了,否则就起不到日志的作用了,而且也不易读,相当于一个规范,你要完全定义一套也可以,不用没多大必要。从我们实验的结果可以看出,log4j默认的优先级为ERROR或者WARN(实际上是ERROR)    

  3. 这不同的级别的含义大家都很容易理解,这里就简单介绍一下:

    • trace
      是追踪,就是程序推进以下,你就可以写个trace输出,所以trace应该会特别多,不过没关系,我们可以设置最低日志级别不让他输出。

    • debug: 调试么,我一般就只用这个作为最低级别,trace压根不用。是在没办法就用eclipse或者idea的debug功能就好了么。

    • info: 输出一下你感兴趣的或者重要的信息,这个用的最多了。

    • warn: 有些信息不是错误信息,但是也要给程序员的一些提示,类似于eclipse中代码的验证不是有error 和warn(不算错误但是也请注意,比如以下depressed的方法)。

    • error: 错误信息。用的也比较多。

    • fatal: 级别比较高了。重大错误,这种级别你可以直接停止程序了,是不应该出现的错误么!不用那么紧张,其实就是一个程度的问题。`~

之前我们看到logger.trace方法没有输出,自然就是因为我们的隔离级大于trace啦~,是不是超级简单呢

Logback

整合SLF4j

logbak不能单独使用,需要整合slf4j,基本上是无缝整合

添加依赖,在maven依赖中可以看到,logback-classic分别导入了SLF4j-apilogback-core

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.11</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.11</version>
</dependency>
1
2
3
4
5
6
7
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(Log4jDemo.class);
logger.trace("trace...");
logger.info("info...");
logger.warn("warn...");
logger.error("error...");
}

console输出

1
2
3
18:20:04.828 [main] INFO com.hunybei.demo.Log4jDemo - info...
18:20:04.830 [main] WARN com.hunybei.demo.Log4jDemo - warn...
18:20:04.830 [main] ERROR com.hunybei.demo.Log4jDemo - error...

logback配置文件

这个文件可用于你的项目中,不过具体的参数需要你自己去设置以达到你想要的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="3600S">

<!--定义一系列属性-->
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 这里看你自己怎么配置-->
<property name="LOG_HOME" value="d://logs/"/>

<!--
%magenta:洋红,%boldMagenta:粗红,%cyan:青色 %white:白色,%magenta:洋红
%d:表示日期,%thread:表示线程名,%-5level:级别从左显示5个字符宽度
%msg:日志消息,%n是换行符 ,%line是显示行号
-->
<!-- 文件格式化输出-->
<property name="outputPattern"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger:%line - %msg%n"></property>
<!-- 控制台格式化输出,注意:这里使用了彩色的日志输出,如果应用在文件格式中就会乱码,只能应用在在控制台-->
<property name="consolePattern"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} | %highlight(%-5level) |%blue(%thread) |%green(%logger:%line) |%cyan(%msg%n)"></property>

<property name="CONSOLE_LOG_PATTERN"
value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
<!-- 控制台输出,如果是生产环境请去掉,不然疯狂输出烦死你 -->
<appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日志输出格式 -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${consolePattern}</pattern>
</layout>
</appender>

<!-- 按照每天生成日志文件 生成INFO日志 -->
<appender name="RollingFileINFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<append>false</append>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/INFO.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--最大保存天数-->
<MaxHistory>30</MaxHistory>
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- level:过滤级别 -->
<!-- onMatch:用于配置符合过滤条件的操作 -->
<!-- onMismatch:用于配置不符合过滤条件的操作 -->
<!--
onMatch和onMisMatch:
将过滤器的日志级别配置为Error,所有ERROR级别的日志交给appender处理,非INFO级别的日志,被过滤掉。
值:DENY,日志将立即被抛弃不再经过其他过滤器;
值:NEUTRAL,有序列表里的下个过滤器过接着处理日志;
值:ACCEPT,日志会被立即处理,不再经过剩余过滤器;
-->
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${outputPattern}</pattern>
</layout>
</appender>

<!-- 按照每天生成日志文件 生成WARN日志 -->
<appender name="RollingFileWARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/WARN.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
<!-- 设置当前日志的文件的大小,决定日志翻滚 -->
<!-- 除按日志记录之外,还配置了日志文件不能超过10M(默认),若超过10M,日志文件会以索引0开始, -->
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${outputPattern}</pattern>
</layout>

</appender>


<appender name="RollingFileError" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/ERROR.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${outputPattern}</pattern>
</layout>
<triggeringPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
  
<fileNamePattern>tests.%i.log.zip</fileNamePattern>
          
<minIndex>1</minIndex>
          
<MaxFileSize>100kb</MaxFileSize>
<maxIndex>3</maxIndex>
</triggeringPolicy>
</appender>


<!-- 日志异步到数据库 需要额外建表-->
<!-- <appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<!--日志异步到数据库-->
<connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
<!--连接池-->
<dataSource class="com.zaxxer.hikari.HikariDataSource">
<driverClass>com.mysql.cj.jdbc.Driver</driverClass>
<url>jdbc:mysql://127.0.0.1:3306/test?useUnicode=true<![CDATA[&]]>characterEncoding=UTF-8<![CDATA[&]]>
autoReconnect=true<![CDATA[&]]>serverTimezone=UTC
</url>
<user>root</user>
<password>root</password>
</dataSource>
</connectionSource>
</appender>-->
<!-- 日志输出级别 -->
<root level="info">
<appender-ref ref="DB"/>
<appender-ref ref="Stdout"/>
<appender-ref ref="RollingFileError"/>
<appender-ref ref="RollingFileWARN"/>
<appender-ref ref="RollingFileINFO"/>
</root>

</configuration>

logback过滤类

1,LevelFilter: 级别过滤器,根据日志级别进行过滤。如果日志级别等于配置级别,过滤器会根据onMathonMismatch接收或拒绝日志。
有以下子节点:

:设置过滤级别

:用于配置符合过滤条件的操作

:用于配置不符合过滤条件的操作

例如:将过滤器的日志级别配置为INFO,所有INFO级别的日志交给appender处理,非INFO级别的日志,被过滤掉。

2,ThresholdFilter: 临界值过滤器,过滤掉低于指定临界值的日志。当日志级别等于或高于临界值时,过滤器返回NEUTRAL;当日志级别低于临界值时,日志会被拒绝。

例子:

1
2
3
4
 <!-- 过滤掉 TRACE 和 DEBUG 级别的日志-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>info</level>
</filter>

3,EvaluatorFilter: 求值过滤器,评估、鉴别日志是否符合指定条件。有一下子节点:

:鉴别器,常用的鉴别器是JaninoEventEvaluato,也是默认的鉴别器,它以任意的java布尔值表达式作为求值条件,求值条件在配置文件解释过成功被动态编译,布尔值表达式返回true就表示符合过滤条件。

evaluator有个子标签,用于配置求值条件

,用于配置符合过滤条件的操作

,用于配置不符合过滤条件的操作

使用这个filter需要单独加入一个依赖,不然会报类找不到异常

1
2
3
4
5
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>3.0.7</version>
</dependency>

例子:

1
2
3
4
5
6
7
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator> <!-- 默认为 ch.qos.logback.classic.boolex.JaninoEventEvaluator -->
<expression>return message.contains("hunybei");</expression>
</evaluator>
<OnMatch>ACCEPT</OnMatch>
<OnMismatch>DENY</OnMismatch>
</filter>

logback的配置介绍

  • Logger、appender及layout

  Logger作为日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。
  Appender主要用于指定日志输出的目的地,目的地可以是控制台、文件、远程套接字服务器、 MySQLPostreSQL、 Oracle和其他数据库、 JMS和远程UNIX Syslog守护进程等。
  Layout 负责把事件转换成字符串,格式化的日志信息的输出。

  • logger context

  各个logger 都被关联到一个 LoggerContextLoggerContext负责制造logger,也负责以树结构排列各logger。其他所有logger也通过org.slf4j.LoggerFactory 类的静态方法getLogger取得。 getLogger方法以 logger名称为参数。用同一名字调用LoggerFactory.getLogger 方法所得到的永远都是同一个logger对象的引用。

  • 有效级别及级别的继承

  Logger 可以被分配级别。级别包括:TRACE、DEBUG、INFO、WARN 和 ERROR,定义于ch.qos.logback.classic.Level类。如果 logger没有被分配级别,那么它将从有被分配级别的最近的祖先那里继承级别。root logger 默认级别是 DEBUG。

  • 打印方法与基本的选择规则

  打印方法决定记录请求的级别。例如,如果 L 是一个 logger 实例,那么,语句 logger.info("..")是一条级别为 INFO的记录语句。记录请求的级别在高于或等于其 logger 的有效级别时被称为被启用,否则,称为被禁用。记录请求级别为 p,其 logger的有效级别为 q,只有则当 p>=q时,该请求才会被执行。
该规则是logback 的核心。级别排序为: TRACE < DEBUG < INFO < WARN < ERROR

logback.xml配置详解

如果配置文件 logback-test.xmllogback.xml都不存在,那么logback 默认地会调用BasicConfigurator,创建一个最小化配置。最小化配置由一个关联到根 logger 的ConsoleAppender组成。输出用模式为%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%nPatternLayoutEncoder进行格式化。root logger 默认级别是 DEBUG。

  • Logback的配置文件

  Logback 配置文件的语法非常灵活。正因为灵活,所以无法用DTD 或 XML schema 进行定义。尽管如此,可以这样描述配置文件的基本结构:以开头,后面有零个或多个元素,有零个或多个元素,有最多一个元素。

  • Logback默认配置的步骤
  1. 尝试在 classpath下查找文件logback-test.xml
  2. 如果文件不存在,则查找文件logback.xml
  3. 如果两个文件都不存在,logbackBasicConfigurator自动对自己进行配置,这会导致记录输出到控制台。

logback.xml由appender,Logger,root三部分组成

  1. 根节点,包含下面三个属性:

    scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
    scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
    debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
例如:

1
2
3
<configuration scan="true" scanPeriod="60 seconds" debug="false"> 
   <!--其他配置省略-->
</configuration>
  1. 子节点:用来设置上下文名称,每个logger都关联到logger上下文,默认上下文名称为default。但可以使用设置成其他名字,用于区分不同应用程序的记录。一旦设置,不能修改。

例如:

1
2
3
4
<configuration scan="true" scanPeriod="60 seconds" debug="false"> 
<contextName>myAppName</contextName>
   <!--其他配置省略-->
</configuration>
  1. 子节点 :用来定义变量值,它有两个属性name和value,通过定义的值会被插入到logger上下文中,可以使“${}”来使用变量。

    name: 变量的名称
    value: 的值时变量定义的值
例如:

1
2
3
4
5
<configuration scan="true" scanPeriod="60 seconds" debug="false"> 
   <property name="myName" value="hunybie" />
   <contextName>${myName}</contextName>
   <!--其他配置省略-->
</configuration>
  1. 子节点:获取时间戳字符串,他有两个属性keydatePattern

    key: 标识此 的名字;
    datePattern: 设置将当前时间(解析配置文件的时间)转换为字符串的模式,遵循java.txt.SimpleDateFormat的格式。
例如:

1
2
3
4
5
<configuration scan="true" scanPeriod="60 seconds" debug="false"> 
  <timestamp key="date" datePattern="yyyyMMdd-HHmmss"/>
  <contextName>${date}</contextName>
  <!-- 其他配置省略-->
</configuration>
  1. 子节点:负责写日志的组件,它有两个必要属性name和class。name指定appender名称,class指定appender的全限定名

    • ConsoleAppender把日志输出到控制台,有以下子节点:
            :对日志进行格式化。
            :字符串System.out(默认)或者System.err(区别不多说了)

    • FileAppender:把日志添加到文件,有以下子节点:

      • :被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。
      • :如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。
      • :对记录事件进行格式化。(具体参数稍后讲解 )
      • :如果是 true,日志会被安全的写入文件,即使其他的FileAppender也在向此文件做写入操作,效率低,默认是 false.`
    • RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。有以下子节点:

      • :被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。

      • :如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。
        rollingPolicy

      • :当发生滚动时,决定RollingFileAppender的行为,涉及文件移动和重命名。属性class定义具体的滚动策略类
        class=”ch.qos.logback.core.rolling.TimeBasedRollingPolicy“: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。有以下子节点:

        • :必要节点,包含文件名及“%d”转换符,“%d”可以包含一个java.text.SimpleDateFormat指定的时间格式,如:%d{yyyy-MM}
          如果直接使用 %d,默认格式是yyyy-MM-dd。,RollingFileAppender的file子节点可有可无,通过设置file,可以为活动文件和归档文件指定不同位置,当前日志总是记录到file指定的文件(活动文件),活动文件的名字不会改变;
          如果没设置file,活动文件的名字会根据fileNamePattern的值,每隔一段时间改变一次。“/”或者“\”会被当做目录分隔符。
        • :
          可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每个月滚动,且是6,则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除。
      • :告知 RollingFileAppender 合适激活滚动。class=”ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy“: 查看当前活动文件的大小,如果超过指定大小会告知RollingFileAppender 触发当前活动文件滚动。只有一个节点:
          :这是活动文件的大小,默认值是10MB
          :当为true时,不支持FixedWindowRollingPolicy。支持TimeBasedRollingPolicy,但是有两个限制,1不支持也不允许文件压缩,2不能设置file属性,必须留空。(已废弃)      class=”ch.qos.logback.core.rolling.FixedWindowRollingPolicy“ 根据固定窗口算法重命名文件的滚动策略。有以下子节点:
          :窗口索引最小值
          :窗口索引最大值,当用户指定的窗口过大时,会自动将窗口设置为12。
          :必须包含“%i”例如,假设最小值和最大值分别为1和2,命名模式为 mylog%i.log,会产生归档文件mylog1.logmylog2.log。还可以指定文件压缩选项,例如,mylog%i.log.gz 或者 没有log%i.log.zip
          

      • :对记录事件进行格式化。负责两件事,一是把日志信息转换成字节数组,二是把字节数组写入到输出流。
        PatternLayoutEncoder是唯一有用的且默认的encoder ,有一个节点,用来设置日志的输入格式。使用“%”加“转换符”方式,如果要输出“%”,则必须用“\”对“%”进行转义。

    • 还有SocketAppender、SMTPAppender、DBAppender、SyslogAppender、SiftingAppender,并不常用,这里就不详解了。大家可以参考官方文档(http://logback.qos.ch/documentation.html),还可以编写自己的Appender。

  2. 子节点:用来设置某一个包或具体的某一个类的日志打印级别、以及指定仅有一个name属性,一个可选的level和一个可选的addtivity属性。

    可以包含零个或多个元素,标识这个appender将会添加到这个loger

    • name: 用来指定受此loger约束的某一个包或者具体的某一个类。
    • level: 用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL和OFF,还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。 如果未设置此属性,那么当前loger将会继承上级的级别。
    • addtivity: 是否向上级loger传递打印信息。默认是true。同一样,可以包含零个或多个元素,标识这个appender将会添加到这个loger
  3. 子节点:它也是元素,但是它是根loger,是所有的上级。只有一个level属性,因为name已经被命名为”root”,且已经是最上级了。

    • level: 用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL和OFF,不能设置为INHERITED或者同义词NULL。 默认是DEBUG
    • 元素,标识这个appender将会添加到这个root

常用logger配置

additivity=”false”,只在当前日式记录器中输出,如果不配置的话可能会出现问题

1
2
3
4
5
6
7
8
9
10
11
12
<!-- show parameters for hibernate sql 专为 Hibernate 定制 -->
<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE" additivity="false"/>
<logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="DEBUG" />
<logger name="org.hibernate.SQL" level="DEBUG" />
<logger name="org.hibernate.engine.QueryParameters" level="DEBUG" />
<logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG" />

<!--myibatis log configure-->
<logger name="com.apache.ibatis" level="TRACE"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>

注意事项

关于按大小生成文件

我们在日常中对于日志的归档和日志超过一定的大小生成一个新的文件,这些都是必须需求的,之前的配置可能会有问题,不会生效。

关于TimeBasedRollingPolicySizeBasedTriggeringPolicy和冲突

TimeBasedRollingPolicySizeBasedTriggeringPolicy不能同时使用,否则就会出现就算日志大小超过了之后也不会生成第二个文件,类似于下面这样

1
2
3
4
5
6
7
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/ERROR.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>100kb</MaxFileSize>
</triggeringPolicy>

解决办法,使用ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy类即可。

特别注意,输出格式中的%i一定要加上,不然不会生效

1
2
3
4
5
6
7
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/WARN.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<MaxHistory>1</MaxHistory>
<!-- 设置当前日志的文件的大小,决定日志翻滚 -->
<maxFileSize>100kb</maxFileSize>
<!--省略其它配置-->
</rollingPolicy>

关于自定义Policy

自己实现对应的类,参考SizeBasedTriggeringPolicy等一些实现类

其实xml标签最终会被解析成java的属性,这个属性需要有setter,getter方法

关于日志输出到数据库

需要建表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
CREATE TABLE `logging_event` (
`timestmp` bigint(20) NOT NULL,
`formatted_message` text NOT NULL,
`logger_name` varchar(254) NOT NULL,
`level_string` varchar(254) NOT NULL,
`thread_name` varchar(254) DEFAULT NULL,
`reference_flag` smallint(6) DEFAULT NULL,
`arg0` varchar(254) DEFAULT NULL,
`arg1` varchar(254) DEFAULT NULL,
`arg2` varchar(254) DEFAULT NULL,
`arg3` varchar(254) DEFAULT NULL,
`caller_filename` varchar(254) NOT NULL,
`caller_class` varchar(254) NOT NULL,
`caller_method` varchar(254) NOT NULL,
`caller_line` char(4) NOT NULL,
`event_id` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`event_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `logging_event_exception` (
`event_id` bigint(20) NOT NULL,
`i` smallint(6) NOT NULL,
`trace_line` varchar(254) NOT NULL,
PRIMARY KEY (`event_id`,`i`),
CONSTRAINT `logging_event_exception_ibfk_1` FOREIGN KEY (`event_id`) REFERENCES `logging_event` (`event_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `logging_event_property` (
`event_id` bigint(20) NOT NULL,
`mapped_key` varchar(254) NOT NULL,
`mapped_value` text,
PRIMARY KEY (`event_id`,`mapped_key`),
CONSTRAINT `logging_event_property_ibfk_1` FOREIGN KEY (`event_id`) REFERENCES `logging_event` (`event_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

更多配置

请访问log4j2的官网

Log4j2

单独使用

log4j2log4j的升级版,出自同一个作者,

导出依赖。不难发现再使用log4j的时候只需要导入一个依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency
1
2
3
4
5
6
7
public static void main(String[] args) {
Logger logger = LogManager.getLogger(Log4j2Demo.class.getName());
logger.trace("trace...");
logger.info("info...");
logger.warn("warn...");
logger.error("error...");
}

console输出:

1
14:42:19.863 [main] ERROR com.hunybei.demo.Log4j2Demo - error...

如果没有配置文件会使用默认的配置,由此看出,默认的输出级别是ERROR

log4j2配置文件

Log4j2能够在初始化期间自动配置,
它支持4种格式的配置文件:
Properties, YAML, JSON, XML
并且按从高到低的加权顺序查找配置文件。

这个文件可用于你的项目中,不过具体的参数需要你自己去设置以达到你想要的效果,如果需要加入控制台颜色特效,请在参数中设置-Dlog4j.skipJansi=false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration status="warn" monitorInterval="30">
<properties>
<!-- 定义一系列变量 -->
<Property name="logPath">d://logs</Property>
<Property name="consolePattern">%d [%t] %highlight{%-5p} %highlight{%-20c:%L} %m%n</Property>
<Property name="outputPattern">%d [%t] %-5p %-20c:%L %m%n</Property>
<Property name="curDay">%d{yyyy-MM-dd}</Property>
<Property name="curMonth">$${date:yyyy-MM}</Property>
</properties>
<appenders>
<console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${consolePattern}"/>
</console>
<!--
这个会打印出所有的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份 建立的文件夹下面并进行压缩,作为存档
如果你想把文件变成压缩包的格式,只需要把文件后缀改一下即可,如果要变成压缩包格式,例如.zip
注意配合compressionLevel参数一起使用
-->
<!--输出info日志 -->
<RollingFile name="RollingFileInfo" fileName="${logPath}/info.log"
filePattern="${logPath}/${curMonth}/info-${curDay}-%i.log.zip">
<Filters>
<ThresholdFilter level="INFO"/>
<!--过滤掉Warn级别以上的日志-->
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<PatternLayout pattern="${outputPattern}"/>
<Policies>
<!-- 归档每天的文件
interval:表示多久滚动一次。默认是1 hour
modulate:true/false用来调整时间:比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am
-->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 限制单个文件大小 -->
<SizeBasedTriggeringPolicy size="50kb"/>
</Policies>
<!-- 限制每天文件个数 compressionLevel:设置压缩级别0-9,其中0=无,1=最佳速度,通过9=最佳压缩。只适用于ZIP文件。 max:最大的文件个数-->
<DefaultRolloverStrategy compressionLevel="1" max="10"/>
</RollingFile>

<!--输出warn日志 -->
<RollingFile name="RollingFileWarn" fileName="${logPath}/warn.log"
filePattern="${logPath}/${curMonth}/warn-${curDay}-%i.log">
<Filters>
<ThresholdFilter level="WARN"/>
<ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<PatternLayout pattern="${outputPattern}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="50kb"/>
</Policies>
<DefaultRolloverStrategy compressionLevel="1" max="10"/>
</RollingFile>

<!--输出error日志 -->
<RollingFile name="RollingFileError" fileName="${logPath}/error.log"
filePattern="${logPath}/${curMonth}/error-${curDay}-%i.log">
<ThresholdFilter level="ERROR"/>
<PatternLayout pattern="${outputPattern}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="50kb"/>
</Policies>
<DefaultRolloverStrategy compressionLevel="1" max="10"/>
</RollingFile>
<!--异步log 配置,打印速度贼快,可自行测试 -->
<Async name="Async">
<appenderRef ref="Console"/>
</Async>
</appenders>

<loggers>

<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
<logger name="org.springframework" level="INFO"></logger>
<logger name="org.mybatis" level="INFO"></logger>
<root level="info">
<!--<appender-ref ref="Console"/> 用异步log替代-->
<appenderRef ref="Async"/>
<appender-ref ref="RollingFileInfo"/>
<appender-ref ref="RollingFileWarn"/>
<appender-ref ref="RollingFileError"/>
</root>
</loggers>

</configuration>

整合SLF4j

导入依赖,一个是SLF4j-api的依赖,这是接口。再导入一个适配log4j-slf4j-impl

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.1</version>
</dependency>
1
2
3
4
5
6
7
public static void main(String[] args) {        
Logger logger = LoggerFactory.getLogger(Log4j2Demo.class.getName());
logger.trace("trace...");
logger.info("info...");
logger.warn("warn...");
logger.error("error...");
}

console输出:

1
2019-12-10 17:40:31,759 [main] ERROR com.hunybei.demo.Log4j2Demo:15 error...

啦啦啦完美适配,默认的配置是Error级别的哦

Log4j2的配置介绍

log4j2配置节点说明

  如下图为log4j2的设计架构图,接下里对设计图中涉及的各节点做简单的介绍

  (1)LoggerContext:日志系统上下文

  (2)Configuration:每一个 LoggerContext 都有一个有效的 Configuration, Configuration 包含所有的Appender 、Filter、LoggerConfigStrSubstitutor引用和对Layout的格式设置

  (3)Logger : Logger继承自 AbstractLogger,当配置被修改后,它将与不同的 LoggerConfig 相关联,这导致其行为也被改变。

  (4)LoggerConfigLoggerConfig 对象在 Logger 被声明时创建,它包含了一组用于处理事件的Appender引用,以及一组用于过滤传递给Appender事件的Filter,相当于是Appender的集合。

  (5)AppenderLog4j2还允许将记录请求输出到多个目标中,而这种输出目标被称为Appender。目前Appender的类型有控制台、文件、socket、Apache Flume、JMS、远程UNIX 系统日志守护进程以及各种数据库API,用户可以根据需要选择将日志输出到不同的目标上,同时在一个Logger的配置中,允许开启多个Appender。

  (6)Filter :Log4j2 提供了Filter 来过滤消息事件,它可被应用于事件传递给LoggerConfig之前,及传递给LoggerConfig之后,即LoggerConfig的前后置拦截器。Filter包含了三种行为: Accept, Deny 或 Neutral,其中Accept,Deny分别代表着接受和拒绝,即过滤器接受或拒绝某种日志过滤表达式等,经过这两种行为处理后将不再经过其他过滤器。Neutral代表着中立,意味着事件应由其他Filter来处理。如果未配置任何Filter,那么事件将直接被处理。

  (7)Layout:Log4j2除了可以输出到不同的目标Appender之外,还支持在目标中定义自定义的日志格式,Layout 负责对日志事件进行格式化,通过配置PatternLayout来实现。

  (8)StrSubstitutorStrLookup:这两个组件用来对Log4j2中的各项配置进行动态变量赋值。``

  (9)日志级别:LoggerConfig会被分配一个日志级别,常用的级别包含TRACE, DEBUG, INFO, WARN, ERROR 和 FATAL

  1. 根节点Configuration有两个属性:statusmonitorinterval,

    有两个子节点:Appenders和Loggers(表明可以定义多个Appender和Logger).

       status用来指定log4j本身的打印日志的级别.

       monitorinterval用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s.

  1. Appenders节点,常见的有三种子节点:ConsoleRollingFileFile.

    Console节点用来定义输出到控制台的Appender.

    • name:指定Appender的名字.

    • target:SYSTEM_OUT 或 SYSTEM_ERR,一般只设置默认:SYSTEM_OUT.

    • PatternLayout:输出格式,不设置默认为:%m%n.

    File节点用来定义输出到指定位置的文件的Appender.

    • name:指定Appender的名字.

    • fileName:指定输出日志的目的文件带全路径的文件名.

    • PatternLayout:输出格式,不设置默认为:%m%n.

    RollingFile节点用来定义超过指定大小自动删除旧的创建新的的Appender.

    • name:指定Appender的名字.

    • fileName:指定输出日志的目的文件带全路径的文件名.

    • PatternLayout:输出格式,不设置默认为:%m%n.

    • filePattern:指定新建日志文件的名称格式.

    • Policies:指定滚动日志的策略,就是什么时候进行新建日志文件输出日志.

      • TimeBasedTriggeringPolicy:Policies子节点,基于时间的滚动策略,interval属性用来指定多久滚动一次,默认是1 hour。modulate=true用来调整时间:比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am,12am…而不是7am.
    • SizeBasedTriggeringPolicy:Policies子节点,基于指定文件大小的滚动策略,size属性用来定义每个日志文件的大小.

      • DefaultRolloverStrategy:用来指定同一个文件夹下最多有几个日志文件时开始删除最旧的,创建新的(通过max属性)。
    1. Loggers节点,常见的有两种:Root和Logger.

    Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出

    • level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.

      • AppenderRef:Root的子azsXDCFVDXSZAQqawszdxecfv hgbdxeswaq1 Q2W3E4RD5FT4E3W2Q1Q2W3E4RW2Q11` QAwszxazVCXDSSAQW,用来指定该日志输出到哪个Appender.

    Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。

    • level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.

    • name:用来指定该Logger所适用的类或者类所在的包全路径,继承自Root节点.

    • AppenderRef:Logger的子节点,用来指定该日志输出到哪个Appender,如果没有指定,就会默认继承自Root.如果指定了,那么会在指定的这个Appender和Root的Appender中都会输出,此时我们可以设置Logger的additivity=”false”只在自定义的Appender中进行输出。

  2. 关于日志level.

共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.

  • All:最低等级的,用于打开所有日志记录.

  • Trace:是追踪,就是程序推进以下,你就可以写个trace输出,所以trace应该会特别多,不过没关系,我们可以设置最低日志级别不让他输出.

  • Debug:指出细粒度信息事件对调试应用程序是非常有帮助的.

  • Info:消息在粗粒度级别上突出强调应用程序的运行过程.

  • Warn:输出警告及warn以下级别的日志.

  • Error:输出错误信息日志.

  • Fatal:输出每个严重的错误事件将会导致应用程序的退出的日志.

  • OFF:最高等级的,用于关闭所有日志记录.

程序会打印高于或等于所设置级别的日志,设置的日志等级越高,打印出来的日志就越少

注意事项

关于异步模式

  1. :此类异步队列是BockingQueue,队列默认大小是128

    1
    2
    3
    <Async name="Async">
    <appenderRef ref="Console"/>
    </Async>
  2. : 此类异步队列是Disruptor队列默认大小是4096,此配置需要加入disruptor框架

    1
    2
    3
    4
    <!--additivity="false" : additivity设置事件是否在root logger输出,为了避免重复输出,可以在Logger 标签下设置additivity为”false”-->
    <AsyncLogger name="com.hunybei" level="info" additivity="true">
    <appender-ref ref="Console"/>
    </AsyncLogger>
  3. 使用JVM参数

    1
    2
    #启动参数方式
    -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

    #代码方式

    1
    System.setProperty("Log4jContextSelector","org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");

更多配置

请访问log4j2的官网

Springboot整合日志框架

整合Logback

一个Springboot项目,天然支持logback,观看依赖我们可以发现,其实logback所需要的jar包已经被加进来了。我们只需要把logback的配置文件放到resource目录下即可。

新建配置文件logback-spring.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<?xml version="1.0" encoding="UTF-8" ?>
<configuration >
<property name="LOG_HOME" value="d://logs/"/>
<!-- 文件格式化输出-->
<property name="outputPattern"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger:%line - %msg%n"></property>

<property name="consolePattern"
value="%date{yyyy-MM-dd HH:mm:ss}%highlight(- %-5level) ---[%15.15t] %cyan(%-40.40logger{39}) : %msg%n"></property>

<!-- 控制台输出,如果是生产环境请去掉,不然疯狂输出烦死你 -->
<appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日志输出格式 -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${consolePattern}</pattern>
</layout>
</appender>

<!-- 按照每天生成日志文件 生成INFO日志 -->
<appender name="RollingFileINFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<append>false</append>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/INFO.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<!--最大保存天数-->
<MaxHistory>30</MaxHistory>
<maxFileSize>100kb</maxFileSize>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${outputPattern}</pattern>
</layout>
</appender>

<!-- 按照每天生成日志文件 生成WARN日志 -->
<appender name="RollingFileWARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/WARN.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
<maxFileSize>100kb</maxFileSize>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${outputPattern}</pattern>
</layout>
</appender>


<appender name="RollingFileError" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/ERROR.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
<maxFileSize>100kb</maxFileSize>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${outputPattern}</pattern>
</layout>
</appender>

<appender name ="ASYNC_FILEINFO" class= "ch.qos.logback.classic.AsyncAppender">
<!-- 设置为不丢失日志,默认如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold >0</discardingThreshold>
<!-- 更改默认的队列的深度,默认值为256 -->
<queueSize>1000</queueSize>
<appender-ref ref ="Stdout"/>
</appender>

<springProfile name="dev,test">
<root level="info">
<appender-ref ref="Stdout"/>
</root>
</springProfile>
<springProfile name="pro">
<root level="info">
<appender-ref ref="RollingFileINFO"/>
<appender-ref ref="RollingFileWARN"/>
<appender-ref ref="RollingFileError"/>
</root>
</springProfile>

</configuration>

关于logback文件的命名方式

共有8种

logback-test.groovy, logback-test.xml, logback.groovy, logback.xml

logback-test-spring.groovy, logback-test-spring.xml, logback-spring.groovy, logback-spring.xml

深入springboot源码

我们都知道Springboot的自动装配是基于Configuration模式的,一切的第三方框架在Springboot中集成都有对应的XXXConfiguration类,所以我们的Logback也有,在开发工具中搜索logback下面会出现一大堆的类,不难发现DefaultLogbackConfiguration这个类也在下面,这就是所谓的配置文件了,我们打开之后,发现并没有看到关于logback文件名的配置,这就很尴尬啊,回过头继续搜关于logback命名开头的类,有一个叫LogbackLoggingSystem的引起了我的注意,看名字就知道是日志的系统配置,点进去发现一片桃花源~

关键代码

1
2
3
4
@Override
protected String[] getStandardConfigLocations() {
return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };
}

可以看到支持这么多名字的配置文件,那为什么会支持logback-spring.xml这种呢?很是不解。不要慌,我们可以进入父类看看,因为Spring的设计模式用得很好,他不会一步到位的写,而是做了某种优化。

哇哦,发现了

1
2
3
4
5
6
7
8
9
10
11
protected abstract String[] getStandardConfigLocations();

protected String[] getSpringConfigLocations() {
String[] locations = getStandardConfigLocations();
for (int i = 0; i < locations.length; i++) {
String extension = StringUtils.getFilenameExtension(locations[i]);
locations[i] = locations[i].substring(0, locations[i].length() - extension.length() - 1) + "-spring."
+ extension;
}
return locations;
}

可以看到在这里加入了-spring这一名称,是不是恍然大悟呢,其实别框架整合的源码也是如此

Spring独有的配置

1
2
<!--这里可以取Spring的配置信息-->
<springProperty scope="context" name="appName" source="spring.application.name" defaultValue="defaultApp">

这里可以配置不同的环境输出不同的日志 ,与之对应的是resource目录下建对应的开发环境的文件夹,比如test,dev,pro,在不同的文件夹下写对应的配置,然后再激活配置文件即可

1
2
3
4
5
6
7
8
9
10
<springProfile name="dev,test">
<root level="info">
<appender-ref ref="Stdout"/>-->
</root>
</springProfile>
<springProfile name="pro">
<root level="info">
<appender-ref ref="RollingFileWARN"/>
</root>
</springProfile>

整合Log4j2

方法一

因为spring-boot-starter-web这个jar包默认依赖了spring-boot-starter-logging这个jar包,而这个jar包默认导入了很多的jar包,其中包括logbackjar,所以我们得排除这些jar包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
<!--这个jar包必须排除,不然会报错-->
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--再引入log4j2 的jar -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.1</version>
</dependency>

方法二

直接排除spring-boot-starter-logging这个jar,导入log4j2整合Springboot的启动包

其实启动包相当于帮我们导入了方法一里面的依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<version>1.3.0.RELEASE</version>
</dependency>

Log4j2配置文件,如果要启用配置异步AsyncLogger模式,需要加入依赖disruptor框架

1
2
3
4
5
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration monitorInterval="60" status="debug">
<properties>
<!-- 定义一系列变量 -->
<Property name="logPath">d://logs</Property>

<Property name="consolePattern">%date{yyyy-MM-dd HH:mm:ss.SSS} %highlight{%-5p} ---[%15.15t] %-40.40logger{39} : %msg%n</Property>
<Property name="outputPattern">%date{yyyy-MM-dd HH:mm:ss.SSS} %-5p ---[%15.15t] %-40.40logger{39} : %msg%n</Property>
<Property name="curDay">%d{yyyy-MM-dd}</Property>
<Property name="curMonth">$${date:yyyy-MM}</Property>
</properties>
<appenders>
<console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${consolePattern}"/>
</console>
<RollingFile name="RollingFileInfo" fileName="${logPath}/info.log"
filePattern="${logPath}/${curMonth}/info-${curDay}-%i.log.zip">
<Filters>
<ThresholdFilter level="INFO"/>
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<PatternLayout pattern="${outputPattern}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="false"/>
<SizeBasedTriggeringPolicy size="50kb"/>
</Policies>
<DefaultRolloverStrategy compressionLevel="1" max="10"/>
</RollingFile>

<RollingFile name="RollingFileWarn" fileName="${logPath}/warn.log"
filePattern="${logPath}/${curMonth}/warn-${curDay}-%i.log">
<Filters>
<ThresholdFilter level="WARN"/>
<ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<PatternLayout pattern="${outputPattern}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="50kb"/>
</Policies>
<DefaultRolloverStrategy compressionLevel="1" max="10"/>
</RollingFile>

<!--输出error日志 -->
<RollingFile name="RollingFileError" fileName="${logPath}/error.log"
filePattern="${logPath}/${curMonth}/error-${curDay}-%i.log">
<ThresholdFilter level="ERROR"/>
<PatternLayout pattern="${outputPattern}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="50kb"/>
</Policies>
<DefaultRolloverStrategy compressionLevel="1" max="10"/>
</RollingFile>
<!--异步log -->
<Async name="Async">
<appenderRef ref="Console"/>
</Async>

</appenders>

<loggers>
<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
<logger name="org.mybatis" level="INFO"></logger>
<logger name="org.springframework" level="INFO"/>
<root level="info">
<appender-ref ref="Async"/>
<appenderRef ref="RollingFileInfo"/>
<appenderRef ref="RollingFileWarn"/>
<appenderRef ref="RollingFileError"/>
</root>
</loggers>

</configuration>

关于log4j2的文件的命名方式

log4j2.properties,log4j2.yamllog4j2.ymllog4j2.jsonlog4j2.jsonlog4j2.jsnlog4j2.xml,

然后所有的后缀在后面加一个-spring均可生效

源码在Log4J2LoggingSystem里,回想一下之前logback的配置类,Springboot中的配置是不是大同小异呢?

1
2
3
4
5
6
7
8
9
10
11
12
private String[] getCurrentlySupportedConfigLocations() {
List<String> supportedConfigLocations = new ArrayList<>();
supportedConfigLocations.add("log4j2.properties");
if (isClassAvailable("com.fasterxml.jackson.dataformat.yaml.YAMLParser")) {
Collections.addAll(supportedConfigLocations, "log4j2.yaml", "log4j2.yml");
}
if (isClassAvailable("com.fasterxml.jackson.databind.ObjectMapper")) {
Collections.addAll(supportedConfigLocations, "log4j2.json", "log4j2.jsn");
}
supportedConfigLocations.add("log4j2.xml");
return StringUtils.toStringArray(supportedConfigLocations);
}

AbstractLoggingSystem Spring的日志抽象类,同Logback

1
2
3
4
5
6
7
8
9
protected String[] getSpringConfigLocations() {
String[] locations = getStandardConfigLocations();
for (int i = 0; i < locations.length; i++) {
String extension = StringUtils.getFilenameExtension(locations[i]);
locations[i] = locations[i].substring(0, locations[i].length() - extension.length() - 1) + "-spring."
+ extension;
}
return locations;
}

Spring独有的配置

logback

整合log4j

同上面的整合方式,此处不做过多描述

排除依赖,加入log4j的启动器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.0.RELEASE</version>
</dependency>

总结对比

看到这相信你已经大致的明白了日志框架的基本使用了。

现在仅凭我个人经验谈谈Logbacklog4j2的优劣。(log41j就不说了差不多已经退出历史的舞台)

性能方面:

Log2j

以下是网上给出的测试数据

  • 可以看到在同步日志模式下, Logback的性能是最糟糕的.
  • log4j2的性能无论在同步日志模式还是异步日志模式下都是最佳的.

log4j2优越的性能其原因在于log4j2使用了LMAX,一个无锁的线程间通信库代替了,logbacklog4j之前的队列. 并发性能大大提升。

其实博主也进行过类似的测试,循环写入100W条日志,log4j2确实更胜一筹

用户方面

logback优势:

1
根据log4j2的官方介绍,log4j2的性能要比logback高一点点,但据我对log4j1和Logback源码的熟悉,知道logback的作者几乎是个完美主义者,性能优化也做得非常好。所以,不要盲目相信log4j2的性能测试,所谓“王婆卖瓜,自卖自夸”,真实情况是:即便log4j2性能更好,那也只是在特定条件下或理论层面的,实际使用上,两者性能差不多。

在用户方面:

1
其实在国内Logback使用的人是要比Log4j2多的,而且SpringBoot对Logback天然的支持,这点我就不说了

个人推荐:

对于日志性能不高的推荐logback,高的话推荐log4j2,这当然也是仁者见仁智者见智了

-------------本文结束 感谢您的阅读-------------
湖南有北 wechat
扫码进群哦~~
你可以对我打赏哦