log4j2
简介
log4j2 是 log4j 1.x 的升级版,2015 年 5 月,Apache 宣布 log4j1.x 停止更新, 最后版本为 1.2.17。
log4j2 参考了 logback 的一些优秀的设计,并且修复了一些问题,因此带来了一些重大的提升,主要有:
- 异常处理:在 logback 中,Appender 中的异常不会被应用感知到,但是在 log4j2 中,提供了一些异常处理机制
- 性能提升:log4j2 相较于 log4j 1 和 logback 都具有很明显的性能提升
- 自动重载配置:参考 logback 设计,支持自动刷新参数,可以动态修改日志级别,而不重启应用
- 无垃圾机制: 大部分情况下,都可以使用其无垃圾机制,避免频繁的日志收集导致的 jvm gc
版本
在官网的下载页 (https://logging.apache.org/log4j/2.x/download.html),有提示支持各 jdk 大版本的最后 log4j 版本
案例
创建项目
创建一个空的 maven 项目,结构如图:
配置项目
添加依赖
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
</dependency>
我这里使用的版本为 2.14.0
添加 log4j2 配置
在 src/main/resources 下,创建文件 log4j2.xml,注意名字不要变:
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="error" monitorinterval="5">
<!-- 先定义所有的 appender -->
<appenders>
<!-- 这个输出控制台的配置 -->
<Console name="Console" target="SYSTEM_OUT">
<!-- 控制台只输出 level 及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
<ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/>
<!-- 这个都知道是输出日志的格式 -->
<PatternLayout pattern="%d{HH🇲🇲ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</Console>
<span class="hljs-comment"><!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用 --></span>
<span class="hljs-comment"><!--append为TRUE表示消息增加到指定文件中,false表示消息覆盖指定的文件内容,默认值是true --></span>
<span class="hljs-tag"><<span class="hljs-name">File</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"log"</span> <span class="hljs-attr">fileName</span>=<span class="hljs-string">"D:/logs/log4j2.log"</span> <span class="hljs-attr">append</span>=<span class="hljs-string">"false"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">PatternLayout</span> <span class="hljs-attr">pattern</span>=<span class="hljs-string">"%d{HH🇲🇲ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"</span>/></span>
<span class="hljs-tag"></<span class="hljs-name">File</span>></span>
<span class="hljs-comment"><!--添加过滤器ThresholdFilter,可以有选择的输出某个级别以上的类别 onMatch="ACCEPT" onMismatch="DENY"意思是匹配就接受,否则直接拒绝 --></span>
<span class="hljs-tag"><<span class="hljs-name">File</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"ERROR"</span> <span class="hljs-attr">fileName</span>=<span class="hljs-string">"D:/logs/error.log"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">ThresholdFilter</span> <span class="hljs-attr">level</span>=<span class="hljs-string">"error"</span> <span class="hljs-attr">onMatch</span>=<span class="hljs-string">"ACCEPT"</span> <span class="hljs-attr">onMismatch</span>=<span class="hljs-string">"DENY"</span>/></span>
<span class="hljs-tag"><<span class="hljs-name">PatternLayout</span> <span class="hljs-attr">pattern</span>=<span class="hljs-string">"%d{yyyy.MM.dd 'at' HH🇲🇲ss z} %-5level %class{36} %L %M - %msg%xEx%n"</span>/></span>
<span class="hljs-tag"></<span class="hljs-name">File</span>></span>
<span class="hljs-comment"><!--这个会打印出所有的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 --></span>
<span class="hljs-tag"><<span class="hljs-name">RollingFile</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"RollingFile"</span> <span class="hljs-attr">fileName</span>=<span class="hljs-string">"D:/logs/web.log"</span>
<span class="hljs-attr">filePattern</span>=<span class="hljs-string">"logs/$${date:yyyy-MM}/web-%d{MM-dd-yyyy}-%i.log.gz"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">PatternLayout</span> <span class="hljs-attr">pattern</span>=<span class="hljs-string">"%d{yyyy-MM-dd 'at' HH🇲🇲ss z} %-5level %class{36} %L %M - %msg%xEx%n"</span>/></span>
<span class="hljs-tag"><<span class="hljs-name">SizeBasedTriggeringPolicy</span> <span class="hljs-attr">size</span>=<span class="hljs-string">"2MB"</span>/></span>
<span class="hljs-tag"></<span class="hljs-name">RollingFile</span>></span>
<span class="hljs-tag"></<span class="hljs-name">appenders</span>></span>
<span class="hljs-comment"><!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 --></span>
<span class="hljs-tag"><<span class="hljs-name">loggers</span>></span>
<span class="hljs-tag"><<span class="hljs-name">root</span> <span class="hljs-attr">level</span>=<span class="hljs-string">"trace"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">appender-ref</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"RollingFile"</span>/></span>
<span class="hljs-tag"><<span class="hljs-name">appender-ref</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"Console"</span>/></span>
<span class="hljs-tag"><<span class="hljs-name">appender-ref</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"ERROR"</span>/></span>
<span class="hljs-tag"><<span class="hljs-name">appender-ref</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"log"</span>/></span>
<span class="hljs-tag"></<span class="hljs-name">root</span>></span>
<span class="hljs-tag"></<span class="hljs-name">loggers</span>></span>
</configuration>
先这样使用,后面再做详解
创建测试类
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4j2Test {
private static Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {
<span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < <span class="hljs-number">3</span>; i++) {
<span class="hljs-comment">// trace level</span>
logger.trace(<span class="hljs-string">"trace log ..."</span>);
<span class="hljs-comment">// debug level</span>
logger.debug(<span class="hljs-string">"debug log ..."</span>);
<span class="hljs-comment">// info level</span>
logger.info(<span class="hljs-string">"info log ..."</span>);
<span class="hljs-comment">// error level</span>
logger.error(<span class="hljs-string">"error log ..."</span>);
}
}
}
运行测试
运行项目,控制台输出:
用法
简介
log4j 2.x 版本不再支持像 1.x 中的.properties 后缀的文件配置方式,2.x 版本常用.xml 后缀的文件进行配置,除此之外还包含.json 和.jsn 配置文件
log4j2 虽然采用 xml 风格进行配置,依然包含三个组件, 分别是 Logger(记录器)、Appender(输出目的地)、Layout(日志布局)。
配置文件
根节点
根节点 Configuration 有两个属性:
- status: 指定 log4j2 本身的日志级别, 包括 "trace", "debug", "info", "warn", "error" and "fatal"
- monitorinterval: log4j 2.x 的新特点,自动重载配置。指定自动重新配置的监测间隔时间,单位是 s, 最小是 5s
有两个子节点 (表明可以定义多个 Appender 和 Logger)
- Appenders: 输出源,用于定义日志输出的地方。
- Loggers: 日志器
Appenders 节点
详见官网: https://logging.apache.org/log4j/2.x/manual/appenders.html
log4j2 支持的输出源有很多,有控制台 Console、文件 File、RollingRandomAccessFile、MongoDB、Flume 等
- Console:控制台输出源是将日志打印到控制台上,开发的时候一般都会配置,以便调试
- File:文件输出源,用于将日志写入到指定的文件,需要配置输入到哪个位置(例如:D:/logs/mylog.log)
- RollingRandomAccessFile: 该输出源也是写入到文件,不同的是比 File 更加强大,可以指定当文件达到一定大小(如 20MB)时,另起一个文件继续写入日志,另起一个文件就涉及到新文件的名字命名规则,因此需要配置文件命名规则
这种方式更加实用,因为你不可能一直往一个文件中写,如果一直写,文件过大,打开就会卡死,也不便于查找日志。- fileName 指定当前日志文件的位置和文件名称
- filePattern 指定当发生 Rolling 时,文件的转移和重命名规则
- SizeBasedTriggeringPolicy 指定当文件体积大于 size 指定的值时,触发 Rolling
- DefaultRolloverStrategy 指定最多保存的文件个数
- TimeBasedTriggeringPolicy 这个配置需要和 filePattern 结合使用,注意 filePattern 中配置的文件重命名规则是 ${FILE_NAME}-%d{yyyy-MM-dd HH-mm}-%i,最小的时间粒度是 mm,即分钟
- TimeBasedTriggeringPolicy 指定的 size 是 1,结合起来就是每 1 分钟生成一个新文件。如果改成 %d{yyyy-MM-dd HH},最小粒度为小时,则每一个小时生成一个文件
- NoSql:MongoDb, 输出到 MongDb 数据库中
- Flume:输出到 Apache Flume(Flume 是 Cloudera 提供的一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统,Flume 支持在日志系统中定制各类数据发送方,用于收集数据;同时,Flume 提供对数据进行简单处理,并写到各种数据接受方(可定制)的能力。)
- Async:异步,需要通过 AppenderRef 来指定要对哪种输出源进行异步(一般用于配置 RollingRandomAccessFile)
常见的有三种子节点:
- Console: 定义输出到控制台的 Appender
- File: 定义输出到指定位置的文件的 Appender
- RollingFile: 定义超过指定大小自动删除旧的创建新的的 Appender
通过在子节点中加入进行日志布局
配置 | 含义 |
---|---|
%m | 输出代码指定信息,如 info(“message”), 输出 message |
%c | 输出所属类的全名, Num 为类名输出的范围 如 "com.sun.aaa.classB",%c{2} 将使日志输出输出范围为:aaa.classB |
%d | 输出日志时间其格式为 可指定格式 如 %d{HH🇲🇲ss.SSS}, 输出到毫秒 |
%-5level | 输出日志级别,-5 表示左对齐并且固定输出 5 个字符,如果不足在右边补齐空格 |
%L | 输出行号 |
%l | 输出日志事件发生位置,包括类名、方法名、文件名、行数 |
%F | 输出所在的类文件名,如 Log4j2Test.java |
%M | 输出所在方法名 |
%t | 输出产生该日志事件的线程名 |
%r | 输出从启动到显示该条日志信息所耗费的时间 (ms) |
%logger | 输出 logger 名称。Root Logger 因为没有名称,所以不会输出 |
%p | 输出日志的优先级,即 FATAL ,ERROR 等 |
%n | 换行符 |
Loggers 节点
日志器分根日志器 Root 和自定义日志器。
当根据日志名字 (Appender) 获取不到指定的日志器时,就使用 Root 作为默认的日志器。
- Root: 用来指定项目的根日志,如果没有单独指定 Logger,那么就会默认使用该 Root 日志输出
- Logger: 用来单独指定日志的形式,需要指定:
- Logger 的名称 (name)。对于命名可以以包名作为日志的名字,不同的包配置不同的级别等
- 日志级别 (level): TRACE, DEBUG, INFO, WARN, ERROR, ALL or OFF,不指定时 level 默认为 ERROR
- 相加性 (additivity):是否同时输出 log 到父类的 appender,缺省为 true。 对于一般的日志器(如 Console、File、RollingRandomAccessFile),一般需要配置一个或多个输出源 AppenderRef;