Java日志框架学习--LogBack和Log4j2--下

x33g5p2x  于2022-05-18 转载在 Java  
字(24.9k)|赞(0)|评价(0)|浏览(503)

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 换行

使用演示

  1. <dependency>
  2. <groupId>org.slf4j</groupId>
  3. <artifactId>slf4j-api</artifactId>
  4. <version>1.7.36</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>ch.qos.logback</groupId>
  8. <artifactId>logback-classic</artifactId>
  9. <version>1.2.11</version>
  10. </dependency>

配置文件

输出到控制台

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <configuration>
  3. <!--
  4. 配置文件通用属性
  5. 可以在当前配置文件中通过${name}的形式,取得value值
  6. 我们在此指定通用的日志输出格式
  7. 日志输出格式:
  8. %-10level 级别 案例为设置10个字符,左对齐
  9. %d{yyyy-MM-dd HH:mm:ss.SSS} 日期
  10. %c 当前类全限定名
  11. %M 当前执行日志的方法
  12. %L 行号
  13. %thread 线程名称
  14. %m或者%msg 信息
  15. %n 换行
  16. -->
  17. <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %c %M %L %thread %m%n"></property>
  18. <!--
  19. 配置控制台输出的appender
  20. -->
  21. <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  22. <!--
  23. 表示对于日志输出目标的配置
  24. 默认: system.out 表示以黑色字体输出日志
  25. system.err 表示以红色字体输出日志
  26. -->
  27. <target>
  28. System.err
  29. </target>
  30. <!--
  31. 配置日志输出格式
  32. -->
  33. <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  34. <!-- 格式引用通用属性配置 -->
  35. <pattern>${pattern}</pattern>
  36. </encoder>
  37. </appender>
  38. <!--
  39. 日志记录器
  40. 配置Root Logger
  41. level: 日志级别
  42. -->
  43. <root level="all">
  44. <appender-ref ref="console"/>
  45. </root>
  46. </configuration>

输出到控制台和文件

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <configuration>
  3. <!--
  4. 配置文件通用属性
  5. 可以在当前配置文件中通过${name}的形式,取得value值
  6. 我们在此指定通用的日志输出格式
  7. 日志输出格式:
  8. %-10level 级别 案例为设置10个字符,左对齐
  9. %d{yyyy-MM-dd HH:mm:ss.SSS} 日期
  10. %c 当前类全限定名
  11. %M 当前执行日志的方法
  12. %L 行号
  13. %thread 线程名称
  14. %m或者%msg 信息
  15. %n 换行
  16. -->
  17. <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %c %M %L %thread %m%n"></property>
  18. <!-- 配置文件的输出路径 -->
  19. <property name="dir" value="logback.log"></property>
  20. <!-- 配置文件输出的appender -->
  21. <appender name="file" class="ch.qos.logback.core.FileAppender">
  22. <!-- 引入文件位置 -->
  23. <file>${dir}</file>
  24. <!--
  25. 配置日志输出格式
  26. -->
  27. <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  28. <!-- 格式引用通用属性配置 -->
  29. <pattern>${pattern}</pattern>
  30. </encoder>
  31. </appender>
  32. <!--
  33. 配置控制台输出的appender
  34. -->
  35. <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  36. <!--
  37. 表示对于日志输出目标的配置
  38. 默认: system.out 表示以黑色字体输出日志
  39. system.err 表示以红色字体输出日志
  40. -->
  41. <target>
  42. System.err
  43. </target>
  44. <!--
  45. 配置日志输出格式
  46. -->
  47. <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  48. <!-- 格式引用通用属性配置 -->
  49. <pattern>${pattern}</pattern>
  50. </encoder>
  51. </appender>
  52. <!--
  53. 日志记录器
  54. 配置Root Logger
  55. level: 日志级别
  56. -->
  57. <root level="all">
  58. <appender-ref ref="file"/>
  59. <appender-ref ref="console"/>
  60. </root>
  61. </configuration>

