Log4j2配置:log4j2.xml
文件示例配置; Log4j2 官方文档
异步日志
log4j2 支持异步日志功能, 可以大幅提高日志记录的性能。AsyncAppender,Async Loggers
AsyncAppender
AsyncAppender 默认使用 java.util.concurrent.ArrayBlockingQueue jdk内部库, 这是一个阻塞队列, 容易受锁争用的影响,在多线程环境时性能会降低变差,建议使用无锁异步记录器来获得最佳性能。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
<Appenders>
<File name="MyFile" fileName="logs/app.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
</File>
<Async name="Async">
<AppenderRef ref="MyFile"/>
</Async>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="Async"/>
</Root>
</Loggers>
</Configuration>
Async Loggers
LMAX Disruptor技术。异步记录器在内部使用Disruptor,一个无锁的线程间通信库,而不是队列,从而产生更高的吞吐量和更低的延迟,异步记录器的延迟始终低于同步记录器甚至基于队列的 AsyncAppender。Disruptor技术支持全异步,混合异步, 性能优于AsyncAppender。
如果记录的日志来自于业务逻辑, 官方建议使用同步记录日志消息,是因为 Async Loggers 在记录日志过程中发生问题并抛出异常,存在不容易通知到应用程序的情况。
使用 Async Loggers 异步记录日志,需要添加 disruptor 依赖,Log4j-2.9及更高版本在类路径上需要 disruptor-3.3.4.jar 或更高版本。 在Log4j-2.9之前,需要disruptor-3.0.0.jar或更高版本。
默认情况下,异步记录器不会将位置传递给 I/O 线程。 如果您的某个布局或自定义过滤器需要位置信息,则需要在所有相关记录器的配置中设置includeLocation = true
,包括根记录器。
全异步记录日志
需要设置系统环境变量:Don't forget to set system property-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
to make all loggers asynchronous.<?xml version="1.0" encoding="UTF-8"?> <!-- Don't forget to set system property -Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector to make all loggers asynchronous. --> <Configuration status="WARN"> <Appenders> <!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. --> <RandomAccessFile name="RandomAccessFile" fileName="async.log" immediateFlush="false" append="false"> <PatternLayout> <Pattern>%d %p %c{1.} [%t] %m %ex%n</Pattern> </PatternLayout> </RandomAccessFile> </Appenders> <Loggers> <Root level="info" includeLocation="false"> <AppenderRef ref="RandomAccessFile"/> </Root> </Loggers> </Configuration>
当使用 AsyncLoggerContextSelector 使所有记录器异步时,请确保在配置中使用普通的
<root> 和 <logger>
元素。混合异步
混合异步:无需设置系统环境变量 Log4jContextSelector。
使用<asyncRoot>
或<asyncLogger>
配置元素指定需要异步的记录器。配置只能包含一个根记录器(<root>或<asyncRoot>
元素),但是可以组合异步和非异步记录器。例如,包含<asyncLogger>
元素的配置文件还可以包含同步记录器的<root> 和 <logger>
元素。<?xml version="1.0" encoding="UTF-8"?> <!-- No need to set system property "log4j2.contextSelector" to any value when using <asyncLogger> or <asyncRoot>. --> <Configuration status="WARN"> <Appenders> <!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. --> <RandomAccessFile name="RandomAccessFile" fileName="asyncWithLocation.log" immediateFlush="false" append="false"> <PatternLayout> <Pattern>%d %p %class{1.} [%t] %location %m %ex%n</Pattern> </PatternLayout> </RandomAccessFile> </Appenders> <Loggers> <!-- pattern layout actually uses location, so we need to include it --> <AsyncLogger name="com.foo.Bar" level="trace" includeLocation="true"> <AppenderRef ref="RandomAccessFile"/> </AsyncLogger> <Root level="info" includeLocation="true"> <AppenderRef ref="RandomAccessFile"/> </Root> </Loggers> </Configuration>
同步日志
没有配置异步日志,默认就是同步日志。
log4j2.xml 配置示例
<?xml version="1.0" encoding="UTF-8"?>
<!-- Configuration后面的status, 这个用于设置log4j2自身内部的信息输出, 可以不设置, 当设置成trace时,
会看到log4j2内部各种详细输出, 可以设置成OFF(关闭) 或 Error(只输出错误信息), 30s 刷新此配置。
-->
<Configuration status="INFO" monitorInterval="30">
<Properties>
<Property name="log.path.root">logs</Property>
<Property name="log.file">project_name</Property>
<Property name="log.pattern">%d %p [%C{1.}->%M:%L] [%t] %m%n</Property>
</Properties>
<Appenders>
<!-- 输出控制台日志 -->
<Console name="Console" target="SYSTEM_OUT" follow="true">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout>
<charset>UTF-8</charset>
<!-- 输出日志格式 -->
<pattern>${log.pattern}</pattern>
</PatternLayout>
</Console>
<!-- INFO 日志 -->
<RollingFile name="INFO-LOG" fileName="${log.path.root}/info/${log.file}.log"
immediateFlush="false"
filePattern="${log.path.root}/info/${log.file}-%d{yyyy-MM-dd}_%i.log.gz">
<!-- 日志过滤:只记录INFO级别的信息 -->
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${log.pattern}" charset="UTF-8"/>
<!--志日文件滚动策略-->
<Policies>
<!-- 按日期打包:
interval:int类型,日志文件滚动时间间隔,默认是1,单位精度与filePattern中配置的日期格式精度一致。
如上d%{yyyy-MM-dd}是精确到天,默认的interval=1表示每天滚动打包;
d%{yyyy-MM-dd HH:mm}精确到分钟, 设置interval=30,表示每30分钟滚动打包
modulate:boolean类型,默认是false,是否对滚动文件的时间进行调制。
若modulate=true,则滚动时间将以0点为边界进行偏移计算调制时间,
如现在是20分钟,interval=30,下次文件滚动时间是30分钟,再下次是60分钟。
-->
<TimeBasedTriggeringPolicy modulate="true" interval="1"/>
<!-- 按日志文件大小打包,达到20M打包 -->
<!--<SizeBasedTriggeringPolicy size="20 MB"/>-->
</Policies>
<!--指定打包文件个数,默认7个,超过则覆盖之前的-->
<!--<DefaultRolloverStrategy max="20"/>-->
</RollingFile>
<!-- ERROR 日志 -->
<RollingFile name="ERROR-LOG" fileName="${log.path.root}/error/${log.file}.log"
filePattern="${log.path.root}/error/${log.file}-%d{yyyy-MM-dd}_%i.log.gz">
<!--日志过-->
<Filters>
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<PatternLayout pattern="${log.pattern}" charset="UTF-8"/>
<Policies>
<TimeBasedTriggeringPolicy modulate="true" interval="1"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<logger name="jdbc.sqlonly" level="INFO"/>
<logger name="jdbc.sqltiming" level="OFF"/>
<logger name="jdbc.resultsettable" level="INFO"/>
<logger name="jdbc.resultset" level="OFF"/>
<logger name="jdbc.connection" level="OFF"/>
<logger name="jdbc.audit" level="OFF"/>
<Root level="INFO">
<AppenderRef ref="Console"/>
<AppenderRef ref="INFO-LOG"/>
<AppenderRef ref="ERROR-LOG"/>
</Root>
</Loggers>
</Configuration>
链路跟踪 LogId
查看日志时,有时需要查看一个线程的整个日志流来分析整个处理流程,这时可以配置 LogId 唯一标识来标记同一个请求,对该请求进行链路跟踪。
log4j2
Log4j2 提供了 Thread Context(线程上下文映射),线程上下文映射相当于 MDC,线程上下文堆栈相当于 NDC。
尽管它们经常用于诊断问题以外的目的,但在 log4j2 中它们仍然经常被称为 MDC 和 NDC,因为这些缩略语已被广泛熟知。
可以将用户信息放入到线程上下文中,在日志配置中取值,可以作为每个请求的唯一标记,跟踪整个请求链路。
Log4j2 Thread Context 更多详情,可参考 http://logging.apache.org/log4j/2.x/manual/thread-context.html
slf4j
slf4j 提供了提供了个 MDC(映射诊断上下文)。为了唯一标记每个请求,用户将上下文信息放入 MDC 中,即 Mapped Diagnostic Contex
t(映射诊断上下文)的缩写。
如果底层日志系统提供 MDC 功能,那么 SLF4J 的 MDC,即这个类,将委托给底层系统的 MDC。
注意:目前只有两个日志系统,即 log4
j和 logback
,提供 MDC 功能。java.util.logging
不支持 MDC,将使用 {@link BasicMDCAdapter}。
对于其他系统,即 slf4j simple 将使用 slf4jnop,{@link NOPMDCAdapter}。作为 SLF4J 用户, 可以在log4j、logback
或java.util.logging
,但不会将这些系统作为对用户的依赖。
关于 MDC 更多详情,可参考 http://logback.qos.ch/manual/mdc.html 中的 MDC 章节。
示例
在日志输出模式添加LogId,或命名为 TraceId 或 SpanId
<Property name="log.pattern">%d %p [LogId:%X{LogId}] [%C{1.}->%M:%L] [%t] %m%n</Property>
给每个请求创建全局唯一LogId,非常方便根据LogId链跟踪整个请求的处理过程
监听器方式,建议使用此方式import com.clearofchina.satisfaction.utils.UniqueIdUtil; import org.apache.logging.log4j.ThreadContext; import org.slf4j.MDC; import org.springframework.stereotype.Component; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; import javax.servlet.annotation.WebListener; /** * @name: RequestListener * @desc: 给请求增加全局唯一ID给日志输出用 **/ @Component @WebListener public class RequestListener implements ServletRequestListener { private static final String LOG_ID = "LogId"; @Override public void requestInitialized(ServletRequestEvent event) { //给请求增加全局唯一ID给日志输出用 String logId = UniqueIdUtil.millisecondIdStr(); // ThreadContext.put(LOG_ID, logId); MDC.put(LOG_ID, logId); } @Override public void requestDestroyed(ServletRequestEvent sre) { // ThreadContext.remove(LOG_ID); MDC.remove(LOG_ID); } }
也可使用拦截器实现
//拦截器 @Component public class LogIdInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { long logId = System.currentTimeMillis(); ThreadContext.put("LogId",String.valueOf(logId)); return true; } } //WebConfig @Configuration public class WebAppConfig implements WebMvcConfigurer { public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LogIdInterceptor()); } }
输出日志效果
2018-11-09 18:15:08,673 INFO [LogId:1541758508672] [c.s.t.c.ShiroRealm->doGetAuthenticationInfo:34] [http-nio-8080-exec-6] 开始认证用户身份 ..... 2018-11-09 18:15:08,674 INFO [LogId:1541758508672] [n.s.l.l.s.Slf4jSpyLogDelegator->sqlOccurred:228] [http-nio-8080-exec-6] SELECT count(0) FROM user WHERE username = 'user1' 2018-11-09 18:15:08,675 INFO [LogId:1541758508672] [n.s.l.l.s.Slf4jSpyLogDelegator->resultSetCollected:610] [http-nio-8080-exec-6]
参数说明
- %m 输出代码中指定的消息
- %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
- %r 输出自应用启动到输出该log信息耗费的毫秒数
- %c 输出所属的类目,通常就是所在类的全名
- %t 输出产生该日志事件的线程名
- %n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”
- %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 :10:28,921
- %l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数
- %x Used to output the NDC (nested diagnostic context) associated with the thread that generated the logging event
- %X Used to output the MDC (mapped diagnostic context) associated with the thread that generated the logging event for specified key
相关参考
注意:本文归作者所有,未经作者允许,不得转载