0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Java日志框架中的王者是谁

Android编程精选 来源:掘金 作者:空无 2021-10-13 09:12 次阅读
来源丨juejin.cn/post/6945753017878577165

Logback 算是JAVA 里一个老牌的日志框架,从06年开始第一个版本,迭代至今也十几年了。不过logback最近一个稳定版本还停留在 2017 年,好几年都没有更新;logback的兄弟 slf4j 最近一个稳定版也是2017年,有点凉凉的意思。

而且 logback的异步性能实在拉跨,功能简陋,配置又繁琐,远不及Apache 的新一代日志框架 - Log4j

目前来看,Log4j2 就是王者,其他日志框架都不是对手

Log4j2简介

Apache Log4j 2是 Log4j(1) 的升级版,比它的祖先 Log4j 1. x 有了很大的改进,和logback对比有很大的改进。除了内部设计的调整外,主要有以下几点的大升级:

  • 更简化的配置
  • 更强大的参数格式化
  • 最夸张的异步性能

Log4j 2中,分为API(log4j-api)和实现(log4j-core)两个模块。API 和slf4j 是一个类型,属于日志抽象/门面,而实现部分,才是Log4j 2的核心。

  • org.apache.logging.log4j » log4j-api
  • org.apache.logging.log4j » log4j-core

最牛逼的性能

最强的异步性能

这个特性,算是Log4j2最强之处了。log4j2 在目前JAVA中的日志框架里,异步日志的性能是最高的,没有之一。

log4j2的异步(全异步,非混合模式)下的性能,远超log4j1和logback,简直吊打。压力越大的情况下,吞吐上的差距就越大。在64线程测试下,log4j2的吞吐达到了180w+/s,而logback/log4j1只有不到20w,相差近十倍

零GC(Garbage-free)

从2.6版本开始(2016年),log4j2 默认就以零GC模式运行了。什么叫零GC呢?就是不会由于log4j2而导致GC。

log4j2 中各种Message对象,字符串数组,字节数组等全部复用,不重复创建,大大减少了无用对象的创建,从而做到“零GC”。

更高性能 I/O 写入的支持

log4j 还提供了一个MemoryMappedFileAppender,I/O 部分使用MemoryMappedFile来实现,可以得到极高的I/O性能。不过在使用MemoryMappedFileAppender之前,得确定你足够了解MemoryMappedFile的相关知识,否则不要轻易使用呦。

更强大的参数格式化

API模块和slf4j相比,提供了更丰富的参数格式化功能。

使用{}占位符格式化参数

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

logger.debug("Logginginuser{}withbirthday{}",user.getName(),user.getBirthdayCalendar());

使用String.format的形式格式化参数

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

publicstaticLoggerlogger=LogManager.getFormatterLogger("Foo");

logger.debug("Logginginuser%swithbirthday%s",user.getName(),user.getBirthdayCalendar());
logger.debug("Logginginuser%1$swithbirthday%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

使用logger.printf格式化参数

log4j2 的 Logger接口中,还有一个printf方法,无需创建LogManager.getFormatterLogger,就可以使用String.format的形式

logger.printf(Level.INFO,"Logginginuser%1$swithbirthday%2$tm%2$te,%2$tY",user.getName(),user.getBirthdayCalendar());

logger.debug("Openingconnectionto{}...",someDataSource);

“惰性”打日志(lazy logging)

这个功能虽然小,但非常实用。

在某些业务流程里,为了留根或追溯问题,需要完整的打印入参,一般是把入参给用JSON/XML序列化后用debug级别打印:

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

如果需要追溯问题时,会将系统的日志级别调到debug/trace,这样就可以打印。但是这里有个问题,虽然在info级别下debug不会输出内容,但JSON.toJSONString()这个序列化的代码一定会执行,严重影响正常流程下的执行效率。

我们期望的结果是info级别下,连序列化都不执行。这里可以通过isDebugEnable来判断当前配置下debug级别是否可以输出:

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

这样虽然可以避免不必要的序列化,但每个地方都这么写还是有点难受的,一行变成了三行。

log4j2 的 logger 对象,提供了一系列lambda的支持,通过这些接口可以实现“惰性”打日志:

voiddebug(Stringmessage,Supplier...paramSuppliers);
voidinfo(Stringmessage,Supplier...paramSuppliers);
voidtrace(Stringmessage,Supplier...paramSuppliers);
voiderror(Stringmessage,Supplier...paramSuppliers);

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

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

这种 Supplier + Lambda 的形式,等同于上面的先判断 isDebugEnable 然后打印,三行的代码变成了一行。嗯,真香。

更简化的配置

Log4j 2 同时支持XML/JSON/YML/Properties 四种形式的配置文件,不过最主流的还是XML的方式,最直观。

来看一下logback和log4j2的配置文件对比,同样功能的配置下:

logback.xml


<configuration>
<appendername="File"class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.logfile>
<rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/archives/app-%d{yyyy-MM-dd}.log.gzfileNamePattern>

<maxFileSize>1GBmaxFileSize>
rollingPolicy>
appender>
<rootlevel="info">
<appender-refref="File"/>
root>
configuration>

log4j2.xml


<Configurationxmlns:xi="http://www.w3.org/2001/XInclude"
status="warn"name="XInclude">
<Appenders>
<RollingFilename="File"fileName="logs/app.log"filePattern="logs/archives/app-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayoutpattern="%d{yyyy-MM-ddHHss.SSS}%5p[%t]%-40.40c{1.}:%m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>

<SizeBasedTriggeringPolicysize="1GB"/>
Policies>
RollingFile>
Appenders>
<Loggers>
<Rootlevel="INFO">
<AppenderRefref="File"/>
Root>
Loggers>
Configuration>

在log4j2中,appender的配置从使用 Appender 实现名即标签名的形式,语法上更简洁一些:

<RollingFilename="File">

<appendername="File"class="ch.qos.logback.core.rolling.RollingFileAppender">

与其他日志抽象/门面适配

log4j2 由于拆分为 API 和 实现两部分,所以可能也需要和其他日志框架进行适配

其他的特点

  • 异步队列使用高性能队列 - LMAX Disruptor
  • Appender丰富,有JMS/JPA/KAFKA/Http/MONGODB/CouchDB/Socket/Script等各种Appender的支持
  • 支持自定义日志级别 ……

基本用法

终于介绍完了Log4j2的强大,现在来介绍下Log4j2的基本使用。

引用log4j2的maven依赖

log4j-api在log4j-core中已经有依赖了,直接依赖core即可

<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-coreartifactId>
<version>2.14.1version>
dependency>

注意,引用log4j2时,需要注意项目中是否有多套日志框架共存/冲突,需要适配的问题。细节请参考上面的与其他日志抽象/门面适配

配置文件示例

首先是配置文件,默认的配置文件路径为:classpath:log4j2.xml推荐使用xml)