因为符合OGNL规范,因此配置文件中可以配置的属性,大多可以通过翻看对应的类源码,通过set方法或者属性名推测出来

输出到控制台,文件和html

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <configuration>
  3. <!--
  4. 配置文件通用属性
  5. 可以在当前配置文件中通过${name}的形式,取得value值
  6. 我们在此指定通用的日志输出格式
  7. 日志输出格式:
  8. %-10level 级别 案例为设置10个字符,左对齐
  9. %d{yyyy-MM-dd HH:mm:ss.SSS} 日期
  10. %c 当前类全限定名
  11. %M 当前执行日志的方法
  12. %L 行号
  13. %thread 线程名称
  14. %m或者%msg 信息
  15. %n 换行
  16. -->
  17. <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %c %M %L %thread %m%n"></property>
  18. <!-- 配置文件的输出路径 -->
  19. <property name="fileDir" value="logback.log"></property>
  20. <!-- 配置输出为html形式的appender -->
  21. <appender name="html" class="ch.qos.logback.core.FileAppender">
  22. <!-- 输出的文件位置 -->
  23. <file>logback.html</file>
  24. <!-- 设置layout为输出html形式的 -->
  25. <layout class="ch.qos.logback.classic.html.HTMLLayout">
  26. <pattern>
  27. ${pattern}
  28. </pattern>
  29. </layout>
  30. </appender>
  31. <!-- 配置文件输出的appender -->
  32. <appender name="file" class="ch.qos.logback.core.FileAppender">
  33. <!-- 引入文件位置 -->
  34. <file>${fileDir}</file>
  35. <!--
  36. 配置日志输出格式
  37. -->
  38. <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  39. <!-- 格式引用通用属性配置 -->
  40. <pattern>${pattern}</pattern>
  41. </encoder>
  42. </appender>
  43. <!--
  44. 配置控制台输出的appender
  45. -->
  46. <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  47. <!--
  48. 表示对于日志输出目标的配置
  49. 默认: system.out 表示以黑色字体输出日志
  50. system.err 表示以红色字体输出日志
  51. -->
  52. <target>
  53. System.err
  54. </target>
  55. <!--
  56. 配置日志输出格式
  57. -->
  58. <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  59. <!-- 格式引用通用属性配置 -->
  60. <pattern>${pattern}</pattern>
  61. </encoder>
  62. </appender>
  63. <!--
  64. 日志记录器
  65. 配置Root Logger
  66. level: 日志级别
  67. -->
  68. <root level="all">
  69. <appender-ref ref="file"/>
  70. <appender-ref ref="console"/>
  71. <appender-ref ref="html"/>
  72. </root>
  73. </configuration>

html展示效果如果:

日志拆分

日志拆分使用的是RollingFileAppender,它继承了FileAppender,因此父类中能配置的属性,这里都可以配置,我们只需要关注当前类新增的查日志拆分策略属性即可。

在当前RollingFileAppender中也为我们提供了两个设置日志拆分策略的set方法:

