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-core、logback-classic 和 logback-access。其中,core 是核心模块,classic 可以看作是 core 的进阶版本(完整实现了 slf4j-api),access 模块与 Servlet 容器集成提供通过 Http 来访问日志。
Appender
记录器将 LoggingEvents 传递给 Appender。追加者执行日志记录的实际工作。我们通常认为日志记录是进入文件或控制台的东西,但 Logback 的功能远不止于此。Logback-core 提供了几个有用的 appender。
ConsoleAppender
FileAppender
FileAppender 将消息追加到文件中。它支持广泛的配置参数。
<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(常用)
通常情况下,我们希望根据时间、日志文件大小或两者的组合来 “滚动” 文件,而不是形成一个超大的日志文件。
<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 根据不同的操作系统使用不同的路径存放日志
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} 引用即可
<define name="log.path" class="com.xxx.config.LogbackHomeConfig"/>
常见的日志级别
-
OFF: 最高等级,关闭所有日志记录。如果将日志级别设置为此级别,那么不会有任何日志消息被记录。
-
FATAL / CRITICAL / EMERGENCY: 表示非常严重的错误,可能导致应用程序崩溃或无法继续运行。
-
ERROR / SEVERE: 表示发生了错误,但应用程序仍能继续运行。这类消息通常用于记录异常和其他非预期行为。
-
WARN / WARNING: 表示潜在的问题,可能不会立即影响应用程序,但值得引起注意。
-
INFO: 提供一般性的信息,如服务启动、停止、状态变化等。这些消息对了解应用程序的整体行为有所帮助。
-
DEBUG: 提供详细的调试信息,主要用于开发过程中排查问题。在生产环境中,通常只有在诊断故障时才会启用此级别。
-
TRACE: 提供非常详细的跟踪信息,通常只在分析极复杂的问题时使用。记录的信息可能包括大量的堆栈跟踪和其他详细的技术数据。
-
ALL: 最低等级,记录所有级别的日志消息。通常只在开发期间用于全面监控应用程序的行为。
以上日志级别并非所有日志框架都会完全支持,某些框架可能会使用略有差异的命名习惯。
Log4j2
log4j2 和 logback 的概念差不多,可以对比着看,只是有一些配置上的不同,其它大同小异,根据需要微调即可。
<?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
<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 点发生滚动。

说些什么吧!