@Otokaze
2018-11-28T12:21:49.000000Z
字数 7171
阅读 683
Java
Apache Log4j 曾经是最流行的 Java 日志组件,它有两个大版本:1.x 和 2.x。log4j 1.x 早在 2012 就已经停止更新,Apache 在此之后憋了好久,然后开发出了 log4j 2.x(借鉴了不少 logback 的新理念),憋了这么久开发出来的 2.x 版本当然是很优秀的,据说比 log4j 1.x 和 logback 的性能要高出好几倍(异步)。
之前我们学习了 logback 和 slf4j,logback 和 log4j 1.x 的作者其实是同一个人,这位大佬之所以会选择开发一个新的日志框架(logback),听说是因为与 Apache 存在意见分歧,具体什么矛盾我也没仔细研究过。
SLF4J 是一个日志门面框架,而 Logback 是一个日志组件,可以将它们简单的理解为 接口 与 实现类。之所以要抽象出一个 slf4j 门面,是为了提高程序的可移植性和可定制性,使用了 slf4j 之后,如果我想换过一个底层的日志组件,比如从 logback 换为 log4j 2.x,我们只需要更换 jar 包,然后使用新的配置文件就行,而我们的程序代码是不需要做任何更改的,这符合开闭原则(对扩展开放,对修改关闭)。
而 Apache 其实也有一个日志门面框架,叫做 Jakarta Commons Logging (JCL),但是很不幸,JCL 和 log4j 1.x 都死得比较早,jcl 在 2014 年就停止更新了,而 log4j 1.x 在 2012 年停止更新。
虽然 log4j 1.x 已经停止更新这么长时间了,但是 log4j 的大名恐怕每个 Java 程序员都有所耳闻,并且目前还有很多应用都还在使用 log4j 1.x,比如 Apache Tomcat。甚至还有新的应用继续选择使用 log4j 1.x。
对于 log4j 2.x,目前暂时没有学习的计划,鉴于 log4j 1.x 还有这么多项目在用,所以本文也是主要介绍 log4j 1.2(2012 年发布的最后一个版本,使用 properties 属性文件作为配置文件格式)。
和大多数日志组件一样,log4j 也由三个主要组件:Logger、Appender、Layout:
Logger 的层次结构
与 logback 意义,log4j 的 logger 之间也是存在层次结构的,并且它们的机构是相似的,都是按照 java 全限定类名的格式来定义父级 logger 和子级 logger,比如名为 com.zfl9
的 logger 是名为 com.zfl9.util
的 logger 的直接父级,反之,com.zfl9.util 是 com.zfl9 的直接子级。
这个树状层次结构中,有一个根节点,那就是 root logger,根记录器,就好比 Java 中的所有类的祖先类是 Object 类一样。我们可以通过 org.apache.log4j.Logger.getRootLogger()
方法来获取 root logger。
Logger 类的基本方法:
package org.apache.log4j;
public class Logger {
// Creation & retrieval methods:
public static Logger getRootLogger();
public static Logger getLogger(String name);
public static Logger getLogger(Class<?> clazz);
// printing methods:
public void trace(Object message);
public void debug(Object message);
public void info(Object message);
public void warn(Object message);
public void error(Object message);
public void fatal(Object message);
// generic printing method:
public void log(Level l, Object message);
}
因为 logger 的名称是有意义的,并且存在层级关系,所以一个最佳实践是,使用当前类的全限定类名作为 logger 的名称,logger 的静态工厂方法提供了一个便捷方法,允许我们直接传递 java.lang.Class 对象,logger 内部会自动使用 Class 对象的 name 作为 logger 的 name,即全限定类名作为 logger 的名称。
logger 提供 6 个级别的日志记录方法(级别由低到高):
其中 trace 和 fatal 不建议使用,所以常用的 4 个级别为:debug、info、warn、error。
与 logback 一样,log4j 的 logger 层级关系当然是有特殊用途的,不然单纯定义一个层级关系没有实际意义,这个意义就是,父 logger 上的 appender 会被子 logger 所继承。而 root logger 是最顶层的 logger,所以 root logger 上的 appender 会被所有 logger 所继承(除非明确指定不继承)。
其实除了 log4j 的配置文件格式与 logback 不同之外,其他的概念都是相辅相成的,毕竟是同一人写的。
和 logback 不同,log4j 没有所谓的默认配置,所以如果你没有提供配置文件,log4j 在运行时会报错。log4j 的配置文件格式为 *.properties java 属性文件,所谓属性文件就是一行一个 name = value 键值对而已,格式非常简单,和我们通过 JVM 运行参数 -Dprop.name=prop.value
指定是一样的。
log4j 的默认配置文件名为 log4j.properties
,在运行时,log4j 会在 class path 路径中查找此文件。
root logger 配置
log4j.rootLogger = [level], appenderName1, appenderName2, ...
# level 为日志记录级别,取值 OFF|TRACE|DEBUG|INFO|WARN|ERROR|FATAL|ALL
# Log4j 建议只使用四个级别,优先级从低到高分别是 DEBUG, INFO, WARN, ERROR
appender 配置
log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.optionN = valueN
#
# Log4j 提供的 appender 有以下几种:
# - org.apache.log4j.ConsoleAppender 输出至控制台
# - org.apache.log4j.FileAppender 输出至磁盘文件
# - org.apache.log4j.DailyRollingFileAppender 日志文件按日期轮转
# - org.apache.log4j.RollingFileAppender 日志文件按大小轮转
#
# ConsoleAppender 属性
# - Threshold = DEBUG 指定日志消息的有效记录级别
# - ImmediateFlush = true 默认为 true,所有的消息都会被立即输出
# - Target = System.err 默认为 System.out,输出到 STDOUT/STDERR
#
# FileAppender 属性
# - Threshold = INFO 指定日志消息的有效日志级别
# - ImmediateFlush = true 默认为 true,所有的消息都会被立即输出
# - File = C:\log4j.log 指定消息输出到 'C:\log4j.log' 文件路径
# - Append = false 默认为 true,追加到源文件,而非覆盖源文件
# - Encoding = UTF-8 可以指定文件编码格式,建议使用 UTF-8 编码
#
# DailyRollingFileAppender 属性
# - Threshold = WARN 指定日志消息的有效日志级别
# - ImmediateFlush = true 默认为 true,所有的消息都会被立即输出
# - File = C:\log4j.log 指定消息输出到 'C:\log4j.log' 文件路径
# - Append = false 默认为 true,追加到源文件,而非覆盖原文件
# - Encoding = UTF-8 可以指定文件编码格式,建议使用 UTF-8 编码
# - DatePattern=$PATTERN 轮转周期,这个 pattern 是已归档文件的后缀
# - '.'yyyy-MM 每月
# - '.'yyyy-ww 每周
# - '.'yyyy-MM-dd 每天
# - '.'yyyy-MM-dd-a 每半天
# - '.'yyyy-MM-dd-HH 每小时
# - '.'yyyy-MM-dd-HH-mm 每分钟
#
# RollingFileAppender 属性
# - Threshold = WARN 指定日志消息的有效日志级别
# - ImmediateFlush = true 默认为 true,所有的消息都会被立即输出
# - File = C:\log4j.log 指定消息输出到 'C:\log4j.log' 文件路径
# - Append = false 默认为 true,追加到源文件,而非覆盖原文件
# - Encoding = UTF-8 可以指定文件编码格式,建议使用 UTF-8 编码
# - MaxFileSize = 100KB 单个日志文件的最大大小,可用单位:KB,MB,GB
# - MaxBackupIndex = 10 要保留的已归档日志文件的数量,超过的将被删除
layout 配置
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.optionN = valueN
#
# Log4j 提供的 layout 有以下几种:
# - org.apache.log4j.PatternLayout
#
# PatternLayout 属性
# - ConversionPattern = %m%n 格式字符串
#
# %m 日志消息
# %p 日志级别
# %c 记录器名称
# %t 当前线程名
# %d 当前的时间,默认 `%d{yyyy-MM-dd HH:mm:ss,SSS}`
# %n 一个换行符
# %% 一个百分号
#
# 在 % 与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式:
# %5c 输出 logger 名称,最小宽度是 5,右对齐
# %-5c 输出 logger 名称,最小宽度是 5,左对齐
# %.5c 输出 logger 名称,最大宽度是 5,会截断
pom.xml
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
log4j.properties(普通)
log4j.rootLogger = TRACE, STDOUT, FILE
log4j.appender.STDOUT = org.apache.log4j.ConsoleAppender
log4j.appender.STDOUT.Threshold = TRACE
log4j.appender.STDOUT.ImmediateFlush = true
log4j.appender.STDOUT.Target = System.out
log4j.appender.STDOUT.layout = org.apache.log4j.PatternLayout
log4j.appender.STDOUT.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss.SSS} [%-5p] %c - %m%n
log4j.appender.FILE = org.apache.log4j.FileAppender
log4j.appender.FILE.Threshold = TRACE
log4j.appender.FILE.ImmediateFlush = true
log4j.appender.FILE.File = D:/TEMP/log4j.log
log4j.appender.FILE.Append = true
log4j.appender.FIlE.Encoding = UTF-8
log4j.appender.FILE.layout = org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss.SSS} [%-5p] %c - %m%n
Main.java
package com.zfl9;
import org.apache.log4j.Logger;
public class Main {
public static void main(String[] args) {
Logger logger = Logger.getLogger(Main.class);
logger.trace("trace message");
logger.debug("debug message");
logger.info("info message");
logger.warn("warn message");
logger.error("error message");
logger.fatal("fatal message");
}
}
运行结果:
2018-11-28 12:08:28.758 [TRACE] com.zfl9.Main - trace message
2018-11-28 12:08:28.759 [DEBUG] com.zfl9.Main - debug message
2018-11-28 12:08:28.759 [INFO ] com.zfl9.Main - info message
2018-11-28 12:08:28.759 [WARN ] com.zfl9.Main - warn message
2018-11-28 12:08:28.759 [ERROR] com.zfl9.Main - error message
2018-11-28 12:08:29.046 [FATAL] com.zfl9.Main - fatal message
日志文件:
2018-11-28 12:08:28.758 [TRACE] com.zfl9.Main - trace message
2018-11-28 12:08:28.759 [DEBUG] com.zfl9.Main - debug message
2018-11-28 12:08:28.759 [INFO ] com.zfl9.Main - info message
2018-11-28 12:08:28.759 [WARN ] com.zfl9.Main - warn message
2018-11-28 12:08:28.759 [ERROR] com.zfl9.Main - error message
2018-11-28 12:08:29.046 [FATAL] com.zfl9.Main - fatal message
日志轮转
log4j.properties(轮转)
log4j.rootLogger = TRACE, STDOUT, FILE
log4j.appender.STDOUT = org.apache.log4j.ConsoleAppender
log4j.appender.STDOUT.Threshold = TRACE
log4j.appender.STDOUT.ImmediateFlush = true
log4j.appender.STDOUT.Target = System.out
log4j.appender.STDOUT.layout = org.apache.log4j.PatternLayout
log4j.appender.STDOUT.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss.SSS} [%-5p] %c - %m%n
log4j.appender.FILE = org.apache.log4j.DailyRollingFileAppender
log4j.appender.FILE.Threshold = TRACE
log4j.appender.FILE.ImmediateFlush = true
log4j.appender.FILE.File = D:/TEMP/log4j.log
log4j.appender.FILE.Append = true
log4j.appender.FIlE.Encoding = UTF-8
log4j.appender.FILE.DatePattern = '.'yyyy-MM-dd-HH-mm-ss
log4j.appender.FILE.layout = org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss.SSS} [%-5p] %c - %m%n
产生的日志文件名:
log4j.log
log4j.log.2018-11-28-12-15-01
log4j.log.2018-11-28-12-16-00
当前活动的文件为 log4j.log,已归档的文件就是后面两个。