先看一波如何配置,再来分析源码:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <configuration>
  3. <!--
  4. 配置文件通用属性
  5. 可以在当前配置文件中通过${name}的形式,取得value值
  6. 我们在此指定通用的日志输出格式
  7. 日志输出格式:
  8. %-10level 级别 案例为设置10个字符,左对齐
  9. %d{yyyy-MM-dd HH:mm:ss.SSS} 日期
  10. %c 当前类全限定名
  11. %M 当前执行日志的方法
  12. %L 行号
  13. %thread 线程名称
  14. %m或者%msg 信息
  15. %n 换行
  16. -->
  17. <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %c %M %L %thread %m%n"></property>
  18. <!-- 配置文件的输出路径 -->
  19. <property name="fileDir" value="logback.log"></property>
  20. <!-- 配置输出为html形式的appender -->
  21. <appender name="html" class="ch.qos.logback.core.FileAppender">
  22. <!-- 输出的文件位置 -->
  23. <file>logback.html</file>
  24. <!-- 设置layout为输出html形式的 -->
  25. <layout class="ch.qos.logback.classic.html.HTMLLayout">
  26. <pattern>
  27. ${pattern}
  28. </pattern>
  29. </layout>
  30. </appender>
  31. <!-- 配置文件输出的appender -->
  32. <appender name="file" class="ch.qos.logback.core.FileAppender">
  33. <!-- 引入文件位置 -->
  34. <file>${fileDir}</file>
  35. <!--
  36. 配置日志输出格式
  37. -->
  38. <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  39. <!-- 格式引用通用属性配置 -->
  40. <pattern>${pattern}</pattern>
  41. </encoder>
  42. </appender>
  43. <!--
  44. 配置控制台输出的appender
  45. -->
  46. <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  47. <!--
  48. 表示对于日志输出目标的配置
  49. 默认: system.out 表示以黑色字体输出日志
  50. system.err 表示以红色字体输出日志
  51. -->
  52. <target>
  53. System.err
  54. </target>
  55. <!--
  56. 配置日志输出格式
  57. -->
  58. <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  59. <!-- 格式引用通用属性配置 -->
  60. <pattern>${pattern}</pattern>
  61. </encoder>
  62. </appender>
  63. <!-- 配置文件的appender,可拆分可归档-归档是压缩的意思 -->
  64. <appender name="roll" class="ch.qos.logback.core.rolling.RollingFileAppender">
  65. <!-- 设置输出格式 -->
  66. <layout class="ch.qos.logback.classic.PatternLayout">
  67. <pattern>${pattern}</pattern>
  68. </layout>
  69. <!-- 设置未归档的日志文件输出位置 -->
  70. <file>roll_logback.log</file>
  71. <!-- 指定拆分规则 -->
  72. <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
  73. <!-- 按照时间和压缩格式声明文件名,压缩格式gz -->
  74. <fileNamePattern>roll.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
  75. <!-- 按照文件大小来进行拆分 -->
  76. <maxFileSize>1KB</maxFileSize>
  77. </rollingPolicy>
  78. </appender>
  79. <!--
  80. 日志记录器
  81. 配置Root Logger
  82. level: 日志级别
  83. -->
  84. <root level="all">
  85. <appender-ref ref="roll"/>
  86. </root>
  87. </configuration>

下面分析一波,这里源码流程从subAppend函数讲起,因为每一条日志的输出,都需要交给至少一个appender,完成日志的输出,而交给appender后,一定会来到appender的subAppend这里,各位可以自行debug源码流程

这里拿RollingFileAppender进行讲解:

  1. @Override
  2. protected void subAppend(E event) {
  3. //日志真正写入前,现需要判断是否应该触发当前日志文件的归档行为
  4. //因为可能当前写入数据超过了日志文件大小的限制,那么当前日志就应该归档,再创建一个新的日志文件
  5. // The roll-over check must precede actual writing. This is the
  6. // only correct behavior for time driven triggers.
  7. // We need to synchronize on triggeringPolicy so that only one rollover
  8. // occurs at a time
  9. //加锁确保一次只有一个线程进行日志roll操作
  10. synchronized (triggeringPolicy) {
  11. //triggeringPolicy负责判断是否需要进行roll over
  12. if (triggeringPolicy.isTriggeringEvent(currentlyActiveFile, event)) {
  13. rollover();
  14. }
  15. }
  16. //调用父类方法真正执行写入操作
  17. super.subAppend(event);
  18. }

具体回滚策略是如何执行的,这里不再进行分析