<Configurationxmlns:xi="http://www.w3.org/2001/XInclude"
status="warn"name="XInclude">
<Properties>
<Propertyname="PATTERN"value="%d{yyyy-MM-ddHHss.SSS}%5p[%t]%-40.40c{1.}:%m%n"/>
Properties>
<Appenders>

<Consolename="Console"target="SYSTEM_OUT">
<PatternLayoutpattern="${PATTERN}"/>
Console>

<RollingFilename="File"fileName="logs/app.log"filePattern="logs/archives/app-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayoutpattern="${PATTERN}"/>
<Policies>

<TimeBasedTriggeringPolicy/>

<SizeBasedTriggeringPolicysize="1GB"/>
Policies>
RollingFile>
Appenders>
<Loggers>


<Loggername="yourlogger/packagename"level="debug"additivity="false"/>

<Rootlevel="INFO">


<AppenderRefref="Console"/>
<AppenderRefref="File"/>
Root>
Loggers>
Configuration>

XML配置文件语法

;
<Configuration>
<Properties>
<Propertyname="name1">valueproperty>
<Propertyname="name2"value="value2"/>
Properties>
<filter.../>
<Appenders>
<appender...>
<filter.../>
appender>
...
Appenders>
<Loggers>
<Loggername="name1">
<filter.../>
Logger>
...
<Rootlevel="level">
<AppenderRefref="name"/>
Root>
Loggers>
Configuration>

创建Logger

直接使用log4j2的api:

importorg.apache.logging.log4j.LogManager;
importorg.apache.logging.log4j.Logger;

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

如果是配合slf4j使用也是可以的,只需要按照前面说的,提前做好适配,然后使用slf4j的api即可。不过如果是新系统的话,建议直接上log4j2的api吧,可以享受所有log4j2的功能,使用slf4j之类的api时,上面说的参数格式化之类的功能就无法使用了。

全异步配置(重要!!)

推荐配置log4j2 全异步(all async),在你的启动脚本中增加一个系统变量的配置:

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

总结

Log4j2 如今性能最强,功能最强,而且持续更新维护。还在等什么?是时候替换你的logback/log4j1了!

责任编辑:haq
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    19

    文章

    2951

    浏览量

    104452
  • 框架
    +关注

    关注

    0

    文章

    398

    浏览量

    17393
  • 日志
    +关注

    关注

    0

    文章

    138

    浏览量

    10623

原文标题:最牛逼的 Java 日志框架,性能无敌,横扫所有对手.....

