Logback是一个可靠、通用且快速的Java日志框架,作为Log4j的继承者,由Log4j创始人设计。它由三个模块组成:
为什么选择Logback?
Spring Boot默认使用Logback作为日志框架,当你使用spring-boot-starter
或spring-boot-starter-web
时,已经自动引入了Logback依赖。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@RestController
public class DemoController {
// 获取Logger实例,通常以当前类作为参数
private static final Logger logger = LoggerFactory.getLogger(DemoController.class);
@GetMapping("/demo")
public String demo() {
logger.trace("This is a TRACE level message");
logger.debug("This is a DEBUG level message");
logger.info("This is an INFO level message");
logger.warn("This is a WARN level message");
logger.error("This is an ERROR level message");
return "Check your console or log file!";
}
}
Spring Boot会按以下顺序查找Logback配置文件:
logback-spring.xml
(推荐使用)logback.xml
为什么推荐使用logback-spring.xml?
标签一个完整的Logback配置文件通常包含以下部分:
<configuration>
<property name="LOG_HOME" value="./logs" />
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%npattern>
encoder>
appender>
<logger name="com.example.demo" level="DEBUG" />
<root level="INFO">
<appender-ref ref="CONSOLE" />
root>
configuration>
元素
是Logback配置文件的根元素,支持以下重要属性:
属性名 | 类型 | 默认值 | 描述 |
---|---|---|---|
scan | boolean | false | 是否监视配置文件变化 |
scanPeriod | 时间间隔 | 1分钟 | 检查配置文件变化的时间间隔 |
debug | boolean | false | 是否打印Logback内部调试信息 |
packagingData | boolean | false | 是否包含调用者信息(影响性能) |
示例:
<configuration scan="true" scanPeriod="30 seconds" debug="false">
configuration>
最佳实践:
scan
以便动态调整配置scan
和debug
以避免性能开销packagingData
元素用于定义变量,可在配置文件中重复使用:
属性:
name
: 变量名(必填)value
: 变量值file
: 从外部文件加载属性resource
: 从classpath资源加载属性scope
: 作用域(“local"或"context”)示例:
<property name="LOG_HOME" value="/var/logs/myapp" />
<property name="LOG_HOME" value="${LOG_DIR:-./logs}" />
<property file="conf/logback.properties" />
<springProperty scope="context" name="appName" source="spring.application.name" />
变量引用方式: ${变量名}
作用域说明:
local
: 仅在当前配置文件中有效context
: 在整个LoggerContext中有效
元素用于定义时间戳变量:
属性:
key
: 变量名datePattern
: 日期格式(遵循Java SimpleDateFormat)timeReference
: 时间参考点("contextBirth"或当前时间)示例:
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss" />
<property name="LOG_FILE" value="${LOG_HOME}/log-${bySecond}.log" />
元素Appender负责定义日志输出目的地,是Logback最核心的组件之一。
<appender name="UNIQUE_NAME" class="APPENDER_CLASS">
<filter class="ch.qos.logback.core.filter.FilterClass" />
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %msg%npattern>
encoder>
appender>
输出日志到控制台(System.out或System.err)
特有属性:
target
: 输出目标(“System.out"或"System.err”),默认为System.outwithJansi
: 是否启用ANSI颜色支持(需jansi库)示例:
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<target>System.outtarget>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%npattern>
encoder>
appender>
输出日志到单个文件
特有属性:
file
: 日志文件路径append
: 是否追加到文件末尾(默认为true)prudent
: 是否安全模式(多进程写入同一文件时使用)示例:
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${LOG_HOME}/myapp.logfile>
<append>trueappend>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%npattern>
encoder>
appender>
支持日志文件滚动的增强FileAppender,必须配置rollingPolicy
特有属性:
file
: 当前活动日志文件路径rollingPolicy
: 滚动策略配置triggeringPolicy
: 触发策略配置示例:
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/myapp.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/myapp.%d{yyyy-MM-dd}.logfileNamePattern>
<maxHistory>30maxHistory>
rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%npattern>
encoder>
appender>
Logback提供多种滚动策略:
TimeBasedRollingPolicy
基于时间的滚动策略
属性 | 说明 |
---|---|
fileNamePattern | 滚动文件命名模式(必须包含%d) |
maxHistory | 保留的历史日志文件最大数量 |
totalSizeCap | 所有日志文件总大小限制 |
cleanHistoryOnStart | 启动时是否清理历史文件(默认false) |
SizeAndTimeBasedRollingPolicy
结合时间和大小的滚动策略
属性 | 说明 |
---|---|
fileNamePattern | 必须包含%d和%i |
maxFileSize | 单个文件最大大小 |
maxHistory | 保留的历史日志文件最大数量 |
totalSizeCap | 所有日志文件总大小限制 |
FixedWindowRollingPolicy
固定窗口滚动策略
属性 | 说明 |
---|---|
minIndex | 窗口最小索引 |
maxIndex | 窗口最大索引 |
fileNamePattern | 必须包含%i |
Appender类型 | 类名 | 用途 |
---|---|---|
SMTPAppender | ch.qos.logback.classic.net.SMTPAppender | 通过邮件发送错误日志 |
DBAppender | ch.qos.logback.classic.db.DBAppender | 存储日志到数据库 |
SocketAppender | ch.qos.logback.classic.net.SocketAppender | 通过网络socket发送日志 |
SyslogAppender | ch.qos.logback.classic.net.SyslogAppender | 发送日志到syslog服务器 |
元素Encoder负责将日志事件转换为字节数组并写入输出流。
最常用的Encoder实现,支持丰富的模式表达式:
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%npattern>
<outputPatternAsHeader>falseoutputPatternAsHeader>
<charset>UTF-8charset>
encoder>
常用转换符:
转换符 | 说明 |
---|---|
%d | 日期时间 |
%thread | 线程名 |
%level | 日志级别 |
%logger | Logger名称 |
%msg | 日志消息 |
%n | 换行符 |
%caller | 调用者信息 |
%mdc | MDC内容 |
%ex | 异常堆栈 |
%marker | 日志标记 |
日期格式:
%d{格式}
,格式遵循Java SimpleDateFormat:
%d{yyyy-MM-dd HH:mm:ss.SSS}
%d{ISO8601}
%d{ABSOLUTE}
Logger名称缩写:
%logger{长度}
,如%logger{36}
表示最长显示36个字符
包装其他Layout实现的Encoder:
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%npattern>
layout>
encoder>
元素Filter用于对日志事件进行过滤,可以配置在Appender或Logger上。
精确匹配特定级别:
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERRORlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
动作类型:
ACCEPT
: 接受日志事件DENY
: 拒绝日志事件NEUTRAL
: 中立,由后续过滤器决定阈值过滤,接受>=指定级别的日志:
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARNlevel>
filter>
使用Groovy表达式过滤:
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator">
<expression>
e.level.toInt() >= WARN.toInt() &&
e.getMessage().contains("important")
expression>
evaluator>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
实现ch.qos.logback.core.filter.Filter
接口:
public class SampleFilter extends Filter<ILoggingEvent> {
@Override
public FilterReply decide(ILoggingEvent event) {
if (event.getMessage().contains("special")) {
return FilterReply.ACCEPT;
}
return FilterReply.NEUTRAL;
}
}
配置:
<filter class="com.example.SampleFilter" />
和
元素
配置特定Logger的日志行为:
属性:
name
: Logger名称(通常为包或类名)level
: 日志级别additivity
: 是否向上传递日志(默认为true)示例:
<logger name="com.example" level="DEBUG" />
<logger name="com.example.Service" level="TRACE" additivity="false">
<appender-ref ref="SPECIAL_APPENDER" />
logger>
配置根Logger,所有Logger最终都继承自Root Logger:
属性:
level
: 根Logger的日志级别示例:
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
root>
Logger继承规则:
元素TurboFilter是全局过滤器,在所有Logger之前执行:
<turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
<Marker>importantMarker>
<OnMatch>ACCEPTOnMatch>
<OnMismatch>NEUTRALOnMismatch>
turboFilter>
常用TurboFilter:
MDCFilter
: 基于MDC值过滤DynamicThresholdFilter
: 动态阈值过滤LoggerFilter
: 基于Logger名称过滤Logback支持以下日志级别(从低到高):
日志级别继承规则:
在logback-spring.xml
中可以使用Spring Boot的属性:
<springProperty scope="context" name="appName" source="spring.application.name" defaultValue="myApp"/>
<property name="LOG_HOME" value="./logs/${appName}" />
可以为不同的Spring Profile配置不同的日志策略:
<springProfile name="dev">
<logger name="com.example.demo" level="DEBUG" />
springProfile>
<springProfile name="prod">
<logger name="com.example.demo" level="INFO" />
<root level="WARN">
<appender-ref ref="FILE" />
root>
springProfile>
TimeBasedRollingPolicy: 基于时间的滚动策略
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/application.%d{yyyy-MM-dd}.logfileNamePattern>
<maxHistory>30maxHistory>
rollingPolicy>
SizeAndTimeBasedRollingPolicy: 基于时间和大小的滚动策略
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/application.%d{yyyy-MM-dd}.%i.logfileNamePattern>
<maxFileSize>10MBmaxFileSize>
<maxHistory>30maxHistory>
<totalSizeCap>1GBtotalSizeCap>
rollingPolicy>
LevelFilter: 按级别过滤
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERRORlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
appender>
ThresholdFilter: 阈值过滤
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARNlevel>
filter>
appender>
使用AsyncAppender可以提高日志性能:
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>512queueSize>
<discardingThreshold>0discardingThreshold>
<includeCallerData>falseincludeCallerData>
<appender-ref ref="FILE" />
appender>
参数说明:
queueSize
: 队列大小(默认256)discardingThreshold
: 当队列剩余容量小于此值时,丢弃TRACE、DEBUG和INFO级别的日志(默认队列大小的20%)includeCallerData
: 是否包含调用者数据(影响性能)最佳实践:
MDC可以用于在日志中添加上下文信息:
import org.slf4j.MDC;
public class UserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
public void login(String userId) {
MDC.put("userId", userId);
logger.info("User logged in");
MDC.remove("userId");
}
}
在配置文件中使用:
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{userId}] %-5level %logger{36} - %msg%npattern>
高级用法:
<pattern>%d{yyyy-MM-dd} [%thread] %mdc{userId:-} %-5level %logger{36} - %msg%npattern>
使用
条件语句(需要Janino库):
<if condition='property("env").equals("prod")'>
<then>
<root level="WARN">
<appender-ref ref="FILE" />
root>
then>
<else>
<root level="DEBUG">
<appender-ref ref="CONSOLE" />
root>
else>
if>
合理使用日志级别:
日志内容规范:
性能考虑:
问题1:日志文件不滚动
fileNamePattern
中的日期模式解决方案:
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/app-%d{yyyy-MM-dd}.%i.logfileNamePattern>
<maxFileSize>50MBmaxFileSize>
<maxHistory>30maxHistory>
rollingPolicy>
问题2:日志文件太大
maxFileSize
和maxHistory
totalSizeCap
限制总大小问题3:日志输出不全
问题4:日志输出乱码
解决方案:
<encoder>
<pattern>%msg%npattern>
<charset>UTF-8charset>
encoder>
<configuration>
<property name="LOG_HOME" value="/var/logs/myapp" />
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}pattern>
encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARNlevel>
filter>
appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/application.logfile>
<encoder>
<pattern>${LOG_PATTERN}pattern>
encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/application.%d{yyyy-MM-dd}.%i.logfileNamePattern>
<maxFileSize>50MBmaxFileSize>
<maxHistory>30maxHistory>
<totalSizeCap>5GBtotalSizeCap>
rollingPolicy>
appender>
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/error.logfile>
<encoder>
<pattern>${LOG_PATTERN}pattern>
encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERRORlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.logfileNamePattern>
<maxHistory>90maxHistory>
rollingPolicy>
appender>
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>1024queueSize>
<discardingThreshold>0discardingThreshold>
<appender-ref ref="FILE" />
appender>
<logger name="com.example" level="INFO" />
<logger name="org.springframework" level="WARN" />
<logger name="org.hibernate.SQL" level="DEBUG" additivity="false">
<appender-ref ref="FILE" />
logger>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="ASYNC_FILE" />
<appender-ref ref="ERROR_FILE" />
root>
configuration>
<configuration>
<springProperty scope="context" name="appName" source="spring.application.name" defaultValue="myApp"/>
<property name="LOG_HOME" value="/var/log/${appName}" />
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>512queueSize>
<discardingThreshold>0discardingThreshold>
<appender-ref ref="FILE" />
appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/application.logfile>
<encoder>
<pattern>${LOG_PATTERN}pattern>
encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/application.%d{yyyy-MM-dd}.%i.logfileNamePattern>
<maxFileSize>50MBmaxFileSize>
<maxHistory>30maxHistory>
<totalSizeCap>5GBtotalSizeCap>
rollingPolicy>
appender>
<appender name="ASYNC_ERROR_FILE" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>512queueSize>
<discardingThreshold>0discardingThreshold>
<appender-ref ref="ERROR_FILE" />
appender>
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/error.logfile>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERRORlevel>
filter>
<encoder>
<pattern>${LOG_PATTERN}pattern>
encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.%i.logfileNamePattern>
<maxFileSize>50MBmaxFileSize>
<maxHistory>60maxHistory>
<totalSizeCap>10GBtotalSizeCap>
rollingPolicy>
appender>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>smtp.example.comsmtpHost>
<smtpPort>587smtpPort>
<username>[email protected]username>
<password>passwordpassword>
<to>[email protected]to>
<from>[email protected]from>
<subject>${appName} - ERROR: %logger{20} - %msubject>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n%expattern>
layout>
<cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker">
<bufferSize>10bufferSize>
cyclicBufferTracker>
appender>
<root level="WARN">
<appender-ref ref="ASYNC_FILE" />
<appender-ref ref="ASYNC_ERROR_FILE" />
<appender-ref ref="EMAIL" />
root>
configuration>
收藏?算了算了,这么优秀的文章你肯定记不住(狗头)。
想了解更多的可以关注微信公众号:“Eric的技术杂货库”,后期会有更多的干货以及资料下载。