过滤器

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <configuration>
  3. <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %c %M %L %thread %m%n"></property>
  4. <!-- 配置文件的输出路径 -->
  5. <property name="fileDir" value="logback.log"></property>
  6. <!--
  7. 配置控制台输出的appender
  8. -->
  9. <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  10. <!--
  11. 表示对于日志输出目标的配置
  12. 默认: system.out 表示以黑色字体输出日志
  13. system.err 表示以红色字体输出日志
  14. -->
  15. <target>
  16. System.err
  17. </target>
  18. <!--
  19. 配置日志输出格式
  20. -->
  21. <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  22. <!-- 格式引用通用属性配置 -->
  23. <pattern>${pattern}</pattern>
  24. </encoder>
  25. <!-- 配置过滤器 -->
  26. <filter class="ch.qos.logback.classic.filter.LevelFilter">
  27. <!-- 设置日志的输出级别 -->
  28. <level>ERROR</level>
  29. <!-- 高于Level中设置的日志级别,则打印日志 -->
  30. <onMatch>ACCEPT</onMatch>
  31. <!-- 低于Level中设置的日志级别,则跳过 -->
  32. <onMismatch>DENY</onMismatch>
  33. </filter>
  34. </appender>
  35. <!--
  36. 日志记录器
  37. 配置Root Logger
  38. level: 日志级别
  39. -->
  40. <root level="all">
  41. <appender-ref ref="console"/>
  42. </root>
  43. </configuration>

通过过滤器,我们可以设置某个appender的日志过滤输出

过滤器链在何时会执行呢?

异步日志

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <configuration>
  3. <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %c %M %L %thread %m%n"></property>
  4. <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  5. <target>
  6. System.err
  7. </target>
  8. <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  9. <pattern>${pattern}</pattern>
  10. </encoder>
  11. </appender>
  12. <!-- 异步日志的配置 -->
  13. <appender name="async" class="ch.qos.logback.classic.AsyncAppender">
  14. <appender-ref ref="console"/>
  15. </appender>
  16. <root level="all">
  17. <appender-ref ref="async"/>
  18. </root>
  19. </configuration>

异步日志测试:

异步日志实现源码来看看吧:
先说明一下,logback采用生产者消费者模型实现的日志异步输出

默认如果丢弃的话,会丢弃掉info以下的日志输出

自定义Logger

  1. <logger name="com.dhy" level="info" additivity="false">
  2. <appender-ref ref="async"/>
  3. </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 的组合,是市场上最强大的日志功能实现方式,绝对是未来的主流趋势。

应用演示

引入依赖:

  1. <dependency>
  2. <groupId>org.apache.logging.log4j</groupId>
  3. <artifactId>log4j-core</artifactId>
  4. <version>2.12.1</version>
  5. </dependency>

可以设置jdk编译版本:

  1. <build>
  2. <plugins>
  3. <!-- 设置编译版本为1.8 -->
  4. <plugin>
  5. <groupId>org.apache.maven.plugins</groupId>
  6. <artifactId>maven-compiler-plugin</artifactId>
  7. <version>3.1</version>
  8. <configuration>
  9. <source>1.8</source>
  10. <target>1.8</target>
  11. <encoding>UTF-8</encoding>
  12. </configuration>
  13. </plugin>
  14. </plugins>
  15. </build>

使用演示:

  1. import org.apache.logging.log4j.LogManager;
  2. import org.apache.logging.log4j.Logger;
  3. import org.testng.annotations.Test;
  4. public class LogTest {
  5. @Test
  6. public void Test(){
  7. Logger logger = LogManager.getLogger(LogTest.class.getName());
  8. log(logger);
  9. }
  10. private void log(Logger logger) {
  11. logger.error("error");
  12. logger.warn("warn");
  13. logger.info("info");
  14. logger.debug("debug");
  15. logger.trace("trace");
  16. }
  17. }

没有提供配置文件,会提供一个日志的默认配置,但是会给出一个警告

配置文件

log4j2的配置文件类似logback,会默认去加载类路径下的log4j2.xml

因为log4j2参考了logback的设计思路,但是未来减少借鉴量,就将原本logback配置文件中小写的标签值,都转为了大写

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <configuration status="debug" monitorInterval="5">
  3. <!-- 配置appender -->
  4. <Appenders>
  5. <!-- 默认是system.out,这里我们设置为system.err,这一点和logback一样 -->
  6. <Console name="console" target="SYSTEM_ERR"></Console>
  7. </Appenders>
  8. <!-- 配置logger -->
  9. <Loggers>
  10. <!-- 配置rootLogger -->
  11. <Root level="trace">
  12. <AppenderRef ref="console"/>
  13. </Root>
  14. </Loggers>
  15. </configuration>

