Logback 与 log4j2 性能哪个更强?

往期热门文章:

1、只用Tomcat,不用Nginx行不行?

2、听说你还在用Xshell?

3、惊艳到我的 10个 MySQL高级查询技巧!

4、我有点想用JDK17了

5、解放大脑:ChatGPT + PlantUML = 不用画图了

一、简介


logback、log4j2 等都是非常优秀的日志框架。在日常使用中,我们很少会关注去使用哪一个框架, 但其实这些日志框架在性能方面存在明显的差异。 尤其在生产环境中, 有时候日志的性能高低,很可能影响到机器的成本, 像一些大企业,如阿里、腾讯、字节等,一点点的性能优化,就能节省数百万的支出。 再次, 统一日志框架也是大厂常有的规范化的事情, 还可以便于后续的 ETL 流程, 因此,我们选一个日志框架,其实还是比较重要的。


1.1 浅谈 slfj4、log4j 与 logback 的关系


笼统的讲就是 slf4j 是一系列的日志接口,而 log4j、logback 是具体实现了这些接口的日志框架。也可以简单理解为 slf4j 是接口, logback 和 log4j 是 slf4j 的具体实现。slf4j 具备很高的易用性和很好的抽象性。 使用 slf4j 编写日志消息非常简单。首先需要调用 LoggerFactory 上的 getLogger 方法来实例化一个新的 Logger 对象。


有两种方法:


方法1: 使用 lombok (推荐)
直接在类上打上 lombok 的注解, 这个方法是最简单,代码量最小,编程效率最高的, 而且 lombok 组件在很多场景都很好用。
    

@Slf4jpublic class Main {}

方法2: 直接使用
使用 org.slf4j.LoggerFactory 的 getLogger 方法获取 logger 实例,注意推荐 private static final。
private static final Logger LOG = LoggerFactory.getLogger(Main.class);


二、性能测试对比


2.1 性能对比图



从上图可以得出两个结论:
  • log4j2 全面优于 logback。log4j2 性能是 logback 的两倍;
  • 随着线程数量的增加, 日志输出能力并不会线性增加,在增加到约两倍于 CPU 核数的时候, 日志性能达到比较高的一个值。

Tips: 已知的影响效率的是,打出方法名称和行号都会显著降低日志输出效率, 如我们单单去掉 行号,在单线程情况下, log4j2 的性能相差一倍多。见下图:


2.2 测试环境


1. 硬件环境:
    

CPU AMD Ryzen 5 3600 6-Core Processor  Base speed:  3.95 GHzMemory 32.0 GB Speed:  2666 MHz

2. JVM 信息
    

JDK版本:semeru-11.0.20JVM参数:-Xms1000m -Xmx1000m

3. log4j2 和 logback 版本
    

<log4j.version>2.22.1</log4j.version><logback.version>1.4.14</logback.version>

4. 测试线程数和测试方式
    

线程数:1 8 32 128测试方式:统一预热, 跑三次,取预热后的正式跑的平均值

5. 日志格式
日志格式对于 log 的效率会有非常大的影响, 有些时候则是天差地别。
<!-log4j2 的配置 --><Property name="log.pattern">[%d{yyyyMMdd HH:mm:ss.SSS}] [%t] [%level{length=4}] %c{1.}:%L %msg%n</Property><!-logback 的配置 --><pattern>[%date{yyyyMMdd HH:mm:ss.SSS}] [%thread] [%-4level] %logger{5}:%line %msg%n</pattern>
    

6. 日志长度

长度大约 129 个字符,常见长度 输出到文件 app.log, 格式统一。
    

[20240125 16:24:27.716] [thread-3] [INFO] c.w.d.Main:32 main - info level ...this is a demo script, pure string log will be used![20240125 16:24:27.716] [thread-1] [INFO] c.w.d.Main:32 main - info level ...this is a demo script, pure string log will be used!

三、 使用方法


3.1 logback 在 SpringBoot 项目中的使用


pom 文件, 不需要做任何事情, Spring 官方默认使用 logback, 非 Spring 项目可以直接引入下面的 XML, 同时包含 logback 和 slf4j。
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>${logback.version}</version></dependency>

配置文件放置位置: src/main/resource/logback.xml 样例如下:

   
<?xml version="1.0" encoding="UTF-8"?><configuration><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><encoder><pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern><charset>utf-8</charset></encoder><file>log/output.log</file><rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"><fileNamePattern>log/output.log.%i</fileNamePattern></rollingPolicy><triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"><MaxFileSize>1MB</MaxFileSize></triggeringPolicy></appender>
<root level="INFO"><appender-ref ref="CONSOLE" /><appender-ref ref="FILE" /></root></configuration>

3.2 log4j2 在 Spring 项目中的使用


由于 Spring 官方默认使用l ogback,因此我们需要对 Spring 默认的依赖进行排除然后再引入以下依赖:

   
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>${log4j.version}</version></dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>${log4j.version}</version></dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j2-impl</artifactId><version>${log4j.version}</version></dependency>

