Java日志框架学习--LogBack和Log4j2--下
- Logback
- Logback中的组件
- Logback配置文件
- 日志输出格式
- 使用演示
- 配置文件
- 输出到控制台
- 输出到控制台和文件
- 输出到控制台,文件和html
- 日志拆分
- 过滤器
- 异步日志
- 自定义Logger
- Log4j
- Log4j2简介
- Log4j2特征
- 应用演示
- 配置文件
- slf4j加log4j2
- 日志输出到文件
- 日志拆分
- 异步日志
- AsyncAppender方式
- AsyncLogger方式
- AsyncAppender测试
- AsyncLogger测试
- 混合异步输出日志
- 异步性能比较
- SpringBoot整合日志框架
- spring配置文件配置日志
- spring配置文件配置日志输出到文件
- spring引入logback的配置文件
- Spring整合log4j2
Logback
Logback是由log4j创始人设计的又一个开源日志组件。
Logback当前分成三个模块:logback-core,logback- classic和logback-access。
- logback-core是其它两个模块的基础模块。
- logback-classic是log4j的一个改良版本。此外logback-classic完整实现SLF4J API。使你可以很方便地更换成其它日志系统如log4j或JDK14 Logging
- logback-access访问模块与Servlet容器集成提供通过Http来访问日志的功能
Logback中的组件
- Logger: 日志的记录器,主要用于存放日志对象,也可以定义日志类型、级别。
- Appender:用于指定日志输出的目的地,目的地可以是控制台、文件、数据库等等。
- Layout: 负责把事件转换成字符串,格式化的日志信息的输出。
- 在Logback中Layout对象被封装在encoder中。
- 也就是说我们未来使用的encoder其实就是Layout
Logback配置文件
Logback提供了3种配置文件
- logback.groovy
- logback-test.xml
- logback.xml
如果都不存在则采用默认的配置
日志输出格式
日志输出格式:
- %-10level 级别 案例为设置10个字符,左对齐
- %d{yyyy-MM-dd HH:mm:ss.SSS} 日期
- %c 当前类全限定名
- %M 当前执行日志的方法
- %L 行号
- %thread 线程名称
- %m或者%msg 信息
- %n 换行
使用演示
代码语言:javascript复制 <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
配置文件
输出到控制台
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--
配置文件通用属性
可以在当前配置文件中通过${name}的形式,取得value值
我们在此指定通用的日志输出格式
日志输出格式:
%-10level 级别 案例为设置10个字符,左对齐
%d{yyyy-MM-dd HH:mm:ss.SSS} 日期
%c 当前类全限定名
%M 当前执行日志的方法
%L 行号
%thread 线程名称
%m或者%msg 信息
%n 换行
-->
<property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %c %M %L %thread %m%n"></property>
<!--
配置控制台输出的appender
-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--
表示对于日志输出目标的配置
默认: system.out 表示以黑色字体输出日志
system.err 表示以红色字体输出日志
-->
<target>
System.err
</target>
<!--
配置日志输出格式
-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 格式引用通用属性配置 -->
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!--
日志记录器
配置Root Logger
level: 日志级别
-->
<root level="all">
<appender-ref ref="console"/>
</root>
</configuration>
输出到控制台和文件
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--
配置文件通用属性
可以在当前配置文件中通过${name}的形式,取得value值
我们在此指定通用的日志输出格式
日志输出格式:
%-10level 级别 案例为设置10个字符,左对齐
%d{yyyy-MM-dd HH:mm:ss.SSS} 日期
%c 当前类全限定名
%M 当前执行日志的方法
%L 行号
%thread 线程名称
%m或者%msg 信息
%n 换行
-->
<property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %c %M %L %thread %m%n"></property>
<!-- 配置文件的输出路径 -->
<property name="dir" value="logback.log"></property>
<!-- 配置文件输出的appender -->
<appender name="file" class="ch.qos.logback.core.FileAppender">
<!-- 引入文件位置 -->
<file>${dir}</file>
<!--
配置日志输出格式
-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 格式引用通用属性配置 -->
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!--
配置控制台输出的appender
-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--
表示对于日志输出目标的配置
默认: system.out 表示以黑色字体输出日志
system.err 表示以红色字体输出日志
-->
<target>
System.err
</target>
<!--
配置日志输出格式
-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 格式引用通用属性配置 -->
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!--
日志记录器
配置Root Logger
level: 日志级别
-->
<root level="all">
<appender-ref ref="file"/>
<appender-ref ref="console"/>
</root>
</configuration>
因为符合OGNL规范,因此配置文件中可以配置的属性,大多可以通过翻看对应的类源码,通过set方法或者属性名推测出来
输出到控制台,文件和html
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--
配置文件通用属性
可以在当前配置文件中通过${name}的形式,取得value值
我们在此指定通用的日志输出格式
日志输出格式:
%-10level 级别 案例为设置10个字符,左对齐
%d{yyyy-MM-dd HH:mm:ss.SSS} 日期
%c 当前类全限定名
%M 当前执行日志的方法
%L 行号
%thread 线程名称
%m或者%msg 信息
%n 换行
-->
<property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %c %M %L %thread %m%n"></property>
<!-- 配置文件的输出路径 -->
<property name="fileDir" value="logback.log"></property>
<!-- 配置输出为html形式的appender -->
<appender name="html" class="ch.qos.logback.core.FileAppender">
<!-- 输出的文件位置 -->
<file>logback.html</file>
<!-- 设置layout为输出html形式的 -->
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>
${pattern}
</pattern>
</layout>
</appender>
<!-- 配置文件输出的appender -->
<appender name="file" class="ch.qos.logback.core.FileAppender">
<!-- 引入文件位置 -->
<file>${fileDir}</file>
<!--
配置日志输出格式
-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 格式引用通用属性配置 -->
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!--
配置控制台输出的appender
-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--
表示对于日志输出目标的配置
默认: system.out 表示以黑色字体输出日志
system.err 表示以红色字体输出日志
-->
<target>
System.err
</target>
<!--
配置日志输出格式
-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 格式引用通用属性配置 -->
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!--
日志记录器
配置Root Logger
level: 日志级别
-->
<root level="all">
<appender-ref ref="file"/>
<appender-ref ref="console"/>
<appender-ref ref="html"/>
</root>
</configuration>
html展示效果如果:
日志拆分
日志拆分使用的是RollingFileAppender,它继承了FileAppender,因此父类中能配置的属性,这里都可以配置,我们只需要关注当前类新增的查日志拆分策略属性即可。
在当前RollingFileAppender中也为我们提供了两个设置日志拆分策略的set方法:
先看一波如何配置,再来分析源码:
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--
配置文件通用属性
可以在当前配置文件中通过${name}的形式,取得value值
我们在此指定通用的日志输出格式
日志输出格式:
%-10level 级别 案例为设置10个字符,左对齐
%d{yyyy-MM-dd HH:mm:ss.SSS} 日期
%c 当前类全限定名
%M 当前执行日志的方法
%L 行号
%thread 线程名称
%m或者%msg 信息
%n 换行
-->
<property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %c %M %L %thread %m%n"></property>
<!-- 配置文件的输出路径 -->
<property name="fileDir" value="logback.log"></property>
<!-- 配置输出为html形式的appender -->
<appender name="html" class="ch.qos.logback.core.FileAppender">
<!-- 输出的文件位置 -->
<file>logback.html</file>
<!-- 设置layout为输出html形式的 -->
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>
${pattern}
</pattern>
</layout>
</appender>
<!-- 配置文件输出的appender -->
<appender name="file" class="ch.qos.logback.core.FileAppender">
<!-- 引入文件位置 -->
<file>${fileDir}</file>
<!--
配置日志输出格式
-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 格式引用通用属性配置 -->
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!--
配置控制台输出的appender
-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--
表示对于日志输出目标的配置
默认: system.out 表示以黑色字体输出日志
system.err 表示以红色字体输出日志
-->
<target>
System.err
</target>
<!--
配置日志输出格式
-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 格式引用通用属性配置 -->
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!-- 配置文件的appender,可拆分可归档-归档是压缩的意思 -->
<appender name="roll" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 设置输出格式 -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${pattern}</pattern>
</layout>
<!-- 设置未归档的日志文件输出位置 -->
<file>roll_logback.log</file>
<!-- 指定拆分规则 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 按照时间和压缩格式声明文件名,压缩格式gz -->
<fileNamePattern>roll.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
<!-- 按照文件大小来进行拆分 -->
<maxFileSize>1KB</maxFileSize>
</rollingPolicy>
</appender>
<!--
日志记录器
配置Root Logger
level: 日志级别
-->
<root level="all">
<appender-ref ref="roll"/>
</root>
</configuration>
下面分析一波,这里源码流程从subAppend函数讲起,因为每一条日志的输出,都需要交给至少一个appender,完成日志的输出,而交给appender后,一定会来到appender的subAppend这里,各位可以自行debug源码流程
这里拿RollingFileAppender进行讲解:
代码语言:javascript复制 @Override
protected void subAppend(E event) {
//日志真正写入前,现需要判断是否应该触发当前日志文件的归档行为
//因为可能当前写入数据超过了日志文件大小的限制,那么当前日志就应该归档,再创建一个新的日志文件
// The roll-over check must precede actual writing. This is the
// only correct behavior for time driven triggers.
// We need to synchronize on triggeringPolicy so that only one rollover
// occurs at a time
//加锁确保一次只有一个线程进行日志roll操作
synchronized (triggeringPolicy) {
//triggeringPolicy负责判断是否需要进行roll over
if (triggeringPolicy.isTriggeringEvent(currentlyActiveFile, event)) {
rollover();
}
}
//调用父类方法真正执行写入操作
super.subAppend(event);
}
具体回滚策略是如何执行的,这里不再进行分析
过滤器
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %c %M %L %thread %m%n"></property>
<!-- 配置文件的输出路径 -->
<property name="fileDir" value="logback.log"></property>
<!--
配置控制台输出的appender
-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--
表示对于日志输出目标的配置
默认: system.out 表示以黑色字体输出日志
system.err 表示以红色字体输出日志
-->
<target>
System.err
</target>
<!--
配置日志输出格式
-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 格式引用通用属性配置 -->
<pattern>${pattern}</pattern>
</encoder>
<!-- 配置过滤器 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 设置日志的输出级别 -->
<level>ERROR</level>
<!-- 高于Level中设置的日志级别,则打印日志 -->
<onMatch>ACCEPT</onMatch>
<!-- 低于Level中设置的日志级别,则跳过 -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--
日志记录器
配置Root Logger
level: 日志级别
-->
<root level="all">
<appender-ref ref="console"/>
</root>
</configuration>
通过过滤器,我们可以设置某个appender的日志过滤输出
过滤器链在何时会执行呢?
异步日志
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %c %M %L %thread %m%n"></property>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<target>
System.err
</target>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!-- 异步日志的配置 -->
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="console"/>
</appender>
<root level="all">
<appender-ref ref="async"/>
</root>
</configuration>
异步日志测试:
异步日志实现源码来看看吧:
先说明一下,logback采用生产者消费者模型实现的日志异步输出
默认如果丢弃的话,会丢弃掉info以下的日志输出
自定义Logger
代码语言:javascript复制 <logger name="com.dhy" level="info" additivity="false">
<appender-ref ref="async"/>
</logger>
name是包路径,包路径的设置是具有父子关系的
level是日志等级
additivity表示是否继承父类logger的配置
additivity为true,表示会继承父类的appenders,为false表示不继承父类的appenders
Log4j
Log4j2简介
Apache Log4j 2是对Log4j的升级,它比其前身Log4j 1.x提供了重大改进,并提供了Logback中可用的许多改进,同时修复了Logback架构中的一些问题。被誉为是目前最优秀的Java日志框架
Log4j2特征
- 性能提升
Log4j2包含基于LMAX Disruptor库的下一代异步记录器。在多线程场景中,异步记录器的吞吐量比Log4j 1.x和Logback高18倍,延迟低。
- 自动重新加载配置
与Logback一样,Log4j2可以在修改时自动重新加载其配置。与Logback不同,它会在重新配置发生时不会丢失日志事件。
- 高级过滤
与Logback一样,Log4j2支持基于Log事件中的上下文数据,标记,正则表达式和其他组件进行过滤。 此外,过滤器还可以与记录器关联。与Logback不同,Log4j2可以在任何这些情况下使用通用的Filter类。
- 插件架构
Log4j使用插件模式配置组件。因此,您无需编写代码来创建和配置Appender,Layout,Pattern Converter等。在配置了的情况下,Log4j自动识别插件并使用它们。
- 无垃圾机制
在稳态日志记录期间,Log4j2 在独立应用程序中是无垃圾的,在Web应用程序中是低垃圾。这减少了垃圾收集器的压力,并且可以提供更好的响应性能。
目前市面上最主流的日志门面就是SLF4J,虽然Log4j2 也是日志门面,因为它的日志实现功能非常强大,性能优越。所以我们一般情况下还是将 Log4j2 看作是日志的实现
SLF4j Log4j2 的组合,是市场上最强大的日志功能实现方式,绝对是未来的主流趋势。
应用演示
引入依赖:
代码语言:javascript复制<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
可以设置jdk编译版本:
代码语言:javascript复制<build>
<plugins>
<!-- 设置编译版本为1.8 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
使用演示:
代码语言:javascript复制import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.testng.annotations.Test;
public class LogTest {
@Test
public void Test(){
Logger logger = LogManager.getLogger(LogTest.class.getName());
log(logger);
}
private void log(Logger logger) {
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
}
没有提供配置文件,会提供一个日志的默认配置,但是会给出一个警告
配置文件
log4j2的配置文件类似logback,会默认去加载类路径下的log4j2.xml
因为log4j2参考了logback的设计思路,但是未来减少借鉴量,就将原本logback配置文件中小写的标签值,都转为了大写
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8" ?>
<configuration status="debug" monitorInterval="5">
<!-- 配置appender -->
<Appenders>
<!-- 默认是system.out,这里我们设置为system.err,这一点和logback一样 -->
<Console name="console" target="SYSTEM_ERR"></Console>
</Appenders>
<!-- 配置logger -->
<Loggers>
<!-- 配置rootLogger -->
<Root level="trace">
<AppenderRef ref="console"/>
</Root>
</Loggers>
</configuration>
默认输出格式为%m%n
slf4j加log4j2
我们只需要导入一个依赖就够了:
代码语言:javascript复制 <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.17.2</version>
</dependency>
这样一个适配器依赖模块以及将slf4j门面依赖,log4j门面和log4k2日志实现依赖全部导入了
使用还是正常使用slf4j的日志门面api即可
日志输出到文件
这里的使用和之前logback的配置类似
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8" ?>
<configuration monitorInterval="5">
<!-- 公共属性 -->
<properties>
<property name="logDir">log4j2.log</property>
</properties>
<!-- 配置appender -->
<Appenders>
<!-- 默认是system.out,这里我们设置为system.err,这一点和logback一样 -->
<Console name="console" target="SYSTEM_ERR"></Console>
<File name="file" fileName="${logDir}">
<!-- 输出格式 -->
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n"></PatternLayout>
</File>
</Appenders>
<!-- 配置logger -->
<Loggers>
<!-- 配置rootLogger -->
<Root level="trace">
<AppenderRef ref="console"/>
<AppenderRef ref="file"/>
</Root>
</Loggers>
</configuration>
文件默认也是追加写入
日志拆分
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8" ?>
<configuration monitorInterval="5">
<!-- 公共属性 -->
<properties>
<property name="logDir">log4j2.log</property>
</properties>
<!-- 配置appender -->
<Appenders>
<!-- 默认是system.out,这里我们设置为system.err,这一点和logback一样 -->
<Console name="console" target="SYSTEM_ERR"></Console>
<File name="file" fileName="${logDir}">
<!-- 输出格式 -->
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n"></PatternLayout>
</File>
<RollingFile name="rollingFile" fileName="rollLog.log"
filePattern="${date:yyyy-MM-dd}/roll-%d{yyyy-MM-dd-HH-mm}-%i.log">
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n"></PatternLayout>
<Policies>
<!-- 在系统启动时,触发拆分规则,产生一个日志文件 -->
<OnStartupTriggeringPolicy/>
<!-- 按照文件的大小进行拆分 -->
<SizeBasedTriggeringPolicy size="10KB"/>
<!-- 按照时间节点进行拆分,拆分规则就是filePattern -->
<TimeBasedTriggeringPolicy/>
</Policies>
<!--在同一目录下,文件的个数限制,如果超出了设置的数值,则根据时间进行覆盖,新的覆盖旧的规则 -->
<DefaultRolloverStrategy max="30"/>
</RollingFile>
</Appenders>
<!-- 配置logger -->
<Loggers>
<!-- 配置rootLogger -->
<Root level="trace">
<AppenderRef ref="rollingFile"/>
</Root>
</Loggers>
</configuration>
关于log4j2的配置文件解析源码,大家可以参考AbstractConfiguration
异步日志
异步日志是log4j2最大的特色,其性能的提升主要也是从异步日志中受益。
Log4j2提供了两种实现日志的方式,一个是通过AsyncAppender,一个是通过AsyncLogger,分别对应前面我们说的Appender组件和Logger组件。
注意这是两种不同的实现方式,在设计和源码上都是不同的体现。
AsyncAppender方式
是通过引用别的Appender来实现的,当有日志事件到达时,会开启另外一个线程来处理它们。
需要注意的是,如果在Appender的时候出现异常,对应用来说是无法感知的。
AsyncAppender应该在它引用的Appender之后配置,默认使用 java.util.concurrent.ArrayBlockingQueue实现而不需要其它外部的类库。
当使用此Appender的时候,在多线程的环境下需要注意,阻塞队列容易受到锁争用的影响,这可能会对性能产生影响。
这时候,我们应该考虑使用无锁的异步记录器(AsyncLogger)。
AsyncLogger方式
AsyncLogger才是log4j2实现异步最重要的功能体现,也是官方推荐的异步方式。
它可以使得调用Logger.log返回的更快。你可以有两种选择:全局异步和混合异步。
全局异步:所有的日志都异步的记录,在配置文件上不用做任何改动,只需要在jvm启动的时候增加一个参数即可实现。
混合异步:你可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活。虽然Log4j2提供以一套异常处理机制,可以覆盖大部分的状态,但是还是会有一小部分的特殊情况是无法完全处理的,比如我们如果是记录审计日志(特殊情况之一),那么官方就推荐使用同步日志的方式,而对于其他的一些仅仅是记录一个程序日志的地方,使用异步日志将大幅提升性能,减少对应用本身的影响。
混合异步的方式需要通过修改配置文件来实现,使用AsyncLogger标记配置。
AsyncAppender测试
- 引入异步日志依赖
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.7</version>
</dependency>
- 修改配置
<?xml version="1.0" encoding="UTF-8" ?>
<configuration monitorInterval="5">
<!-- 公共属性 -->
<properties>
<property name="logDir">log4j2.log</property>
</properties>
<!-- 配置appender -->
<Appenders>
<!-- 默认是system.out,这里我们设置为system.err,这一点和logback一样 -->
<Console name="console" target="SYSTEM_ERR"></Console>
<File name="file" fileName="${logDir}">
<!-- 输出格式 -->
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n"></PatternLayout>
</File>
<Async name="async">
<AppenderRef ref="console"></AppenderRef>
</Async>
</Appenders>
<!-- 配置logger -->
<Loggers>
<!-- 配置rootLogger -->
<Root level="trace">
<AppenderRef ref="async"/>
</Root>
</Loggers>
</configuration>
- 测试异步效果
public class LogTest {
@Test
public void Test(){
Logger logger = LoggerFactory.getLogger(LogTest.class.getName());
log(logger);
System.out.println("1111");
System.out.println("3333");
System.out.println("2222");
}
private void log(Logger logger) {
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
}
}
AsyncLogger测试
代码语言:javascript复制Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
配置文件去掉async的配置
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8" ?>
<configuration monitorInterval="5">
<!-- 配置appender -->
<Appenders>
<!-- 默认是system.out,这里我们设置为system.err,这一点和logback一样 -->
<Console name="console" target="SYSTEM_ERR"></Console>
<File name="file" fileName="${logDir}">
<!-- 输出格式 -->
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n"></PatternLayout>
</File>
</Appenders>
<!-- 配置logger -->
<Loggers>
<!-- 配置rootLogger -->
<Root level="trace">
<AppenderRef ref="console"/>
</Root>
</Loggers>
</configuration>
测试
混合异步输出日志
代码语言:javascript复制<!-- 配置异步Logger -->
<AsyncLogger name="com" level="info"
includeLocation="false" additivity="false">
<AppenderRef ref="console"/>
</AsyncLogger>
异步性能比较
SpringBoot整合日志框架
springboot提供对日志的整合模块主要是spring-boot-starter-logging模块
首先引入了logback-classic,可以看出springboot默认使用的日志框架为logback
下面还有两个:一个是log4j-to-slf4j:2.14.1,这个是桥接器模块,因为该模块里面只有log4j2的门面api,这里还是通过api重定向,将log4j2的api使用,重定向到slf4j,然后slf4j底层再使用logback作为底层日志框架实现
jul-to-slf4j类似原理
下面来验证两点,一点是springboot默认使用的是logback作为日志选型,另一点是我们使用log4j2和jul的日志api,最终底层调用的还是logback
代码语言:javascript复制package helper.com.logTest;
import org.apache.logging.log4j.LogManager;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
/**
* @author 大忽悠
* @create 2022/5/14 10:28
*/
public class Log4jTest {
@Test
public void testLogBack() {
System.err.println("------------testLogBack---------------");
Logger logger = LoggerFactory.getLogger(Log4jTest.class);
logger.error("error");
logger.warn("warn");
logger.info("info");
logger.debug("debug");
logger.trace("trace");
System.err.println("-----------testLog4j2Bridge----------------");
org.apache.logging.log4j.Logger log = LogManager.getLogger(Log4jTest.class);
log.error("error");
log.warn("warn");
log.info("info");
log.debug("debug");
log.trace("trace");
System.err.println("-----------testJULBridge----------------");
java.util.logging.Logger julLog= java.util.logging.Logger.getLogger(Log4jTest.class.getName());
julLog.addHandler(new SLF4JBridgeHandler());
julLog.severe("severe");
julLog.warning("warning");
julLog.info("info");
julLog.fine("fine");
julLog.finer("finer");
julLog.finest("finest");
}
}
JUL的桥接需要手动添加handler完成,该handler的publish方法源码如下:
代码语言:javascript复制 public void publish(LogRecord record) {
if (record != null) {
org.slf4j.Logger slf4jLogger = this.getSLF4JLogger(record);
String message = record.getMessage();
if (message == null) {
message = "";
}
if (slf4jLogger instanceof LocationAwareLogger) {
this.callLocationAwareLogger((LocationAwareLogger)slf4jLogger, record);
} else {
this.callPlainSLF4JLogger(slf4jLogger, record);
}
}
}
spring配置文件配置日志
代码语言:javascript复制logging:
level:
helper:
com: trace
pattern:
console: "%d{yyyy年-MM月-dd日} [%-5level] %m%n"
spring配置文件配置日志输出到文件
代码语言:javascript复制logging:
level:
helper:
com: trace
pattern:
console: "%d{yyyy年-MM月-dd日} [%-5level] %m%n"
file:
path: test
指定的是存放日志文件夹的名字,日志会生成在该文件夹下面,名字为spring.log
spring引入logback的配置文件
放在类路径下,名字就叫logback.xml就可以了
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<property name="pattern" value="大忽悠日志: [%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %c %M %L %thread %m%n"></property>
<!-- 配置文件的输出路径 -->
<property name="fileDir" value="logback.log"></property>
<!--
配置控制台输出的appender
-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!--
表示对于日志输出目标的配置
默认: system.out 表示以黑色字体输出日志
system.err 表示以红色字体输出日志
-->
<target>
System.err
</target>
<!--
配置日志输出格式
-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 格式引用通用属性配置 -->
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!-- 配置文件的appender,可拆分可归档-归档是压缩的意思 -->
<appender name="roll" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 设置输出格式 -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${pattern}</pattern>
</layout>
<!-- 设置未归档的日志文件输出位置 -->
<file>roll_logback.log</file>
<!-- 指定拆分规则 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 按照时间和压缩格式声明文件名,压缩格式gz -->
<fileNamePattern>roll.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
<!-- 按照文件大小来进行拆分 -->
<maxFileSize>1KB</maxFileSize>
</rollingPolicy>
</appender>
<!--
日志记录器
配置Root Logger
level: 日志级别
-->
<root level="info">
<appender-ref ref="roll"/>
<appender-ref ref="console"/>
</root>
</configuration>
Spring整合log4j2
- 移除spring-boot-starter-logging模块依赖
- 添加之前我们使用的log4j2的依赖即可
<!-- spring web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
下面引入log4j2的配置文件进行测试即可:
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8" ?>
<configuration status="debug" monitorInterval="5">
<!-- 配置appender -->
<Appenders>
<!-- 默认是system.out,这里我们设置为system.err,这一点和logback一样 -->
<Console name="console" target="SYSTEM_ERR"></Console>
</Appenders>
<!-- 配置logger -->
<Loggers>
<!-- 配置rootLogger -->
<Root level="info">
<AppenderRef ref="console"/>
</Root>
</Loggers>
</configuration>