文章出处:【微信号:AndroidPush,微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    nginx日志配置方法

    access_log用来定义日志级别,日志位置。
    的头像 发表于 10-24 17:43 169次阅读

    日志篇:模组日志总体介绍

    ​今天我们学习合宙模组日志总体介绍,以下进入正文。 一、本文讨论的边界 本文是对合宙 4G 模组, 以及 4G+GNSS 模组的日志功能的总体介绍。通过日志,可以对研发过程,以及模组
    的头像 发表于 10-24 07:16 115次阅读
    <b class='flag-5'>日志</b>篇:模组<b class='flag-5'>日志</b>总体介绍

    日志框架简介-Slf4j+Logback入门实践

    结果不受日志的有无影响,但没有日志的应用程序是不完整的,甚至可以说是有缺陷的。优秀的日志系统可以 记录操作轨迹 、 监控系统运行状态 和 解决系统故障 。 Java
    的头像 发表于 07-30 10:00 1030次阅读
    <b class='flag-5'>日志</b><b class='flag-5'>框架</b>简介-Slf4j+Logback入门实践

    奇怪!应用的日志呢??

    1. 问题回顾 问题背景 是在进行台应用中间件迁移过程,发现存在 项目启动失败 或者 项目正常启动 (jsf正常挂载并正常运行,mq正常发送和消费)但是 无任何日志打印 现象。 更奇怪 的是不打
    的头像 发表于 06-11 10:48 280次阅读
    奇怪!应用的<b class='flag-5'>日志</b>呢??

    【嵌入式SD NAND】基于FATFS/Littlefs文件系统的日志框架实现

    文章目录 【嵌入式】基于FATFS/Littlefs文件系统的日志框架实现 1. 概述 2. 设计概要 3. 设计实现 3.1 初始化 `init` 3.2 日志写入 `write` 3.3
    的头像 发表于 03-14 18:13 993次阅读
    【嵌入式SD NAND】基于FATFS/Littlefs文件系统的<b class='flag-5'>日志</b><b class='flag-5'>框架</b>实现

    【嵌入式SD NAND】基于FATFS/Littlefs文件系统的日志框架实现

    设备的运行数据。日志文件不同于其他功能,其往往需要具备几个基本特性需求: 单个文件大小限制 日志总大小空间占用限制 自动循环覆盖 网上也有一些开源的日志框架,如 Log4j,不过大都是
    发表于 03-14 18:12

    【嵌入式SD NAND】基于FATFS/Littlefs文件系统的日志框架实现

    文章目录【嵌入式】基于FATFS/Littlefs文件系统的日志框架实现1.概述2.设计概要3.设计实现3.1初始化`init`3.2日志写入`write`3.3日志读取`read`3
    的头像 发表于 03-14 18:12 1085次阅读
    【嵌入式SD NAND】基于FATFS/Littlefs文件系统的<b class='flag-5'>日志</b><b class='flag-5'>框架</b>实现

    西门子博途数据日志的使用

    数据日志文件以 CSV 格式存储在永久性闪存。可以使用以下两种方法管理、查看数据日志
    的头像 发表于 12-20 10:37 3532次阅读
    西门子博途数据<b class='flag-5'>日志</b>的使用

    关于2023年Java趋势的内容

    Java 17+ 被重新归类为 Java 17,仍处于 早期采用者 阶段,因为有更多的框架Java 17 作为基线。Java 21 已被
    的头像 发表于 12-13 11:17 417次阅读
    关于2023年<b class='flag-5'>Java</b>趋势的内容

    基于springboot和vue框架Java

    本文将详细介绍基于Spring Boot和Vue框架进行Java应用开发的实践。首先,将介绍Spring Boot和Vue框架的基本概念及其优点。然后,将详细介绍如何搭建Spring Boot
    的头像 发表于 12-03 15:15 922次阅读

    javaweb的三大框架有哪些

    在JavaWeb开发,有许多流行的框架可供选择。这些框架旨在提高开发效率、简化开发过程,并为开发人员提供更强大的功能和灵活性。下面是三个在JavaWeb开发中广泛使用的主要框架。 S
    的头像 发表于 12-03 11:47 1957次阅读

    Android开发如何解决典型场景缺通用日志的问题

    下面针对一些典型场景缺通用日志(android/kernel)的问题,一一列举如下,希望可以让大家关注到缺日志的真实原因。如下问题也提醒各位工程师:谨慎添加日志,不要随意添加,否则即容易造成自己
    的头像 发表于 11-23 17:39 838次阅读
    Android开发<b class='flag-5'>中</b>如何解决典型场景缺通用<b class='flag-5'>日志</b>的问题

    logcat命令抓取日志方法

    命令抓取日志 logcat -b main -b system -b crash -r 1024 -n 5 -f android.log -v threadtime -b:加载可供查看的缓冲区的日志
    的头像 发表于 11-23 17:31 997次阅读
    logcat命令抓取<b class='flag-5'>日志</b>方法

    Android开发日志接口介绍

    1、日志接口 日志接口内容,共分为java层、native层、kernel层等。下面就对每个层级的内容分别进行介绍。 1.1 java层调用接口
    的头像 发表于 11-23 16:27 1003次阅读
    Android开发<b class='flag-5'>中</b>的<b class='flag-5'>日志</b>接口介绍

    C++异步日志实践

    一个高效可拓展的异步C++日志库:RING LOG,本文分享了了其设计方案与技术原理等内容 导论 同步日志与缺点 传统的日志也叫同步日志,每次调用一次打印
    的头像 发表于 11-09 10:29 614次阅读
    C++异步<b class='flag-5'>日志</b>实践