默认输出格式为%m%n

slf4j加log4j2

我们只需要导入一个依赖就够了:

  1. <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
  2. <dependency>
  3. <groupId>org.apache.logging.log4j</groupId>
  4. <artifactId>log4j-slf4j-impl</artifactId>
  5. <version>2.17.2</version>
  6. </dependency>

这样一个适配器依赖模块以及将slf4j门面依赖,log4j门面和log4k2日志实现依赖全部导入了

使用还是正常使用slf4j的日志门面api即可

日志输出到文件

这里的使用和之前logback的配置类似

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <configuration monitorInterval="5">
  3. <!-- 公共属性 -->
  4. <properties>
  5. <property name="logDir">log4j2.log</property>
  6. </properties>
  7. <!-- 配置appender -->
  8. <Appenders>
  9. <!-- 默认是system.out,这里我们设置为system.err,这一点和logback一样 -->
  10. <Console name="console" target="SYSTEM_ERR"></Console>
  11. <File name="file" fileName="${logDir}">
  12. <!-- 输出格式 -->
  13. <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n"></PatternLayout>
  14. </File>
  15. </Appenders>
  16. <!-- 配置logger -->
  17. <Loggers>
  18. <!-- 配置rootLogger -->
  19. <Root level="trace">
  20. <AppenderRef ref="console"/>
  21. <AppenderRef ref="file"/>
  22. </Root>
  23. </Loggers>
  24. </configuration>

文件默认也是追加写入

日志拆分

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <configuration monitorInterval="5">
  3. <!-- 公共属性 -->
  4. <properties>
  5. <property name="logDir">log4j2.log</property>
  6. </properties>
  7. <!-- 配置appender -->
  8. <Appenders>
  9. <!-- 默认是system.out,这里我们设置为system.err,这一点和logback一样 -->
  10. <Console name="console" target="SYSTEM_ERR"></Console>
  11. <File name="file" fileName="${logDir}">
  12. <!-- 输出格式 -->
  13. <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n"></PatternLayout>
  14. </File>
  15. <RollingFile name="rollingFile" fileName="rollLog.log"
  16. filePattern="${date:yyyy-MM-dd}/roll-%d{yyyy-MM-dd-HH-mm}-%i.log">
  17. <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n"></PatternLayout>
  18. <Policies>
  19. <!-- 在系统启动时,触发拆分规则,产生一个日志文件 -->
  20. <OnStartupTriggeringPolicy/>
  21. <!-- 按照文件的大小进行拆分 -->
  22. <SizeBasedTriggeringPolicy size="10KB"/>
  23. <!-- 按照时间节点进行拆分,拆分规则就是filePattern -->
  24. <TimeBasedTriggeringPolicy/>
  25. </Policies>
  26. <!--在同一目录下,文件的个数限制,如果超出了设置的数值,则根据时间进行覆盖,新的覆盖旧的规则 -->
  27. <DefaultRolloverStrategy max="30"/>
  28. </RollingFile>
  29. </Appenders>
  30. <!-- 配置logger -->
  31. <Loggers>
  32. <!-- 配置rootLogger -->
  33. <Root level="trace">
  34. <AppenderRef ref="rollingFile"/>
  35. </Root>
  36. </Loggers>
  37. </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测试

  • 引入异步日志依赖
  1. <dependency>
  2. <groupId>com.lmax</groupId>
  3. <artifactId>disruptor</artifactId>
  4. <version>3.3.7</version>
  5. </dependency>
  • 修改配置
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <configuration monitorInterval="5">
  3. <!-- 公共属性 -->
  4. <properties>
  5. <property name="logDir">log4j2.log</property>
  6. </properties>
  7. <!-- 配置appender -->
  8. <Appenders>
  9. <!-- 默认是system.out,这里我们设置为system.err,这一点和logback一样 -->
  10. <Console name="console" target="SYSTEM_ERR"></Console>
  11. <File name="file" fileName="${logDir}">
  12. <!-- 输出格式 -->
  13. <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n"></PatternLayout>
  14. </File>
  15. <Async name="async">
  16. <AppenderRef ref="console"></AppenderRef>
  17. </Async>
  18. </Appenders>
  19. <!-- 配置logger -->
  20. <Loggers>
  21. <!-- 配置rootLogger -->
  22. <Root level="trace">
  23. <AppenderRef ref="async"/>
  24. </Root>
  25. </Loggers>
  26. </configuration>
  • 测试异步效果
  1. public class LogTest {
  2. @Test
  3. public void Test(){
  4. Logger logger = LoggerFactory.getLogger(LogTest.class.getName());
  5. log(logger);
  6. System.out.println("1111");
  7. System.out.println("3333");
  8. System.out.println("2222");
  9. }
  10. private void log(Logger logger) {
  11. logger.error("error");
  12. logger.warn("warn");
  13. logger.info("info");
  14. logger.debug("debug");
  15. logger.trace("trace");
  16. }
  17. }

