微服务架构师封神之路03-使用log4j2统一微服务内部不同日志工具
日志是软件架构设计中首先要考虑的问题
在设计一个微服务应用的过程中,我首先想到的是日志架构的设计。
人世间的所有应用系统,无论什么业务,无论它是否以数据为中心,它必然会有日志系统。有了日志,我们才知道应用中发生了什么。日志无外乎有两个用途:第一,监控当前应用系统的运行状态;第二,回溯系统中发生的错误,帮助开发人员分析问题。所以日志对任何系统来说都是首要的、不可或缺的。虽然它看起来是那么不起眼。
为什么我要考虑统一日志工具的问题
首先我先设定一个前题:『同一个应用系统的日志必须写在同一个日志文件中』
如果没有这个前题也就没有讨论下去的必要了。系统里并存800个日志工具也完全没有问题,大不了同时输出800个日志文件呗?!哥们,那你要这么说就没意思了!…
我是这样想的。当我们构建微服务的时候,无非有两种可能:第一种,完完全全的一个新的应用,从头到脚都要我们一行一行的代码码出来;另一种,就是老的系统还在,但我们要进行重构,比如原来是monolithic architecture,现在我们要重新设计另一个微服务架构的版本。
- 第一种情况,我们的服务还要依赖一些第三方的模块,大概率它们不会和我们采用一样的日志工具。这些模块的日志要不要出现在微服务的日志中?
- 第二种情况,我们为了多快好省的建设新的微服务应用,避免不了要大面积的从旧的系统中拷贝代码。旧代码的日志肯定不是用log4j2写的啊!如果为了减少修改日志代码所引入的风险,可不可以不修改这些日志代码,但是仍然可以让应用享受到最新的日志工具(log4j2)所带来的性能优势?
统一系统的日志工具就是为了解决以上两个问题。这两个问题的答案也都是肯定的。日志工具的设计者们不可能不考虑到这两个问题。
我的微服务应用采用springboot框架,日志工具选型我选了log4j2。如果你想知道这个故事的来龙去脉,请参看『微服务架构师封神之路01』和『微服务架构师封神之路02』。闲话少说,我把我为微服务统一日志工具的任务分解成一下两个步骤。
Enable log4j2 in springboot
springboot的原生日志系统是logback,它不需要做额外的配置,自动生效,默认将日志写入console。
现在我们要让log4j2生效需要做一下三件事:
- Disable logback in pom.xml。Exclude spring-boot-starter-logging from spring-boot-starter-web [1]
- Enable log4j2 in pom.xml。添加依赖spring-boot-starter-log4j2 [2]
- 添加log4j2.xml。将lgo4j2的配置文件加入classpath中,我们可以把它放在src/main/resources下面
pom.xml
Please pay attention on [1] and [2]
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- [1] -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- [2] -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
</dependencies>
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="com.b5wang.cloudlab.helloworld" level="debug" />
<Root level="error">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>
都有哪些日志工具被统一了
列出常见的Java日志工具:
- JUL (java.util.logging)
- JCL (Apache commons logging)
- Log4j
- SLF4j / Logback
- Log4j2
目前,除了log4j还不支持。其它以上所有的日志都可以通过上面的log4j2的配置统一输出。道理很简单,spring-boot-starter-log4j2提供了这些日志工具与log4j2完整的对接工具。无论是在我自己的微服务代码中,还是第三方依赖的代码中,出现以下任意的Logger的调用,最终都会输出到log4j2的日志中。
/** log4j2
* fatal, error, warn, info, debug, trace
* */
private static final org.apache.logging.log4j.Logger LOG4J2_LOGGER = org.apache.logging.log4j.LogManager.getLogger(AppMain.class);
/** slf4j
* error, warn, info, debug, trace
* If want fatal, you need create by yourself with marker and specific apender
*
* logback is the same api as slf4j. Logback is implementation
* */
private static final org.slf4j.Logger SLF4J_LOGGER = org.slf4j.LoggerFactory.getLogger(AppMain.class);
/** JUL
* SEVERE (highest level)
* WARNING
* INFO
* CONFIG
* FINE
* FINER
* FINEST (lowest level)
* */
private static final java.util.logging.Logger JUL_LOGGER = java.util.logging.Logger.getLogger(AppMain.class.getName());
/** JCL
* fatal, error, warn, info, debug, trace
* */
private static final org.apache.commons.logging.Log JCL_LOG = org.apache.commons.logging.LogFactory.getLog(AppMain.class);
如何将log4j也统一进来?
只要在pom.xml中增加一个依赖。
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-1.2-api</artifactId>
<version>${log4j-1.2-api.version}</version>
</dependency>
现在微服务代码中,和第三方依赖中对log4j日志接口的调用都会输出到log4j2指定的地方了。
/** log4j
*
* */
private static final org.apache.log4j.Logger LOG4J_LOGGER = org.apache.log4j.Logger.getLogger(AppMain.class);
例外
目前我们只是尝试了简单的调用日志接口而已,这种情况下的统一是完全可行,而且十分简单方便。
当如果代码中还涉及到对日志工具更深层次的调用,比如编程式的配置,或者调用了非日志接口的程序,这种统一日志的思想就很难通过像上面演示的简单的配置来实现的。
如果你在现实工作中遇到过类似的问题或者用例,欢迎留言交流!
今天就想到这里! 拜拜! : )
参考与延伸阅读
How to migrate log4j to log4j2: https://logging.apache.org/log4j/log4j-2.2/manual/migration.html