SLF4J
Java 的日志门面,使用者居多。这里为了更方便理解,引用 Rust 语言圣经 里的一段话:
slf4j 是 Java 的日志门面库,日志门面不是说排场很大的意思,而是指相应的日志 API 已成为事实上的标准,会被其它日志框架所使用(比如 Logback,Log4j 和 java.util.logging)。通过这种统一的门面,开发者就可以不必再拘泥于日志框架的选择,未来大不了再换一个日志框架就是。
所以,SLF4J 就像是一个接口,其它的具体日志框架是对该接口的实现,应用中选择使用哪个就看开发者自己的选择。
与 SLF4J 一样同为日志门面,可以绑定到不同的日志管理器,使应用程序与日志管理器解耦。JBoss 还有一套自己的日志框架:JBoss Log Manager。
日志框架
Logback 的使用较为简单,可以参考 官方文档
以下是常见的打印日志的格式:
%d{HH:mm:ss.SSS}
– 包含小时、分钟、秒和毫秒的时间戳
[%thread]
– 生成日志消息的线程名称,用方括号括起来
%-5level
– 日志记录事件的级别,填充为 5 个字符
%logger{36}
– 记录器的名称,截断为 35 个字符
%msg%n
– 日志消息后跟与平台相关的行分隔符
[%method,%line]
- 在哪个方法的第几行调用了日志打印
Logback 分为三个模块:logback-core
、logback-classic
和 logback-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">
<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.xml1 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"> <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.gz</fileNamePattern>
<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.java1 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"); return os.toLowerCase().contains("window") ? "./logs" : "/home/" + username + "/logs"; } }
|
然后在 logback.xml
中定义一个属性,在后面具体的日志中使用 ${log.path}
引用即可
logback.xml1
| <define name="log.path" class="com.xxx.config.LogbackHomeConfig"/>
|
常见的日志级别
-
OFF:
最高等级,关闭所有日志记录。如果将日志级别设置为此级别,那么不会有任何日志消息被记录。
-
FATAL / CRITICAL / EMERGENCY:
表示非常严重的错误,可能导致应用程序崩溃或无法继续运行。
-
ERROR / SEVERE:
表示发生了错误,但应用程序仍能继续运行。这类消息通常用于记录异常和其他非预期行为。
-
WARN / WARNING:
表示潜在的问题,可能不会立即影响应用程序,但值得引起注意。
-
INFO:
提供一般性的信息,如服务启动、停止、状态变化等。这些消息对了解应用程序的整体行为有所帮助。
-
DEBUG:
提供详细的调试信息,主要用于开发过程中排查问题。在生产环境中,通常只有在诊断故障时才会启用此级别。
-
TRACE:
提供非常详细的跟踪信息,通常只在分析极复杂的问题时使用。记录的信息可能包括大量的堆栈跟踪和其他详细的技术数据。
-
ALL:
最低等级,记录所有级别的日志消息。通常只在开发期间用于全面监控应用程序的行为。
以上日志级别并非所有日志框架都会完全支持,某些框架可能会使用略有差异的命名习惯。
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 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"/>
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> </Console>
<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}" /> <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> <Policies> <TimeBasedTriggeringPolicy interval="1" modulate="true" /> <SizeBasedTriggeringPolicy size="50MB" /> </Policies> <DefaultRolloverStrategy max="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>
<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> <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 点发生滚动。