利用 Log4j2 异步保存日志到 MongoDB 中

Posted HelloTech

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用 Log4j2 异步保存日志到 MongoDB 中相关的知识,希望对你有一定的参考价值。

需求

将 Log4j2 日志文件写到 MongoDB 中,并且希望能按自定义字段进行保存。

添加依赖

由于此工程没有使用 Spring / SpringBoot 框架,主要演示怎么配置 Log4j2 配置将日志保存到 MongoDB,如果使用了 SpringBoot 框架,请按 spring-boot-starter-xxxx 的方式配置。

注意版本问题,如果使用 log4j2 2.11.0 以上的版本配置有区别,可以参考 github 上 log4j2 对应 tag 的项目结构

 
   
   
 
  1. <properties>

  2.    <log4j2.version>2.9.0</log4j2.version>

  3.    <log4j2-nosql.version>2.9.0</log4j2-nosql.version>

  4.    <log4j2-mongodb3.version>2.11.1</log4j2-mongodb3.version>

  5.    <mongodb-driver.version>3.8.2</mongodb-driver.version>

  6. </properties>

  7. <dependency>

  8.    <groupId>org.mongodb</groupId>

  9.    <artifactId>mongo-java-driver</artifactId>

  10.    <version>${mongodb-driver.version}</version>

  11. </dependency>

  12. <dependency>

  13.    <groupId>org.apache.logging.log4j</groupId>

  14.    <artifactId>log4j-nosql</artifactId>

  15.    <version>${log4j2.version}</version>

  16. </dependency>

Log4j2 配置中增加

 
   
   
 
  1. <appenders>

  2.    ...

  3.    <NoSql name="mongoAppender" bufferSize="10">

  4.        <MongoDb databaseName="phone-schedule" collectionName="asr_log" server="10.0.0.xx" port="xxx" username="xxxx" password="xxxx"/>

  5.    </NoSql>

  6.       <!-- 异步保存数据 -->  

  7.    <Async name="mongoAppenderAsync">

  8.        <AppenderRef ref="mongoAppender" />

  9.    </Async>

  10.    ...

  11. </appenders>

  12. <loggers>

  13.    ...

  14.    <logger name="mongolog" level="info" additivity="false">

  15.        <AppenderRef ref = "mongoAppenderAsync" />

  16.        <!--<AppenderRef ref="Console" />--> <!-- 生产环境要注释掉 -->

  17.    </logger>

  18.    ...

  19. <loggers>

单元测试

 
   
   
 
  1. @Log4j2(topic = "mongolog")

  2. public class LogTest {

  3.    @Test

  4.    public void testLog() {

  5.        log.info("xxxx");

  6.        log.error("error message");

  7.    }

  8. }

结果:

结论:

从上图中,发现日志已经保存到 MongoDB 中且结构一致(自动创建 Field),同时,无论什么级别的日志都是将消息保存到 message 字段中,如果想保存结构化的信息怎么办?

通过 MDC 增加自定义字段

 
   
   
 
  1. @Log4j2(topic = "mongolog")

  2. public class LogTest {

  3.    @Test

  4.    public void testLog() {

  5.        MDC.put("sessionId", UUID.randomUUID());

  6.        MDC.put("ask", "Hello");

  7.        MDC.put("asr", "Jack");

  8.        MDC.put("phone", "15011227340");

  9.        MDC.put("audioPath", "/data/file");

  10.        log.info("xxxx");

  11.        MDC.clear();

  12. //        log.error("error message");

  13.    }

  14. }

结果:

结论:

从上图中,可以发现在 contextMap 中已经有5个 Fields 了,然后就方便提供 Restful API 在 Web UI 中 渲染了。

遇到的问题

在本地测试通过之后,打包发布测试环境 错误日志如下:

 
   
   
 
  1. ERROR StatusLogger appenders contains an invalid element or attribute "NoSql"

  2. ERROR StatusLogger No appender named mongoAppender was configured

  3. Exception in thread "main" java.lang.ExceptionInInitializerError

  4. Caused by: org.apache.logging.log4j.core.config.ConfigurationException: No appenders are available for AsyncAppender mongoAppenderAsync

  5.        at org.apache.logging.log4j.core.appender.AsyncAppender.start(AsyncAppender.java:120)

  6.        at org.apache.logging.log4j.core.config.AbstractConfiguration.start(AbstractConfiguration.java:265)

  7.        at org.apache.logging.log4j.core.LoggerContext.setConfiguration(LoggerContext.java:545)

  8.        at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:617)

  9.        at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:634)

  10.        at org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:229)

  11.        at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:242)

  12.        at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:45)

  13.        at org.apache.logging.log4j.LogManager.getContext(LogManager.java:174)

  14.        at org.apache.logging.log4j.LogManager.getLogger(LogManager.java:618)

解决方案

LOG4J2-691 https://issues.apache.org/jira/browse/LOG4J2-691

默认的 assembly 配置,这样打出一个 jar 包里没有依赖的 lib 目录,可以通过自定义 assembly 文件,指定把什么内容打到包里。

 
   
   
 
  1. <plugin>

  2.    <artifactId>maven-assembly-plugin</artifactId>

  3.    <configuration>

  4.        <appendAssemblyId>false</appendAssemblyId>

  5.        <descriptorRefs>

  6.            <descriptorRef>jar-with-dependencies</descriptorRef>

  7.        </descriptorRefs>

  8.        <archive>

  9.            <manifest>

  10.                <mainClass>com.gemantic.phone.PhoneServer</mainClass>

  11.            </manifest>

  12.        </archive>

  13.    </configuration>

  14.    <executions>

  15.        <execution>

  16.            <id>make-assembly</id>

  17.            <phase>package</phase>

  18.            <goals>

  19.                <goal>assembly</goal>

  20.            </goals>

  21.        </execution>

  22.    </executions>

  23. </plugin>


以上是关于利用 Log4j2 异步保存日志到 MongoDB 中的主要内容,如果未能解决你的问题,请参考以下文章

Log4j2简介和异步日志梳理

如何监控 Log4j2 异步日志遇到写入瓶颈

Log4j2中的同步日志与异步日志

Log4j2同步日志,混合日志和异步日志配置详解

Log4j2异步日志同步日志和混合日志的配置详解

Log4j2异步日志背后的数字