一. log4j2简介
log4j2是log4j 1.x 的升级版,参考了logback的一些优秀的设计,并且修复了一些问题,因此带来了一些重大的提升。
- 性能提升, log4j2相较于log4j 1和logback都具有很明显的性能提升,支持异步日志处理。
- 自动重载配置,参考了logback的设计,当然会提供自动刷新参数配置,在生产上可以动态的修改日志的级别而不需要重启应用。
- log4j2不再支持properties文件了,只支持xml,json或是yaml。
- 功能结构
日志框架:slf4j
日志实现:log4j2
桥接包:log4j-slf4j-impl
<!-- 日志实现 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.1</version>
</dependency>
<!-- 日志桥接包 桥接包的版本须对应log4j2的版本 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.1</version>
</dependency>
<!-- 日志框架(门面) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
二. SpringBoot2.x集成Log4j2
spring boot 默认采用lobback作为日志实现框架,需要排除logback的依赖。
1.pom文件添加
<!-- 集成Log4j2日志 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
2.application.properties文件添加
logging.config=classpath:log4j2.xml
3.resources文件夹下编辑log4j2.xml文件
?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<!-- <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/> -->
<PatternLayout pattern=".%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%file:%line]
%-5level %logger{36} - %msg %n"/>
</Console>
<RollingFile name="RollingFile" fileName="logs/app.log"
filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%file:%line] %-5level %logger{35} - %msg %n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="102400KB"/>
</Policies>
<DefaultRolloverStrategy fileIndex="max" max="2"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="STDOUT"/>
<AppenderRef ref="RollingFile"/>
</Root>
</Loggers>
</Configuration>
- Appender
Appender就是一个管道,定义了日志内容的去向(保存位置)。
配置一个或者多个Filter,Filter的过滤机制和Servlet的Filter有些差别,下文会进行说明。
配置Layout来控制日志信息的输出格式。
配置Policies以控制日志何时(When)进行滚动。
配置Strategy以控制日志如何(How)进行滚动。 - Logger
Logger就是一个路由,指定类、包中的日志信息流向哪个管道,以及控制他们的流量(日志级别)
三.异步日志
log4j2最大的特点就是异步日志,增加消息队列作为缓存,Log4j2提供了两种实现日志的方式,一个是通过AsyncAppender,一个是通过AsyncLogger,分别对应前面我们说的Appender组件和Logger组件。
AsyncAppender方式
默认使用 java.util.concurrent.ArrayBlockingQueue实现。 在多线程的环境下阻塞队列容易受到锁争用的影响,这可能会对性能产生影响。
<?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>
AsyncLogger方式
官方推荐的异步方式,速度更快,分为全局异步和混合异步,采用disruptor非阻塞消息队列。
- 全局异步
所有的日志都异步的记录,在系统初始化的时候,增加全局参数配置:
System.setProperty("log4j2.contextSelector, "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");
也可以加载JVM启动参数里设置:
java -Dog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
- 混合异步
在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活。
<?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>
异步日志注意事项
- 不要同时使用AsyncAppender和AsyncLogger。
- 不要在开启了全局同步的情况下,仍然使用AsyncAppender和AsyncLogger。
- 如果不是确实需要,不要打印location信息,比如pattern模式里的%C or $class, %F or %file, %l or %location, %L or %line, %M or %method, 等,对于性能来说是个极大的损耗。
- 不管是同步异步,都设置immediateFlush为false,这会对性能提升有很大帮助。
四.注解@Slf4j的使用
如果不想每次都写private final Logger logger = LoggerFactory.getLogger(当前类名.class); 可以用注解@Slf4j。
1.eclipse安装lombok插件
2.在pom文件加入lombok的依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
</dependency>
3.类上面添加@Sl4j注解,然后使用log打印日志。
@Slf4j
class LogTest {
@Test
void testLog() {
String testInfo = "Free flying flowers are like dreams";
log.info("The test info is :{}", testInfo);
}
}
五.日志整合
日志接口
可以统一到slf4,引用slf4j方式
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.13</version>
</dependency>
日志实现
slf4j只是一个日志外壳,需要你加入slf4j-jdk14.jar, slf4j-log4j12.jar或logback.jar,将日志调用转发到实际的日志框架。在classpath中有哪个jar包,slf4j就会选择哪个实现。如果错误的同时存在多个jar包,用哪个那就看运气了。
用Log4j2的实现方式:
<!--核心log4j2jar包-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.4.1</version>
</dependency>
<!--用于与slf4j保持桥接-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.4.1</version>
</dependency>
<!--web工程需要包含log4j-web,非web工程不需要-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>2.4.1</version>
<scope>runtime</scope>
</dependency>
<!--需要使用log4j2的AsyncLogger需要包含disruptor-->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.2.0</version>
</dependency>
spring-boot-starter-log4j2已经实现集成以上各包,引入即可。
日志转换
有些第三方的工具包,已经直接使用了log4j, common-logging 或 java.util.logging。如果我们最后决定使用slf4j做最终输出,则需要放一个jcl104-over-slf4j.jar和 jul-to-slf4j.jar来假装成common-logging和java.util.logging的api,将日志请求转发给slf4j,slf4j再转发给对应日志实现,此时还需要保证,classpath里没有正牌的common-logging.jar。
在spring boot中整合
springboot 默认Slf4j 日志门面,采用日志是Slf4j + logback框架 来进行日志输出。有时候其他框架不一定适用springboot 日志 , 不同的框架底层使用的日志框架也是不尽相同。
- 将系统中其他框架排除出去
例如: spring 默认日志框架是 common-logging 日志框架
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
- 用中间包将日志进行替换
springboot默认已经导入转换jar依赖 - 导入slf4j其他的实现即可
用log4j2实现如下:
<!-- 集成Log4j2日志 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
其它方式整合
项目中的其它jar在启动的时候自己加载了其默认的配置文件,导致项目中的配置文件无法加载。
例如加入nacos包项目无法输出日志的问题
通过在系统变量中设置日志文件位置
System.setProperty("nacos.logging.config", "classpath:log4j2-spring.xml");