配置文件放置位置: src/main/resource/log4j2.xml 样例如下:

   
<?xml version="1.0" encoding="UTF-8"?><Configuration><Properties><!-- 定义日志格式 --><Property name="log.pattern">%d{MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36}%n%msg%n%n</Property><!-- 定义文件名变量 --><Property name="file.err.filename">log/err.log</Property><Property name="file.err.pattern">log/err.%i.log.gz</Property></Properties><!-- 定义Appender,即目的地 --><Appenders><!-- 定义输出到屏幕 --><Console name="console" target="SYSTEM_OUT"><!-- 日志格式引用上面定义的log.pattern --><PatternLayout pattern="${log.pattern}" /></Console><!-- 定义输出到文件,文件名引用上面定义的file.err.filename --><RollingFile name="err" bufferedIO="true" fileName="${file.err.filename}" filePattern="${file.err.pattern}"><PatternLayout pattern="${log.pattern}" /><Policies><!-- 根据文件大小自动切割日志 --><SizeBasedTriggeringPolicy size="1 MB" /></Policies><!-- 保留最近10份 --><DefaultRolloverStrategy max="10" /></RollingFile></Appenders><Loggers><Root level="info"><!-- 对info级别的日志,输出到console --><AppenderRef ref="console" level="info" /><!-- 对error级别的日志,输出到err,即上面定义的RollingFile --><AppenderRef ref="err" level="error" /></Root></Loggers></Configuration>

3.3 最佳实践


1. 滚动日志,永远不让磁盘满
根据运行环境要求, 配置最大日志数量与日志文件最大大小。
2. 日志如何使用才方便统计和定位问题
统一日志格式,比如统一先打印方法名称,再打印参数列表。写好要打印参数的  toString() 方法。
3. 日志如何配置性能才比较高
日志配置应该遵循结构清晰,尽量简化的原则,能不让框架计算的,尽量不让框架计算, 比如方法名、行号等。
4. 全公司或者个人使用习惯统一
这样有助于后续的日志收集、分析和统计。


四、测试代码


package com.winjeg.demo;

import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang3.concurrent.BasicThreadFactory;import org.slf4j.Logger;import org.slf4j.LoggerFactory;
import java.util.ArrayList;import java.util.List;import java.util.concurrent.*;
@Slf4jpublic class Main {
private static final Logger LOG = LoggerFactory.getLogger(Main.class);
private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(128, 256, 1L, TimeUnit.MINUTES, new ArrayBlockingQueue<>(512),new BasicThreadFactory.Builder().namingPattern("thread-%d").daemon(true).build());
public static void main(String[] args) {long start = System.currentTimeMillis(); execute(8, 160_000);long first = System.currentTimeMillis(); execute(8, 160_000); System.out.printf("time cost, preheat:%d\t, formal:%d\n", first - start, System.currentTimeMillis() - first); }
private static void execute(int threadNum, int times) { List<Future<?>> futures = new ArrayList<>();for (int i = 0; i < threadNum; i++) { Future<?> f = EXECUTOR.submit(() -> {for (long j = 0; j < times; j++) { log.info("main - info level ...this is a demo script, pure string log will be used!"); } }); futures.add(f); } futures.forEach(f -> {try { f.get(); } catch (InterruptedException | ExecutionException e) {throw new RuntimeException(e); } }); }}

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.winjeg.spring</groupId><artifactId>demo</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><log4j.version>2.22.1</log4j.version><logback.version>1.4.14</logback.version><java.version>1.8</java.version></properties>
<dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency>
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version></dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>${log4j.version}</version></dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>${log4j.version}</version></dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j2-impl</artifactId><version>${log4j.version}</version></dependency>
<!-- <dependency>--><!-- <groupId>ch.qos.logback</groupId>--><!-- <artifactId>logback-classic</artifactId>--><!-- <version>${logback.version}</version>--><!-- </dependency>--></dependencies></project>

参考文档
  • Log4J - Performance:https://logging.apache.org/log4j/2.x/performance.html
  • Benchmarking synchronous and asynchronous logging:https://logback.qos.ch/performance.html
  • Java日志框架:log4j vs logback vs log4j2:https://zhuanlan.zhihu.com/p/472941897


   
转自:winjeg,链接:juejin.cn/post/7327878308757520419


往期热门文章:

1、高逼格的SQL写法:行行比较2、限流算法哪家强?时间窗口,令牌桶与漏桶算法对比3、每天都提交代码,那你知道.git目录内部的秘密吗?4、我患上了空指针后遗症5、这10个小技巧让你减少80%的Bug!6、升级 JDK17 一个不可拒绝的理由7、SQL中为什么不要使用1=1?8、同事使用 insert into select 迁移数据,开开心心上线,导致公司损失近10w!9、拒绝写重复代码,试试这套开源的SpringBoot组件,效率翻倍~10、我原以为是个笑话,没想到深挖一下背后还有故事。错怪官方了...

相关推荐

  • CVPR 2024满分论文出炉!这些方向杀疯了!
  • 本科生连发两篇AI顶会后,再发大模型顶刊!
  • 一文吃透Transformer代码与应用【附226篇顶会论文】
  • APP如何与H5通信?
  • 超火超实用的 10 个前端工具库,可能就是你一直在寻找的~
  • AI浪潮,Spring也赶上了!?
  • 盘点历届 Java 语言的关键字,一定有你不认识的
  • 用 Lag-Llama 进行时间序列预测实战
  • 这算是裁到大动脉了吧
  • [开源]轻松构建车联网平台,可应用于各种车辆监管场景和应用平台
  • Kubernetes新手必看:快速生成YAML清单的终极指南!
  • 记一次疑似JVM内存泄漏的排查过程
  • 高中信息技术考试竟然有Flash、IIS、Frontpage、Access、VB……
  • 带您认识物联网首选协议MQTT
  • 29.3K Star强!集成微信登录,核心代码就10行
  • 下半年!真心建议大家冲一冲新兴领域,工资高前景好
  • 探索TypeScript的映射类型,从简单到高级的7个实例
  • 【第17讲】6月17日,AI代写(期刊、演讲稿、小说)
  • 茅台降价,“黄牛”公司纷纷跑路
  • 黄仁勋 · 加州理工2024届毕业典礼演讲 | 2024年6月14日(全文+视频)