SLF4J

Java 的日志门面,使用者居多。这里为了更方便理解,引用 Rust 语言圣经 里的一段话:

slf4j 是 Java 的日志门面库,日志门面不是说排场很大的意思,而是指相应的日志 API 已成为事实上的标准,会被其它日志框架所使用(比如 Logback,Log4j 和 java.util.logging)。通过这种统一的门面,开发者就可以不必再拘泥于日志框架的选择,未来大不了再换一个日志框架就是。

所以,SLF4J 就像是一个接口,其它的具体日志框架是对该接口的实现,应用中选择使用哪个就看开发者自己的选择。

JBoss Logging

与 SLF4J 一样同为日志门面,可以绑定到不同的日志管理器,使应用程序与日志管理器解耦。JBoss 还有一套自己的日志框架:JBoss Log Manager。

日志框架

Logback

Logback 的使用较为简单,可以参考 官方文档

以下是常见的打印日志的格式:

  • %d{HH:mm:ss.SSS} – 包含小时、分钟、秒和毫秒的时间戳
  • [%thread] – 生成日志消息的线程名称,用方括号括起来
  • %-5level – 日志记录事件的级别,填充为 5 个字符
  • %logger{36} – 记录器的名称,截断为 35 个字符
  • %msg%n – 日志消息后跟与平台相关的行分隔符
  • [%method,%line] - 在哪个方法的第几行调用了日志打印

Logback 分为三个模块:logback-corelogback-classiclogback-access。其中,core 是核心模块,classic 可以看作是 core 的进阶版本(完整实现了 slf4j-api),access 模块与 Servlet 容器集成提供通过 Http 来访问日志。

Appender

记录器将 LoggingEvents 传递给 Appender。追加者执行日志记录的实际工作。我们通常认为日志记录是进入文件或控制台的东西,但 Logback 的功能远不止于此。Logback-core 提供了几个有用的 appender。

ConsoleAppender

FileAppender

FileAppender 将消息追加到文件中。它支持广泛的配置参数。

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

<configuration debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>tests.log</file>
<append>true</append>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>

<logger name="com.leaf.logback" level="INFO"/>
<logger name="com.leaf.logback.tests" level="WARN">
<appender-ref ref="FILE"/>
</logger>

<root level="debug">
<appender-ref ref="STDOUT"/>
</root>
</configuration>

FileAppender 通过 <file> 配置文件名。<append> 标签指示 Appender 添加到现有文件,而不是截断文件。如果我们多次运行测试,就会发现日志输出被追加到同一个文件中。

如果我们重新运行上面的测试,com.leaf.logback.tests 的消息将同时输出到控制台和 tests.log 的文件。子日志记录器(logger)同时继承根日志记录器
ConsoleAppender 与 FileAppender 的配置。Appenders 的效果是累加的。

RollingFileAppender(常用)

通常情况下,我们希望根据时间、日志文件大小或两者的组合来 “滚动” 文件,而不是形成一个超大的日志文件。

logback.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<configuration>
<property name="LOG_FILE" value="LogFile"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.gz</fileNamePattern>

<!-- keep 30 days' worth of history capped at 3GB total size -->
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
</configuration>

RollingFileAppender 有一个 RollingPolicy(滚动策略)。在此示例配置中,是基于时间的滚动策略(TimeBasedRollingPolicy)。

与 FileAppender 类似,我们在配置该应用程序时也使用了 file。我们声明了一个属性并使用了它,因为我们将在后面中重复使用此文件名。

我们在 RollingPolicy 中定义了 fileNamePattern。该模式不仅定义了文件名,还定义了滚动文件的频率。基于时间的滚动策略(TimeBasedRollingPolicy)会检查该模式,并在最精确定义的时间段进行滚动。

官网 Policy 说明:https://logback.qos.ch/manual/appenders.html#onRollingPolicies

自定义 Appender

参考该文章:https://www.baeldung.com/custom-logback-appender

logback 根据不同的操作系统使用不同的路径存放日志

com.xxx.config.LogbackHomeConfig.java
1
2
3
4
5
6
7
8
9
10
11
public class LogbackHomeConfig extends PropertyDefinerBase {
@Override
public String getPropertyValue() {
// 获取用户名
String username = System.getProperty("user.name");
// 获取操作系统
String os = System.getProperty("os.name");
// 路径常量可以放到常量类维护,这里说明问题即可(目录放在jar包的同级目录)
return os.toLowerCase().contains("window") ? "./logs" : "/home/" + username + "/logs";
}
}

然后在 logback.xml 中定义一个属性,在后面具体的日志中使用 ${log.path} 引用即可

logback.xml
1
<define name="log.path" class="com.xxx.config.LogbackHomeConfig"/>

常见的日志级别

  1. OFF
    最高等级,关闭所有日志记录。如果将日志级别设置为此级别,那么不会有任何日志消息被记录。

  2. FATAL / CRITICAL / EMERGENCY
    表示非常严重的错误,可能导致应用程序崩溃或无法继续运行。

  3. ERROR / SEVERE
    表示发生了错误,但应用程序仍能继续运行。这类消息通常用于记录异常和其他非预期行为。

  4. WARN / WARNING
    表示潜在的问题,可能不会立即影响应用程序,但值得引起注意。

  5. INFO
    提供一般性的信息,如服务启动、停止、状态变化等。这些消息对了解应用程序的整体行为有所帮助。

  6. DEBUG
    提供详细的调试信息,主要用于开发过程中排查问题。在生产环境中,通常只有在诊断故障时才会启用此级别。

  7. TRACE
    提供非常详细的跟踪信息,通常只在分析极复杂的问题时使用。记录的信息可能包括大量的堆栈跟踪和其他详细的技术数据。

  8. ALL
    最低等级,记录所有级别的日志消息。通常只在开发期间用于全面监控应用程序的行为。

