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
就到这里,再见!!