AsyncLogger测试

  1. Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

配置文件去掉async的配置

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <configuration monitorInterval="5">
  3. <!-- 配置appender -->
  4. <Appenders>
  5. <!-- 默认是system.out,这里我们设置为system.err,这一点和logback一样 -->
  6. <Console name="console" target="SYSTEM_ERR"></Console>
  7. <File name="file" fileName="${logDir}">
  8. <!-- 输出格式 -->
  9. <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n"></PatternLayout>
  10. </File>
  11. </Appenders>
  12. <!-- 配置logger -->
  13. <Loggers>
  14. <!-- 配置rootLogger -->
  15. <Root level="trace">
  16. <AppenderRef ref="console"/>
  17. </Root>
  18. </Loggers>
  19. </configuration>

测试

混合异步输出日志

  1. <!-- 配置异步Logger -->
  2. <AsyncLogger name="com" level="info"
  3. includeLocation="false" additivity="false">
  4. <AppenderRef ref="console"/>
  5. </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

  1. package helper.com.logTest;
  2. import org.apache.logging.log4j.LogManager;
  3. import org.junit.jupiter.api.Test;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.slf4j.bridge.SLF4JBridgeHandler;
  7. /**
  8. * @author 大忽悠
  9. * @create 2022/5/14 10:28
  10. */
  11. public class Log4jTest {
  12. @Test
  13. public void testLogBack() {
  14. System.err.println("------------testLogBack---------------");
  15. Logger logger = LoggerFactory.getLogger(Log4jTest.class);
  16. logger.error("error");
  17. logger.warn("warn");
  18. logger.info("info");
  19. logger.debug("debug");
  20. logger.trace("trace");
  21. System.err.println("-----------testLog4j2Bridge----------------");
  22. org.apache.logging.log4j.Logger log = LogManager.getLogger(Log4jTest.class);
  23. log.error("error");
  24. log.warn("warn");
  25. log.info("info");
  26. log.debug("debug");
  27. log.trace("trace");
  28. System.err.println("-----------testJULBridge----------------");
  29. java.util.logging.Logger julLog= java.util.logging.Logger.getLogger(Log4jTest.class.getName());
  30. julLog.addHandler(new SLF4JBridgeHandler());
  31. julLog.severe("severe");
  32. julLog.warning("warning");
  33. julLog.info("info");
  34. julLog.fine("fine");
  35. julLog.finer("finer");
  36. julLog.finest("finest");
  37. }
  38. }

JUL的桥接需要手动添加handler完成,该handler的publish方法源码如下:

  1. public void publish(LogRecord record) {
  2. if (record != null) {
  3. org.slf4j.Logger slf4jLogger = this.getSLF4JLogger(record);
  4. String message = record.getMessage();
  5. if (message == null) {
  6. message = "";
  7. }
  8. if (slf4jLogger instanceof LocationAwareLogger) {
  9. this.callLocationAwareLogger((LocationAwareLogger)slf4jLogger, record);
  10. } else {
  11. this.callPlainSLF4JLogger(slf4jLogger, record);
  12. }
  13. }
  14. }