以上日志级别并非所有日志框架都会完全支持,某些框架可能会使用略有差异的命名习惯。

Log4j2

log4j2 和 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
<?xml version="1.0" encoding="UTF-8"?>
<!-- Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出 -->
<!-- monitorInterval:Log4j 检测配置文件的时间间隔,并自动重新配置,默认为 0 不开启 -->
<Configuration monitorInterval="60">

<Properties>
<Property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight{%-5level}{ERROR=Bright RED, WARN=Bright Yellow, INFO=Bright Green, DEBUG=Bright Cyan, TRACE=Bright White} %style{ [%t] %c{1.}-%M:%L}{black}: %msg%n"/>
<Property name="FILE_PATH" value="${sys:log.dir:-logs}"/>
<Property name="FILE_NAME" value="app-log"/>
</Properties>

<Appenders>

<Console name="Console" target="SYSTEM_OUT">
<!--输出日志的格式-->
<PatternLayout pattern="${LOG_PATTERN}" disableAnsi="true" noConsoleNoAnsi="true"/>

<!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
</Console>

<!-- INFO 日志的滚动文件 -->
<RollingFile name="info" fileName="${FILE_PATH}/${FILE_NAME}-info.log"
filePattern="${FILE_PATH}/${FILE_NAME}-info-%d{yyyy-MM-dd}_%i.log">
<PatternLayout pattern="${LOG_PATTERN}" />
<!-- 只接收 info 级别及以上的日志,其它的全部过滤 -->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<Policies>
<!-- 按天滚动,interval 默认为 1,单位与上面的 filePattern 有关,这里使用的为 {yyyy-MM-dd},因此是天 -->
<!-- modulate 设置为 true 表示不使用程序启动后到下次滚动的时间间隔隔为一天-->
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
<!-- 每个文件最大 50 MB,超过使用新建文件 -->
<SizeBasedTriggeringPolicy size="50MB" />
</Policies>
<!-- 每天最多只能有 30 个文件 -->
<DefaultRolloverStrategy max="30">
<!-- 归档 30 天前的日志文件,然后删除 -->
<NestedConditions>
<IfFileName glob="${FILE_NAME}-*.log">
<IfLastModified age="30d">
<Then>
<Compression compressFileNamePattern="${FILE_PATH}/${FILE_NAME}-info-%d{yyyy-MM-dd}-%i.log.gz" />
<Delete basePath="${FILE_PATH}">
<IfFileName glob="${FILE_NAME}-info-%d{yyyy-MM-dd}_%i.log" />
</Delete>
</Then>
</IfLastModified>
</IfFileName>
</NestedConditions>
</DefaultRolloverStrategy>
</RollingFile>

<!-- ERROR 日志的滚动文件 -->
<RollingFile name="error" fileName="${FILE_PATH}/${FILE_NAME}-error.log"
filePattern="${FILE_PATH}/${FILE_NAME}-error-%d{yyyy-MM-dd}_%i.log">
<PatternLayout pattern="${LOG_PATTERN}" />
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="50MB" />
</Policies>
</RollingFile>

<!-- 控制台输出 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}" />
</Console>
</Appenders>

<Loggers>
<!-- 指定了 additivity="false" 属性后,指定包的日志走自己的通道 -->
<Logger name="org.mybatis" level="info" additivity="false">
<AppenderRef ref="Console"/>
</Logger>

<Logger name="org.springframework" level="info" additivity="false">
<AppenderRef ref="Console"/>
</Logger>

<!-- 根日志记录器,兜底设置 -->
<Root level="info">
<AppenderRef ref="Console" />
<AppenderRef ref="info" level="info" />
<AppenderRef ref="error" level="error" />
</Root>
</Loggers>

</Configuration >

除了 sys,log4j2 还支持其他前缀来引用不同的变量类型:

sys: 引用系统环境变量或系统属性。
env: 引用环境变量(在某些情况下,env: 和 sys: 是等价的)。
ctx: 引用 ServletContext 属性(仅在 Web 应用中使用)。
main: 引用 Main 类的属性(仅在使用 log4j2 的 Main 类时使用)。

使用非 gz 格式需要额外引入依赖包支持其它格式的压缩算法。官方文档:https://logging.apache.org/log4j/2.x/manual/appenders/rolling-file.html#RolloverStrategy-compress

1
2
3
4
5
6
<RollingFile name="info" fileName="${FILE_PATH}/${FILE_NAME}-info.log"
filePattern="${FILE_PATH}/${FILE_NAME}-info-%d{yyyy-MM-dd}_%i.log">
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
</RollingFile>

这段配置较为容易出错,filePattern 设置的格式影响着 TimeBasedTriggeringPolicy 中的 interval 属性单位。上面的这段配置单位是 ;而如果 filePattern 设置为 xxx-%d{yyyy-MM-dd-HH:mm},则 interval 的单位为 分钟

modulate 用于控制滚动文件记录器的滚动间隔是否应该依赖于时间,而不是设定的精确时间间隔。默认情况下,他会按照自定义的时间间隔去滚动文件,这往往不是我们想要的,我们想要的是根据实际的时间去滚动。

举个荔枝,modulate 为 false 时,会按照程序启动的时间为基准,去加上自定义的时间间隔滚动,假如我们的程序在 23:00 启动,时间间隔为 1 天,那么当天 0 点并不会发生日志滚动;为 true 时,才会按照实际的时间滚定,23:00 启动的程序将会在当天 0 点发生滚动。


本站由 江湖浪子 使用 Stellar 1.29.1 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。