一、相关概念
1.1、 日志门面
日志门面是一个对外的、统一的日志接口,它不是具体的日志实现,需要和log4j、logback这样的实现框架配合使用。现在最流行的日志门面是slf4j,按照官方的说法,slf4j是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用所希望的日志系统。
问:为什么要使用日志门面?直接调用log4j等实现框架的API不行吗?
答:直接调用实现框架的API当然可以,但如果未来某一天有比log4j性能更好的实现框架,那升级替换可就是个大工程。另外,如果现有工程依赖了两个模块,两个模块分别用不同的日志实现方案,那现有工程就需要维护两套日志配置。但有了日志门面后一切都变了,我们仅需要引入一种实现,也只需要维护这一种实现的配置文件,其他的实现全部桥接到门面即可,往后如果需要升级日志实现,也只需要升级一个jar包、一个配置文件即可。
1.2、桥接器
我们在开发中当然可以只使用一种日志实现方案,但引用的第三方依赖可就不一定了,为了统一日志配置,我们需要将第三方依赖的日志实现”引流”到我们自己的日志框架上,这个“引流”的功能就叫做桥接器。桥接器仅能路由API层面的调用,无法路由底层实现。
1.3、日志实现
日志实现就是真正打日志干活的部分,Java中日志实现太多了,常见的有log4j、log4j2、jcl、logback等。
二、各种Jar包及功能一览
jar包 | 类型 | 转换方向 | 描述 | 注意事项 |
---|---|---|---|---|
jcl-over-slf4j | 桥接包 | jcl -> slf4j | 将Jakarta Commons Logging日志框架桥接到slf4j | |
jul-to-slf4j | 桥接包 | juc -> slf4j | 将java.util.logging的日志桥接到slf4j | |
osgi-over-slf4j | 桥接包 | osgi -> slf4j | 将osgi环境下的日志桥接到slf4j | |
slf4j-android | 桥接包 | android -> slf4j | 将android环境下的日志桥接到slf4j | |
log4j-over-slf4j | 桥接包 | log4j -> slf4j | 将log4j的日志桥接到slf4j | 不能和slf4j-log4j12同时用 |
log4j-to-slf4j | 桥接包 | log4j2 -> slf4j | 将log4j2的日志桥接到slf4j | 不能和log4j-slf4j-impl同时用 |
slf4j-api | slf4j门面 | slf4j的api接口jar包 | ||
slf4j-ext | slf4j门面 | slf4j扩展功能jar包 | ||
log4j-api | 实现包 | log4j2的api接口jar包 | ||
log4j-core | 实现包 | log4j2的日志输出核心jar包 | ||
log4j | 实现包 | log4j实现包(1.2) | ||
slf4j-migrator | 实现包 | 一个GUI工具,支持将代码中其他日志API转换为slf4j的写法 | ||
slf4j-jcl | slf4j+jcl打包 | slf4j -> jcl | slf4j采用Jakarta Commons Logging日志框架实现 | |
slf4j-jdk14 | slf4j+jul打包 | slf4j -> jul | slf4j采用java.util.logging实现 | 不能和jul-to-slf4j同时用 |
slf4j-log4j12 | slf4j+log4j12打包 | slf4j -> log4j | slf4j采用log4j实现(12表示1.2版本) | 不能和log4j-over-slf4j同时用 |
log4j-slf4j-impl | slf4j+log4j2打包 | slf4j -> log4j2 | slf4j采用log4j2实现 | 不能和log4j-to-slf4j同时用 |
slf4j-nop | slf4j+无输出打包 | slf4j -> null | slf4j的空接口输出绑定,丢弃所有日志输出 | |
slf4j-simple | sl4j+简单实现打包 | slf4j -> slf4j-simple | slf4j的自带的简单日志输出实现 | |
logback-classic | slf4j+logback打包 | slf4j -> logback | slf4j采用logback实现 | |
logback-core | logback核心依赖 | |||
logback-access | 与servlet容器集成,提供通过http访问日志功能 | |||
log4j-1.2-api | log4j桥接到log4j2 | log4j -> log4j2 | 将log4j的日志桥接到log4j2日志框架 |
Note:一般地,x-over-y.jar
表示将x
日志桥接到y
上,桥接动作只能路由API层面的调用,比如org.apache.log4j.Logger#getLogger(java.lang.Class)
,而不能路由底层实现调用,如果代码中引用了日志的具体实现,比如appenders、filters等,桥接包将对其不起作用,因此在代码中直接依赖日志底层实现的行为都应该被鄙视。
三、常见使用搭配
1、使用slf4j日志门面
- slf4j + log4j(已过时):引入
slf4j-log4j12
即可,另外可根据需求加入桥接包,不可与log4j-over-slf4j
同时使用,会引发StackOverflowError
异常。 - slf4j + log4j2(流行):引入
log4j-slf4j-impl
即可,另外可根据需求加入桥接包,不可与log4j-to-slf4j
同时使用,会引发StackOverflowError
异常。 - slf4j + logback(最优):引入
logback-classic
即可,另外可根据需求加入桥接包。logback与slf4j是同一人开发,兼容性很好,springboot默认就采用logback方案。
2、各实现单独使用,如log4j、log4j2等
- log4j:引入
log4j.jar
即可,上古程序员的最爱,不推荐。 - log4j2:引入
log4j-api
和log4j-core
即可,可根据需要引入log4j-1.2-api
将log4j 1.x桥接到log4j2。
强烈不推荐单独使用日志实现,请按阿里巴巴Java代码规范中【日志规约】编码。
四、常见问题
找到多个slf4j实现(Class path contains multiple SLF4J bindings.)
这种情况是上面的
slf4j+xxx打包
这样的jar包引入了两类导致的,我们根据需要保留一类,排除其他的就可以了。jar包不能共存,或者直接报
StackOverflowError
异常这种情况大多数原因是实现包与实现包对应的桥接包同时引入了,打印日志的时候就会发生死循环方法调用,一直到栈溢出,实现比较好的包如
log4j-slf4j-impl
,会在启动时告诉你两者不能共存。总的来说,实现和桥接只能选一个。引入桥接包后仍提示
xxxClassNotFound
、xxxMethodNotFound
,一般是代码中直接调用了日志底层实现,这种情况无法桥接,只能使用原始日志方案。其他异常情况大多数是由版本不兼容导致的,请参考slf4j错误含义及解决方法。
log4j(也称log4j12)与log4j2的区别:Log4j与Log4j2的区别。
五、常见日志配置
5.1 log4j配置
基于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
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<log4j:configuration debug="true" xmlns:log4j='http://jakarta.apache.org/log4j/' >
<!-- ========================== 自定义输出格式说明======================== -->
<!-- %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL -->
<!-- %r 输出自应用启动到输出该log信息耗费的毫秒数 -->
<!-- %c 输出所属的类目,通常就是所在类的全名 -->
<!-- %t 输出产生该日志事件的线程名 -->
<!-- %n 输出一个回车换行符,Windows平台为“/r/n”,Unix平台为“/n” -->
<!-- %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921 -->
<!-- %l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlo4.main(TestLog4.java:10) -->
<!-- ============================================================== -->
<!-- ========================== 输出方式说明========================= -->
<!-- Log4j提供的appender有以下几种: -->
<!-- org.apache.log4j.ConsoleAppender(控制台), -->
<!-- org.apache.log4j.FileAppender(文件), -->
<!-- org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件), -->
<!-- org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件), -->
<!-- org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方) -->
<!-- ================================================================== -->
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<!-- <param name="Target" value="System.out"/> -->
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c Method: %l ]%n%p:%m%n"/>
</layout>
<!-- <filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="DEBUG"/>
<param name="LevelMax" value="DEBUG"/>
</filter> -->
</appender>
<!-- output the debug -->
<!-- <appender name="log4jDebug" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="log_"/>
<param name="MaxFileSize" value="KB"/>
<param name="MaxBackupIndex" value="2"/> -->
<appender name="log4jDebug" class="org.apache.log4j.rolling.RollingFileAppender">
<param name="Append" value="true"/>
<rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
<param name="FileNamePattern" value="./log/log_%d{yyyy-MM-dd}.log" />
</rollingPolicy>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c Method: %l ]%n%p:%m%n"/>
</layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="DEBUG"/>
<param name="LevelMax" value="DEBUG"/>
</filter>
</appender>
<!-- <appender name="log4jInfo" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="log_"/>
<param name="DatePattern" value="'.log'yyyy-MM-dd"/>
<param name="Append" value="true"/>
<param name="MaxFileSize" value="5KB"/>
<param name="MaxBackupIndex" value="2"/> -->
<appender name="log4jInfo" class="org.apache.log4j.rolling.RollingFileAppender">
<param name="Append" value="true"/>
<rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
<param name="FileNamePattern" value="./log/log_%d{yyyy-MM-dd}.log" />
</rollingPolicy>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c Method: %l ]%n%p:%m%n"/>
</layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="INFO"/>
<param name="LevelMax" value="INFO"/>
</filter>
</appender>
<!-- <appender name="log4jWarn" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="/log_"/>
<param name="DatePattern" value="'.log'yyyy-MM-dd"/>
<param name="Append" value="true"/>
<param name="MaxFileSize" value="5KB"/>
<param name="MaxBackupIndex" value="2"/> -->
<appender name="log4jWarn" class="org.apache.log4j.rolling.RollingFileAppender">
<param name="Append" value="true"/>
<rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
<param name="FileNamePattern" value="./log/log_%d{yyyy-MM-dd}.log" />
</rollingPolicy>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c Method: %l ]%n%p:%m%n"/>
</layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="WARN"/>
<param name="LevelMax" value="WARN"/>
</filter>
</appender>
<!-- <appender name="log4jError" class="org.apache.log4j.DailyRollingFileAppender"> -->
<appender name="log4jError" class="org.apache.log4j.rolling.RollingFileAppender">
<!-- <param name="File" value="/error_"/>
<param name="DatePattern" value="'.log'yyyy-MM-dd"/> -->
<param name="Append" value="true"/>
<rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
<param name="FileNamePattern" value="./log/error_%d{yyyy-MM-dd}.log" />
</rollingPolicy>
<!-- <param name="MaxFileSize" value="5KB"/> -->
<!-- <param name="MaxBackupIndex" value="2"/> -->
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c Method: %l ]%n%p:%m%n"/>
</layout>
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="LevelMin" value="ERROR"/>
<param name="LevelMax" value="ERROR"/>
</filter>
</appender>
<!--通过<category></category>的定义可以将各个包中的类日志输出到不同的日志文件中-->
<!-- <category name="com.gzy">
<priority value="debug" />
<appender-ref ref="log4jTestLogInfo" />
<appender-ref ref="log4jTestDebug" />
</category> -->
<appender name="MAIL"
class="org.apache.log4j.net.SMTPAppender">
<param name="threshold" value="debug" />
<!-- 日志的错误级别
<param name="threshold" value="error"/>
-->
<!-- 缓存文件大小,日志达到512K时发送Email -->
<param name="BufferSize" value="512" /><!-- 单位K -->
<param name="From" value="[email protected]" />
<param name="SMTPHost" value="smtp.163.com" />
<param name="Subject" value="juyee-log4jMessage" />
<param name="To" value="[email protected]" />
<param name="SMTPUsername" value="test" />
<param name="SMTPPassword" value="test" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%-d{yyyy-MM-dd HH:mm:ss.SSS a} [%p]-[%c] %m%n" />
</layout>
</appender>
<root>
<priority value="debug"/>
<appender-ref ref="CONSOLE" />
<appender-ref ref="log4jDebug" />
<appender-ref ref="log4jInfo" />
<appender-ref ref="log4jWarn" />
<appender-ref ref="log4jError" />
<!-- <appender-ref ref="MAIL" /> -->
</root>
</log4j:configuration>基于properties文件的配置
点击展开代码
参考文档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# priority :debug<info<warn<error
#you cannot specify every priority with different file for log4j
log4j.rootLogger=debug,stdout,info,debug,warn,error
#console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= [%d{yyyy-MM-dd HH:mm:ss a}]:%p %l%m%n
#info log
log4j.logger.info=info
log4j.appender.info=org.apache.log4j.DailyRollingFileAppender
log4j.appender.info.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.info.File=./src/com/hp/log/info.log
log4j.appender.info.Append=true
log4j.appender.info.Threshold=INFO
log4j.appender.info.layout=org.apache.log4j.PatternLayout
log4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#debug log
log4j.logger.debug=debug
log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender
log4j.appender.debug.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.debug.File=./src/com/hp/log/debug.log
log4j.appender.debug.Append=true
log4j.appender.debug.Threshold=DEBUG
log4j.appender.debug.layout=org.apache.log4j.PatternLayout
log4j.appender.debug.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#warn log
log4j.logger.warn=warn
log4j.appender.warn=org.apache.log4j.DailyRollingFileAppender
log4j.appender.warn.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.warn.File=./src/com/hp/log/warn.log
log4j.appender.warn.Append=true
log4j.appender.warn.Threshold=WARN
log4j.appender.warn.layout=org.apache.log4j.PatternLayout
log4j.appender.warn.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#error
log4j.logger.error=error
log4j.appender.error = org.apache.log4j.DailyRollingFileAppender
log4j.appender.error.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.error.File = ./src/com/hp/log/error.log
log4j.appender.error.Append = true
log4j.appender.error.Threshold = ERROR
log4j.appender.error.layout = org.apache.log4j.PatternLayout
log4j.appender.error.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
5.2 log4j2配置
点击展开代码
参考文档1 |
|
5.3 logback配置
点击展开代码
参考文档1 |
|