spring配置文件配置日志

  1. logging:
  2. level:
  3. helper:
  4. com: trace
  5. pattern:
  6. console: "%d{yyyy年-MM月-dd日} [%-5level] %m%n"

spring配置文件配置日志输出到文件

  1. logging:
  2. level:
  3. helper:
  4. com: trace
  5. pattern:
  6. console: "%d{yyyy年-MM月-dd日} [%-5level] %m%n"
  7. file:
  8. path: test

指定的是存放日志文件夹的名字,日志会生成在该文件夹下面,名字为spring.log

spring引入logback的配置文件

放在类路径下,名字就叫logback.xml就可以了

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <configuration>
  3. <property name="pattern" value="大忽悠日志: [%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %c %M %L %thread %m%n"></property>
  4. <!-- 配置文件的输出路径 -->
  5. <property name="fileDir" value="logback.log"></property>
  6. <!--
  7. 配置控制台输出的appender
  8. -->
  9. <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  10. <!--
  11. 表示对于日志输出目标的配置
  12. 默认: system.out 表示以黑色字体输出日志
  13. system.err 表示以红色字体输出日志
  14. -->
  15. <target>
  16. System.err
  17. </target>
  18. <!--
  19. 配置日志输出格式
  20. -->
  21. <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  22. <!-- 格式引用通用属性配置 -->
  23. <pattern>${pattern}</pattern>
  24. </encoder>
  25. </appender>
  26. <!-- 配置文件的appender,可拆分可归档-归档是压缩的意思 -->
  27. <appender name="roll" class="ch.qos.logback.core.rolling.RollingFileAppender">
  28. <!-- 设置输出格式 -->
  29. <layout class="ch.qos.logback.classic.PatternLayout">
  30. <pattern>${pattern}</pattern>
  31. </layout>
  32. <!-- 设置未归档的日志文件输出位置 -->
  33. <file>roll_logback.log</file>
  34. <!-- 指定拆分规则 -->
  35. <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
  36. <!-- 按照时间和压缩格式声明文件名,压缩格式gz -->
  37. <fileNamePattern>roll.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
  38. <!-- 按照文件大小来进行拆分 -->
  39. <maxFileSize>1KB</maxFileSize>
  40. </rollingPolicy>
  41. </appender>
  42. <!--
  43. 日志记录器
  44. 配置Root Logger
  45. level: 日志级别
  46. -->
  47. <root level="info">
  48. <appender-ref ref="roll"/>
  49. <appender-ref ref="console"/>
  50. </root>
  51. </configuration>

Spring整合log4j2

  • 移除spring-boot-starter-logging模块依赖
  • 添加之前我们使用的log4j2的依赖即可
  1. <!-- spring web-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. <exclusions>
  6. <exclusion>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-logging</artifactId>
  9. </exclusion>
  10. </exclusions>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.springframework.boot</groupId>
  14. <artifactId>spring-boot-starter-log4j2</artifactId>
  15. </dependency>

下面引入log4j2的配置文件进行测试即可:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <configuration status="debug" monitorInterval="5">
  3. <!-- 配置appender -->
  4. <Appenders>
  5. <!-- 默认是system.out,这里我们设置为system.err,这一点和logback一样 -->
  6. <Console name="console" target="SYSTEM_ERR"></Console>
  7. </Appenders>
  8. <!-- 配置logger -->
  9. <Loggers>
  10. <!-- 配置rootLogger -->
  11. <Root level="info">
  12. <AppenderRef ref="console"/>
  13. </Root>
  14. </Loggers>
  15. </configuration>

输出彩色日志

记得开启Jansi(一个支持输出ANSI颜色的类库)

  1. -Dlog4j.skipJansi=false

记-Log4j 设置控制台打印彩色日志

log4j2日志配置颜色

log4j2底层配置文件动态刷新原理

log4j和log4j2怎么动态加载配置文件

相关文章