讲武德,你们要的高性能日志工具 Log4j2,来了
Log4j 介绍过了,SLF4J 介绍过了,Logback 也介绍过了,你以为日志系列的文章就到此终结了?
不不不,我告诉你,还有一个 Log4j 2,顾名思义,它就是 Log4j 的升级版,就好像手机里面的 Pro 版。我作为一个写文章方面的工具人,或者叫打工人,怎么能不写完这最后一篇。
Log4j、SLF4J、Logback 是一个爹——Ceki Gulcu,但 Log4j 2 却是例外,它是 Apache 基金会的产品。
SLF4J 和 Logback 作为 Log4j 的替代品,在很多方面都做了必要的改进,那为什么还需要 Log4j 2 呢?我只能说 Apache 基金会的开发人员很闲,不,很拼,要不是他们这种精益求精的精神,这个编程的世界该有多枯燥,毕竟少了很多可以用“拿来就用”的轮子啊。
上一篇也说了,老板下死命令要我把日志系统切换到 Logback,我顺利交差了,老板很开心,夸我这个打工人很敬业。为了表达对老板的这份感谢,我决定偷偷摸摸地试水一下 Log4j 2,尽管它还不是个成品,可能会会项目带来一定的隐患。但谁让咱是一个敬岗爱业的打工人呢。
01、Log4j 2 强在哪
1)在多线程场景下,Log4j 2 的吞吐量比 Logback 高出了 10 倍,延迟降低了几个数量级。这话听起来像吹牛,反正是 Log4j 2 官方自己吹的。
Log4j 2 的异步 Logger 使用的是无锁数据结构,而 Logback 和 Log4j 的异步 Logger 使用的是 ArrayBlockingQueue。对于阻塞队列,多线程应用程序在尝试使日志事件入队时通常会遇到锁争用。
下图说明了多线程方案中无锁数据结构对吞吐量的影响。 Log4j 2 随着线程数量的扩展而更好地扩展:具有更多线程的应用程序可以记录更多的日志。其他日志记录库由于存在锁竞争的关系,在记录更多线程时,总吞吐量保持恒定或下降。这意味着使用其他日志记录库,每个单独的线程将能够减少日志记录。
性能方面是 Log4j 2 的最大亮点,至于其他方面的一些优势,比如说下面这些,可以忽略不计,文字有多短就代表它有多不重要。
2)Log4j 2 可以减少垃圾收集器的压力。
3)支持 Lambda 表达式。
4)支持自动重载配置。
02、Log4j 2 使用示例
废话不多说,直接实操开干。理论知识有用,但不如上手实操一把,这也是我多年养成的一个“不那么良好”的编程习惯:在实操中发现问题,解决问题,寻找理论基础。
第一步,在 pom.xml 文件中添加 Log4j 2 的依赖:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.5</version>
</dependency>
(这个 artifactId 还是 log4j,没有体现出来 2,而在 version 中体现,多少叫人误以为是 log4j)
第二步,来个最简单的测试用例:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Demo {
private static final Logger logger = LogManager.getLogger(Demo.class);
public static void main(String[] args) {
logger.debug("log4j2");
}
}
运行 Demo 类,可以在控制台看到以下信息:
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
Log4j 2 竟然没有在控制台打印“ log4j2”,还抱怨我们没有为它指定配置文件。在这一点上,我就觉得它没有 Logback 好,毕竟人家会输出。
这对于新手来说,很不友好,因为新手在遇到这种情况的时候,往往不知所措。日志里面虽然体现了 ERROR,但代码并没有编译出错或者运行出错,凭什么你不输出?
那作为编程老鸟来说,我得告诉你,这时候最好探究一下为什么。怎么做呢?
我们可以复制一下日志信息中的关键字,比如说:“No log4j2 configuration file found”,然后在 Intellij IDEA 中搜一下,如果你下载了源码和文档的话,不除意外,你会在 ConfigurationFactory 类中搜到这段话。
可以在方法中打个断点,然后 debug 一下,你就会看到下图中的内容。
通过源码,你可以看得到,Log4j 2 会去寻找 4 种类型的配置文件,后缀分别是 properties、yaml、json 和 xml。前缀是 log4j2-test 或者 log4j2。
得到这个提示后,就可以进行第三步了。
第三步,在 resource 目录下增加 log4j2-test.xml 文件(方便和 Logback 做对比),内容如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="DEBUG">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
Log4j 2 的配置文件格式和 Logback 有点相似,基本的结构为 < Configuration>
元素,包含 0 或多个 < Appenders>
元素,其后跟 0 或多个 < Loggers>
元素,里面再跟最多只能存在一个的 < Root>
元素。
1)配置 appender,也就是配置日志的输出目的地。
有 Console,典型的控制台配置信息上面你也看到了,我来简单解释一下里面 pattern 的格式:
%d{HH:mm:ss.SSS}
表示输出到毫秒的时间%t
输出当前线程名称%-5level
输出日志级别,-5 表示左对齐并且固定输出 5 个字符,如果不足在右边补空格%logger
输出 logger 名称,最多 36 个字符%msg
日志文本%n
换行
顺带补充一下其他常用的占位符:
%F
输出所在的类文件名,如 Demo.java%L
输出行号%M
输出所在方法名%l
输出语句所在的行数, 包括类名、方法名、文件名、行数%p
输出日志级别%c
输出包名,如果后面跟有{length.}
参数,比如说%c{1.}
,它将输出报名的第一个字符,如com.itwanger
的实际报名将只输出c.i
再次运行 Demo 类,就可以在控制台看到打印的日志信息了:
10:14:04.657 [main] DEBUG com.itwanger.Demo - log4j2
2)配置 Loggers,指定 Root 的日志级别,并且指定具体启用哪一个 Appenders。
3)自动重载配置。
Logback 支持自动重载配置,Log4j 2 也支持,那想要启用这个功能也非常简单,只需要在 Configuration 元素上添加 monitorInterval
属性即可。
<Configuration monitorInterval="30">
...
</Configuration>
注意值要设置成非零,上例中的意思是至少 30 秒后检查配置文件中的更改。最小间隔为 5 秒。
03、Async 示例
除了 Console,还有 Async,可以配合文件的方式来异步写入,典型的配置信息如下所示:
<Configuration>
<Appenders>
<File name="DebugFile" fileName="debug.log">
<PatternLayout>
<Pattern>%d %p %c [%t] %m%n</Pattern>
</PatternLayout>
</File>
<Async name="Async">
<AppenderRef ref="DebugFile"/>
</Async>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Async"/>
</Root>
</Loggers>
</Configuration>
对比 Logback 的配置文件来看,Log4j 2 真的复杂了一些,不太好用,就这么直白地说吧!但自己约的,含着泪也得打完啊。把这个 Async 加入到 Appenders:
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<File name="DebugFile" fileName="debug.log">
<PatternLayout>
<Pattern>%d %p %c [%t] %m%n</Pattern>
</PatternLayout>
</File>
<Async name="Async">
<AppenderRef ref="DebugFile"/>
</Async>
</Appenders>
<Loggers>
<Root level="DEBUG">
<AppenderRef ref="Console"/>
<AppenderRef ref="Async"/>
</Root>
</Loggers>
</Configuration>
再次运行 Demo 类,可以在项目根路径下看到一个 debug.log 文件,内容如下所示:
2020-10-30 09:35:49,705 DEBUG com.itwanger.Demo [main] log4j2
04、RollingFile 示例
当然了,Log4j 和 Logback 我们都配置了 RollingFile,Log4j 2 也少不了。RollingFile 会根据 Triggering(触发)策略和 Rollover(过渡)策略来进行日志文件滚动。如果没有配置 Rollover,则使用 DefaultRolloverStrategy 来作为 RollingFile 的默认配置。
触发策略包含有,基于 cron 表达式(源于希腊语,时间的意思,用来配置定期执行任务的时间格式)的 CronTriggeringPolicy;基于文件大小的 SizeBasedTriggeringPolicy;基于时间的 TimeBasedTriggeringPolicy。
过渡策略包含有,默认的过渡策略 DefaultRolloverStrategy,直接写入的 DirectWriteRolloverStrategy。一般情况下,采用默认的过渡策略即可,它已经足够强大。
来看第一个基于 SizeBasedTriggeringPolicy 和 TimeBasedTriggeringPolicy 策略,以及缺省 DefaultRolloverStrategy 策略的配置示例:
<Configuration>
<Appenders>
<RollingFile name="RollingFile" fileName="rolling.log"
filePattern="rolling-%d{yyyy-MM-dd}-%i.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="1 KB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="RollingFile"/>
</Root>
</Loggers>
</Configuration>
为了验证文件的滚动策略,我们调整一下 Demo 类,让它多打印点日志:
for (int i = 1;i < 20; i++) {
logger.debug("微信搜索「{}」,回复关键字「{}」,有惊喜哦","沉默王二", "java");
}
再次运行 Demo 类,可以看到根目录下多了 3 个日志文件:
结合日志文件名,再来看 RollingFile 的配置,就很容易理解了。
1)fileName 用来指定文件名。
2)filePattern 用来指定文件名的模式,它取决于过渡策略。
由于配置文件中没有显式指定过渡策略,因此 RollingFile 会启用默认的 DefaultRolloverStrategy。
先来看一下 DefaultRolloverStrategy 的属性:
再来看 filePattern 的值 rolling-%d{yyyy-MM-dd}-%i.log
,其中 %d{yyyy-MM-dd}
很好理解,就是年月日;其中 %i
是什么意思呢?
第一个日志文件名为 rolling.log(最近的日志放在这个里面),第二个文件名除去日期为 rolling-1.log,第二个文件名除去日期为 rolling-2.log,根据这些信息,你能猜到其中的规律吗?
其实和 DefaultRolloverStrategy 中的 max 属性有关,目前使用的默认值,也就是 7,那就当 rolling-8.log 要生成的时候,删除 rolling-1.log。可以调整 Demo 中的日志输出量来进行验证。
3)SizeBasedTriggeringPolicy,基于日志文件大小的时间策略,大小以字节为单位,后缀可以是 KB,MB 或 GB,例如 20 MB。
再来看一个日志文件压缩的示例,来看配置:
<RollingFile name="RollingFileGZ" fileName="gz/rolling.log"
filePattern="gz/%d{yyyy-MM-dd-HH}-%i.rolling.gz">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="1 KB"/>
</Policies>
</RollingFile>
fileName 的属性值中包含了一个目录 gz,也就是说日志文件都将放在这个目录下。
filePattern 的属性值中增加了一个 gz 的后缀,这就表明日志文件要进行压缩了,还可以是 zip 格式。
运行 Demo 后,可以在 gz 目录下看到以下文件:
到此为止,Log4j 2 的基本使用示例就已经完成了。测试环境搞定,我去问一下老板,要不要在生产环境下使用 Log4j 2。
04、日志手册
到此为止,日志系统的全家桶 Log4j、SLF4J、Logback、Log4j 2 都被我搞定了。也就意味着,一份将近 2 万字的 PDF 诞生了!MD,我也要成为肝帝!
有了这份 PDF,硬气地告诉 Leader 或者老板吧,再也不用担心代码中会乱打印日志了。墙都不扶就服你。
地址我贴一下:
偷偷地告诉你吧,白嫖的感觉就是舒服,赶紧去下载吧!顺带点个赞,满足一下我的虚荣心,毕竟这是你们赐给我的最强的写作动力。
讲武德,你们要的高性能日志工具 Log4j2,来了的更多相关文章
- IntelliJ IDEA 2020.3正式发布,年度最后一个版本很讲武德
仰不愧天,俯不愧人,内不愧心.关注公众号[BAT的乌托邦],有Spring技术栈.MyBatis.JVM.中间件等小而美的原创专栏供以免费学习.分享.成长,拒绝浅尝辄止.本文已被 https://ww ...
- 【腾讯Bugly干货分享】微信mars 的高性能日志模块 xlog
本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/581c2c46bef1702a2db3ae53 Dev Club 是一个交流移动 ...
- .NET日志工具介绍
最近项目需要一个日志工具来跟踪程序便于调试和测试,为此研究了一下.NET日志工具,本文介绍了一些主流的日志框架并进行了对比.发表出来与大家分享. 综述 所谓日志(这里指程序日志)就是用于记录程序执行过 ...
- Android日志工具的使用
一.使用Android的日志工具Log 1.Android中的日志工具类是Log,这个类中提供了如下5个方法来供我们打印日志. log.v():用于打印哪些最为繁琐.意义最小的日志信息.对应级别ver ...
- 日志工具全面理解及配置应用---以Log4j例子
一.日志系统基本常识 1.日志系统作用:将日志信息输出到控制台和文本文件,以追踪代码运行信息. 2.日志系统操作的是什么?日志系统打印信息,也是调用日志系统的log.Info(),log.Warn() ...
- .NetCore中的日志(2)集成第三方日志工具
.NetCore中的日志(2)集成第三方日志工具 0x00 在.NetCore的Logging组件中集成NLog 上一篇讨论了.NetCore中日志框架的结构,这一篇讨论一下.NetCore的Logg ...
- 细说Java主流日志工具库
概述 在项目开发中,为了跟踪代码的运行情况,常常要使用日志来记录信息. 在Java世界,有很多的日志工具库来实现日志功能,避免了我们重复造轮子. 我们先来逐一了解一下主流日志工具. java.util ...
- Java主流日志工具库
在项目开发中,为了跟踪代码的运行情况,常常要使用日志来记录信息.在Java世界,有很多的日志工具库来实现日志功能,避免了我们重复造轮子.我们先来逐一了解一下主流日志工具. 1.java.util.lo ...
- 【工具推荐】ELMAH——可插拔错误日志工具
今天看到一篇文章(构建ASP.NET网站十大必备工具(2)),里面介绍了一个ELMAH的错误日志工具,于是研究了一下. ELMAH 是 Error Logging Modules and Handle ...
随机推荐
- centos平台scp通过密钥远程复制文件(免密登录)
一,说明:两台机器的平台和ip 1,a服务器: centos8:ip:121.122.123.47 版本 [root@yjweb ~]# cat /etc/redhat-release CentOS ...
- 操作系统OS基础
OS: Operating System 操作系统,通用目的的软件程序 主要功能:硬件驱动 进程管理 内存管理 网络管理 安全管理 文件管理OS分类:服务器OS:CentOS,Ubuntu,Windo ...
- vue知识点12
知识点归纳整理如下: 1. 数组用下标改变,或者对象增加属性,这样的改变数据 是不能触发视图更新的,要用 Vue.set(对象,属性,值) 或this.$set(对象,属性,值) 2. this. ...
- mysql优化篇(基于索引)
在上一篇文章:Mysql索引(一篇就够le) 中介绍了索引的基本使用,分类和原理,也强烈建议先读Mysql索引(一篇就够le),然后继续本文的阅读 我们也知道mysql的优化可以从很多的方面进行,比如 ...
- (静默安装)Cent OS 6_5(x86_64)下安装Oracle 11g
Cent OS 6_5(x86_64)下安装Oracle 11g 1 硬件要求 1.1 内存 & swap 物理内存不少于1G 硬盘可以空间不少于5G swap分区空间不少于2G Mini ...
- mybatis 架构及基础模块
1. mybatis整体架构 基础支撑层详解 1.日志模块 mybatis日志模块没有实现类,需要接入第三方的组件,问题是第三方的组件有各自的log级别,为了能接入第三方组件,mybati日志模块定义 ...
- Redis基础(一)数据结构与数据类型
Redis数据结构 Redis一共有六种数据结构,分别是简单动态字符串.链表.字典.跳表.整数集合.压缩列表. 简单动态字符串(SDS) Redis只会使用C字符串作为字面量,在大多数情况下,Redi ...
- c#封装ActiveX接口实践分析
ActiveX接口 是一个开放的集成平台,为开发人员.用户和 Web生产商提供了一个快速而简便的在 Internet 和 Intranet 创建程序集成和内容的方法. 使用 ActiveX, 可轻松方 ...
- Qlik Sense学习笔记之插件开发
date: 2019-05-06 13:18:45 updated: 2019-08-09 15:18:45 Qlik Sense学习笔记之插件开发 1.开发前的基础工作 1.1 新建插件 dev-h ...
- Java数据结构-00导论
一个程序是怎样组成的呢?数据结构+算法=程序 一.什么是数据结构: 简单定义就是研究数据的存储方式:选择适当的数据结构可以提高计算机程序的运行效率(时间复杂度O)和存储效率(空间复杂度S). 二.数据 ...