vlambda博客
学习文章列表

log4j2强大的日志框架

前一段时间帮助我司的c#大牛来搞java的springboot集成log4j2日志,难点是配置之后没有把日志打印到本地;于是有感,想做一期关于log4j2的说明,关于为什么使用log4j2。


一个老牌的日志框架叫logback,至今也有十几年了,最近的稳定版停留在 17年了,一直没有跟他更新,它的兄弟slf4也是停留在17年,基本liangliang了。


最主要的是logback异步性能垃圾的一批,功能也是很简陋,配置起来也是异常的麻烦,还比不上阿帕奇的新框架 log4j2 也就是我们的主角了。


log4j2比log4j有了很大的提升,包含但不限于内部设计,还有简单了的配置、牛逼的参数格式化、优秀的异步性能

4j2包含 API(log4j-api)、实现(log4j-core)。API属于日志抽象实现部分才是主要核心内容。

图中可以发现,4j2异步性能远远比其他框架性能高的很多,可以算说是非常夸张的了,压力越大吞吐差距就越明显,比如64thread下 差距接近10倍。


2.6版本始, 默认以零GC模式运行,各种Message对象,字符串数组,字节数组等全部复用,不重复创建,大大减少了无用对象的创建,从而做到“零GC”。

还提供memory mapped fileAppender,I/O 部分使用memory mapped file来实现,可以得到极高的I/O性能。


在slf4j里,我们可以用{}的方式来实现“format”的功能(参数会直接toString替换占位符),像下面这样:

logger.debug("Logging in user {} with birthday {}"user.getName(), user.getBirthdayCalendar());

log4j2 中除了支持{}的参数占位符,还支持String.format的形式:

public static Logger logger = LogManager.getFormatterLogger("Foo");

logger.debug("Logging in user %s with birthday %s", user.getName(), user.getBirthdayCalendar());
logger.debug("Logging in user %1$s with birthday %2$tm %2$te,%2$tY", user.getName(), user.getBirthdayCalendar());
logger.debug("Integer.MAX_VALUE = %,d", Integer.MAX_VALUE);
logger.debug("Long.MAX_VALUE = %,d", Long.MAX_VALUE);

如果想使用String.format的形式,需要使用LogManager.getFormatterLogger而不是LogManager.getLogger


printf,不用创建LogManager.getFormatterLogger,也可以用String.format的形式

logger.printf(Level.INFO, "Logging in user %1$s with birthday %2$tm %2$te,%2$tY", user.getName(), user.getBirthdayCalendar());

logger.debug("Opening connection to {}...", someDataSource);


今天的重点来了啊,lazy logging

举例:

logger.debug("入参报文:{}",JSON.toJSONString(policyDTO));

debug级别可以打印,如果改成info级别,不会打印,但是json。toJsonStirng序列化肯定执行,效率低下。


为了改进效率低下,继续走:

if(logger.isDebugEnabled()){
logger.debug("入参报文:{}",JSON.toJSONString(policyDTO));
}

通过isDebugEnable来判断当前配置下debug级别是否可以输出


但是这样的话,一行变成了三行,怎么办呢,继续走:


提供了lambda的支持,可以实现“惰性”打日志

void debug(String message, Supplier<?>... paramSuppliers);
void info(String message, Supplier<?>... paramSuppliers);
void trace(String message, Supplier<?>... paramSuppliers);
void error(String message, Supplier<?>... paramSuppliers);

//等同于下面的先判断,后打印
logger.debug("入参报文:{}",() -> JSON.toJSONString(policyDTO));

if(logger.isDebugEnabled()){
logger.debug("入参报文:{}",JSON.toJSONString(policyDTO));
}

Log4j 2 可以 XML/JSON/YML/Properties 配置文件,最流行的还是xml。


如果真的使用到log4j2日志框架怎么办呢,流水线教程来了:

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>


默认路径是  classpath:log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration xmlns:xi="http://www.w3.org/2001/XInclude"
status="warn" name="XInclude">

<Properties>
<Property name="PATTERN" value="%d
{yyyy-MM-dd HH:mm:ss.SSS} %5p [%t] %-40.40c{1.} : %m%n"/>
</Properties>
<Appenders>
<!-- 输出到控制台,仅在本地开发环境使用 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="$
{PATTERN}"/>
</Console>
<!--输出到日志文件,滚动分割日志文件,自动打包gz-->
<RollingFile name="File" fileName="logs/app.log" filePattern="logs/archives/app-%d
{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="$
{PATTERN}"/>
<Policies>
<!--默认一天一个文件-->
<TimeBasedTriggeringPolicy />
<!--一天内大于size就单独分隔-->
<SizeBasedTriggeringPolicy size="1 GB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<!-- 添加你的自定义logger,一般用于区分包名的日志,不同包名不同的级别/appender -->
<!-- additivity 意思是,调用完当前appender,是否继续调用parent logger appender,默认true-->
<Logger name="your logger/package name" level="debug" additivity="false"/>
<!--默认的Root Logger 级别-->
<Root level="INFO">
<!--这里需要区分下环境(配合maven profile之类的)-->
<!-- 开发环境使用Console Appender,生产环境使用File Appender -->
<AppenderRef ref="Console"/>
<AppenderRef ref="File"/>
</Root>
</Loggers>
</Configuration>

XML配置文件语法

<?xml version="1.0" encoding="UTF-8"?>;
<Configuration>
<Properties>
<Property name="name1">value</property>
<Property name="name2" value="value2"/>
</Properties>
<filter ... />
<Appenders>
<appender ... >
<filter ... />
</appender>
...
</Appenders>
<Loggers>
<Logger name="name1">
<filter ... />
</Logger>
...
<Root level="level">
<AppenderRef ref="name"/>
</Root>
</Loggers>
</Configuration>

使用:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

Logger logger = LogManager.getLogger(Log4j2Test.class);
logger.error(...);
logger.warn(...);
logger.info(...);
logger.debug(...);
logger.trace(...);

使用slf4j也行,但是推荐使用log4j2,以便使用匹配等框架功能


全异步配置,启动项目的时候添加:

text
-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector


就到这里,再见!!