一、简介
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX SYSlog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
Log4j 2 官网:https://logging.apache.org/log4j/2.x
二、背景
Log4j的1.x版本已经被广泛使用于很多应用程序中。然而,由于需要严格遵循很老的Java版本,使它变得越来越难以维护,终在2015年8月寿终正寝,最新版为1.2.17。它的替代品,SLF4J和Logback对框架做了很多必要的改进。那么为什么还要费心去做Log4j 2呢?几个原因如下:
- Log4j 2被设计为可以作为审计框架使用。Log4j 1.x和Logback都会在重新配置的时候失去事件,而Log4j2不会。在Logback中,Appender当中的异常对应用从来都是不可见的。但Log4j2的Appender可以设置为允许将异常渗透给应用程序。
- Log4j 2包含基于LMAX Disruptor库的下一代异步日志器。在多线程情况下,异步日志器具有比Log4j 1.x和Logback高出10倍的吞吐性能以及更低的延迟。
- Log4j 2在稳定记录状态下,对单机应用是无垃圾的,对Web应用是低垃圾的。这不仅降低了垃圾回收器的压力,还可以提供更好的响应性能。
- Log4j 2使用插件系统使得它非常容易通过新的Appender、Filter、Layout、Lookup和Pattern Converter来扩展框架,且不需要对Log4j做任何修改。
- 由于插件系统的配置更简单了,配置项不需要声明类名称。
- 支持自定义日志级别。自定义日志级别可以在代码或配置中定义。
- 支持Lambda表达式。运行在Java 8上的客户端代码可以使用Lambda表达式来实现仅在对应的日志级别启用时延迟构造日志消息。由于不需要明确地层层把关,这带来了更简洁的代码。
- 支持Message对象。Message允许支持感兴趣或复杂的结构体在日志系统中传输,且可以被高效地操作。用户可以自由地创建他们自己的Message类型,并编写自定义的Layout、Filter和Lookup来操作它们。
- Log4j 1.x支持Appender上的Filter。Logback引入了TurboFilter来在事件被Logger处理之前对它们进行过滤。Log4j 2支持的Filter可以设置为在被Logger接管之前即处理事件,如同它在Logger或Appender中被处理。
- 很多Logback的Appender不接受一个Layout,且只能发送固定格式的数据。而大多数Log4j 2的Appender接受Layout,允许数据以任意一种所需的格式传输。
- Log4j 1.x和Logback中的Layout返回一个String。这导致了在Logback Encoder中讨论的问题。Log4j 2用更简单的方法,Layout总是返回一个字节数组。优点是这意味着它们可以用于任何Appender,而不仅仅是写入到OutputStream中的那些。
- Syslog Appender既支持TCP也支持UDP,同样支持BSD系统日志以及RFC 5424格式。
- Log4j 2利用了Java 5的并发优势,并在尽可能最低的程度上进行锁定。Log4j 1.x中已知存在死锁问题。其中很多已经在Logback中修复,但很多Logback的class文件仍然需要在更高的编译级别中同步。
- 这是一个被所有ASF项目集体支持使用的Apache软件基金会项目。如果你想要贡献或修改,只要参照贡献中的方法。
三、配置
1.log4j2配置方法
- 通过一个XML或JSON格式的配置文件
- 以编程方式,通过创建一个ConfigurationFactory工厂和Configuration实现
- 以编程方式,通过调用API暴露在配置界面添加组件的默认配置
- 以编程方式,通过调用Logger内部类上的方法
2.配置文件及加载流程
log4j 2.x版本不再支持像1.x中的.properties后缀的文件配置方式,2.x版本配置文件后缀名只能为".xml",".json"或者".jsn".系统选择配置文件的优先级(从先到后)如下:
- .classpath下的名为log4j2-test.json 或者log4j2-test.jsn的文件.
- .classpath下的名为log4j2-test.xml的文件.
- .classpath下名为log4j2.json 或者log4j2.jsn的文件.
- .classpath下名为log4j2.xml的文件.
一般默认使用log4j2.xml进行命名。如果本地要测试,可以把log4j2-test.xml放到classpath,而正式环境使用log4j2.xml,则在打包部署的时候不要打包log4j2-test.xml即可。
3.默认配置
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<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="error">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
4.配置文件节点解析
- 根节点Configuration有两个属性:status和monitorinterval,两个子节点:Appenders和Loggers
- status用来指定log4j本身的打印日志的级别
- monitorinterval用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s
- Appenders节点,常见的有三种子节点:Console、RollingFile、File
- Console节点用来定义输出到控制台的Appender
- name:指定Appender的名字
- target:SYSTEM_OUT 或 SYSTEM_ERR,一般只设置默认:SYSTEM_OUT
- PatternLayout:输出格式,不设置默认为:%m%n
- File节点用来定义输出到指定位置的文件的Appender
- name:指定Appender的名字
- fileName:指定输出日志的目的文件带全路径的文件名
- PatternLayout:输出格式,不设置默认为:%m%n
- RollingFile节点用来定义超过指定大小自动删除旧的创建新的的Appender
- name:指定Appender的名字
- fileName:指定输出日志的目的文件带全路径的文件名
- PatternLayout:输出格式,不设置默认为:%m%n
- filePattern:指定新建日志文件的名称格式
- Policies:指定滚动日志的策略,就是什么时候进行新建日志文件输出日志
- 时间滚动策略:TimeBasedTriggeringPolicy,interval属性用来指定多久滚动一次,默认是1 hour,modulate=true用来调整时间,比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am,12am...而不是7am
- 文件大小滚动策略:SizeBasedTriggeringPolicy,size属性用来定义每个日志文件的大小
- DefaultRolloverStrategy:用来指定同一个文件夹下最多有几个日志文件时开始删除最旧的,创建新的(通过max属性)
- Loggers节点,常见的子节点有两种:Root和Logger
- Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出
- Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等,name属性用来指定该Logger所适用的类或者类所在的包全路径
- AppenderRef节点,用来指定该日志输出到哪个Appender,如果没有指定,就会默认继承自Root,如果指定了,那么会在指定的这个Appender和Root下的Appender中都输出,此时可以设置Logger的additivity="false",只在自定义的Appender中进行输出
5.日志level
共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF
- All:最低等级的,用于打开所有日志记录
- Trace:是追踪,程序推进跟踪,一般不用
- Debug:指出细粒度信息事件对调试应用程序是非常有帮助的
- Info:消息在粗粒度级别上突出强调应用程序的运行过程
- Warn:输出警告及warn以下级别的日志
- Error:输出错误信息日志
- Fatal:输出每个严重的错误事件将会导致应用程序的退出的日志
- OFF:最高等级的,用于关闭所有日志记录
6.比较完整的log4j2.xml配置模板
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!-- Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出 -->
<!-- monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数 -->
<Configuration status="WARN" monitorInterval="30">
<Properties>
<Property name="baseDir">${sys:user.dir}/logs</Property>
</Properties>
<!-- 先定义所有的appender -->
<Appenders>
<!-- 这个输出控制台的配置 -->
<Console name="Console" target="SYSTEM_OUT">
<!-- 输出日志的格式 -->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"></PatternLayout>
</Console>
<!-- 打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用 -->
<File name="log" fileName="${baseDir}/test.log" append="false">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"></PatternLayout>
</File>
<!-- 打印出所有的info及以上级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 -->
<RollingFile name="RollingFileInfo" fileName="${baseDir}/info.log"
filePattern="${baseDir}/%d{yyyyMMdd}/info-%i.log.zip">
<!-- 控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"></ThresholdFilter>
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %l %-5p - %msg%xEx%n"></PatternLayout>
<Policies>
<!-- 设置每天切换日志一次 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"></TimeBasedTriggeringPolicy>
<!-- 设置日志文件满50MB后切换 -->
<SizeBasedTriggeringPolicy size="50 MB"></SizeBasedTriggeringPolicy>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
<DefaultRolloverStrategy max="20">
<!-- 配置说明:
* 只处理位于${baseDir}文件夹下的文件
* 只处理以 .log.zip 结尾的文件 (name match)
* 只处理最后一次修改时间超过7天以内的文件
-->
<Delete basePath="${baseDir}" maxDepth="1">
<IfFileName glob="*.log.zip" />
<IfLastModified age="7d" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
<RollingFile name="RollingFileError" fileName="${baseDir}/error.log"
filePattern="${baseDir}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"></ThresholdFilter>
<PatternLayout pattern="%d{HH:mm:ss.SSS} - %p - %l - %m%n"></PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="50 MB"></SizeBasedTriggeringPolicy>
</Policies>
</RollingFile>
</Appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 -->
<Loggers>
<!--过滤掉Spring和MyBatis的一些无用的DEBUG信息 -->
<logger name="org.springframework" level="INFO"></logger>
<logger name="org.mybatis" level="INFO"></logger>
<root level="all">
<appender-ref ref="Console"></appender-ref>
<appender-ref ref="RollingFileInfo"></appender-ref>
<appender-ref ref="RollingFileError"></appender-ref>
</root>
</Loggers>
</Configuration>
四.使用LOG4J
/**
* 加载log4j2.xml配置文件
* @throws Exception
*/
public static void load() throws Exception {
System.out.println("加载log4j配置开始...");
ConfigurationSource source = null;
FileInputStream fis = null;
try {
String logConfPath = System.getProperty("user.dir") + File.separatorChar + "configs" + File.separatorChar + "log4j2.xml";
fis = new FileInputStream(logConfPath);
source = new ConfigurationSource(fis);
Configurator.initialize(null, source);
System.out.println("加载log4j配置完成!");
}catch (Exception e) {
e.printStackTrace();
System.err.println("加载log4j配置失败!");
} finally {
if (fis != null) {
fis.close();
}
}
}
public static void main(String[] args) {
try {
// 加载log4j2配置文件
Log4j2Conf.load();
Logger log = LoggerFactory.getLogger(RootLogger.ROOT);
log.debug("hello,woshizjc!");
log.error("报错拉!");
} catch (Exception e) {
e.printStackTrace();
}
}
五.自定义输出格式
%c{参数} 或 %logger{参数} | 输出日志名称;如果加上{<层数>}表示列出从最内层算起的指定层数的名字空间,下同 | 假设当前logger名字空间是"a.b.c" | |
%c{2} | 列出从最内层算起的2层名字空间,输出结果"b.c" | ||
%20c | 若名字空间长度小于20,则左边用空格填充 | ||
%.30c | 若名字空间长度超过30,截去多余字符 | ||
%-20.30c | 若名字空间长度小于20,则右边用空格填充;若名字空间长度超过30,截去多余字符 | ||
%C{参数} 或 %class{参数} | 列出调用logger的类的全名(包含包路径) | 假设当前类是"org.apache.xyz.SomeClass" | |
%C | org.apache.xyz.SomeClass | ||
%C{1} | SomeClass | ||
%F 或 %file | 显示调用logger的源文件名 | ||
%m 或 %msg 或 %message | 输出日志信息 | ||
%M 或 %method | 显示调用logger的方法名 | ||
%r | 显示从程序启动时到记录该条日志时已经经过的毫秒数 | ||
%d{参数} | 显示日志记录时间 | %d{yyyy/MM/dd HH:mm:ss,SSS} | 2005/10/12 22:23:30,117 |
%d{ABSOLUTE} | 22:23:30,117 | ||
%d{DATE} | 12 Oct 2005 22:23:30,117 | ||
%d{ISO8601} | 2005-10-12 22:23:30,117 | ||
%t | 输出产生该日志事件的线程名 | ||
%l | 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数 | ||
%L | 显示调用logger的代码行号 | ||
%p 或 %level | 显示该条日志的日志等级 | ||
%x | 按NDC(Nested Diagnostic Context,线程堆栈)顺序输出日志 | 假设某程序调用顺序是MyApp调用com.foo.Bar | |
%c %x - %m%n | MyApp - Call com.foo.Bar. com.foo.Bar - Log in Bar MyApp - Return to MyApp. | ||
%X | 按MDC(Mapped Diagnostic Context,线程映射表)输出日志。通常用于多个客户端连接同一台服务器,方便服务器区分是那个客户端访问留下来的日志 | %X{5} | 记录代号为5的客户端的日志 |
%% | 显示一个百分号 | ||
%n | 当前操作系统下的换行符 | ||
highlight{pattern}{style} | 高亮显示 | ||
特殊符号 | 有些特殊符号不能直接打印,需要使用实体名称或者编号 | & | & 或者 & |
< | < 或者 < | ||
> | > 或者 > | ||
“ | " 或者 " | ||
‘ | ' 或者 ' | ||
pattern对齐修饰 | 编写格式为在任何pattern和%之间加入一个小数,可以是正数,也可以是负数。 整数表示右对齐,负数表示左对齐;整数位表示输出信息的最小n个字符,如果输出信息不够n个字符,将用空格补齐;小数位表示输出信息的最大字符数,如果超过n个字符,则只保留最后n个字符的信息(注意:保留的是后20个字符,而不是前20个字符) | 示例如下 | |
%20 | 右对齐,不足20个字符则在信息前面用空格补足,超过20个字符则保留原信息 | ||
%-20 | 左对齐,不足20个字符则在信息后面用空格补足,超过20个字符则保留原信息 | ||
%.30 | 如果信息超过30个字符,则只保留最后30个字符 | ||
%20.30 | 右对齐,不足20个字符则在信息前面用空格补足,超过30个字符则只保留最后30个字符 | ||
%-20.30 | 左对齐,不足20个字符则在信息后面用空格补足,超过30个字符则只保留最后30个字符 |