拓展阅读
分布式链路追踪-05-mdc 等信息如何跨线程? Log4j2 与 logback 的实现方式
日志开源组件(一)java 注解结合 spring aop 实现自动输出日志
日志开源组件(二)注解结合 spring aop 实现日志traceId唯一标识
日志开源组件(三)java 注解结合 spring aop 自动输出日志新增拦截器与过滤器
日志开源组件(四)如何动态修改 spring aop 切面信息?让自动日志输出框架更好用
日志开源组件(五)如何将 dubbo filter 拦截器原理运用到日志拦截器中?
日志开源组件(六)Adaptive Sampling 自适应采样
slf4j
Java的简单日志Facade (SLF4J)充当各种日志框架的简单Facade或抽象 (e.g. java.util.logging, logback, log4j)
允许最终用户在部署时插入所需的日志框架。
log4j2
Apache Log4j 2是Log4j的升级版,在其前身Log4j 1.x的基础上提供了重大改进。
并提供了Logback中可用的许多改进,同时修复了Logback架构中的一些固有问题。
只需使用log4j2而不是log4j和logback,原因如下。
-
Log4j 1。x和Logback将在重新配置时丢失事件。Log4j 2则不会。
-
在多线程场景下,异步logger具有10倍高的吞吐量和
延迟比Log4j 1低几个数量级。x和Logback。
- Log4jX有已知的死锁问题。其中许多在Logback中是固定的,但是
许多Logback类仍然需要相当高级别的同步。
Hello world
- pom.xml
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.6.1</version>
</dependency>
</dependencies>
- App.java
// Import log4j classes.
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
public class App {
static final Logger logger = LogManager.getLogger(App.class);
public static void main(String[] args) {
logger.trace("Entering application.");
Bar bar = new Bar();
if (!bar.doIt()) {
logger.error("Didn't do it.");
}
logger.trace("Exiting application.");
}
}
- Bar.java
public class Bar {
static final Logger logger = LogManager.getLogger(Bar.class.getName());
public boolean doIt() {
logger.entry();
logger.error("Did it again!");
return logger.exit(false);
}
}
- result
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
11:11:34.539 [main] ERROR com.ryo.logger.Bar - Did it again!
11:11:34.540 [main] ERROR com.ryo.logger.App - Didn't do it.
Process finished with exit code 0
- add log4j2.xml as following into classpath
<?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="trace">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
- and the result is:
11:22:58.433 [main] TRACE com.ryo.logger.App - Entering application.
11:22:58.437 [main] TRACE com.ryo.logger.Bar - entry
11:22:58.438 [main] ERROR com.ryo.logger.Bar - Did it again!
11:22:58.438 [main] TRACE com.ryo.logger.Bar - exit with(false)
11:22:58.438 [main] ERROR com.ryo.logger.App - Didn't do it.
11:22:58.438 [main] TRACE com.ryo.logger.App - Exiting application.
Process finished with exit code 0
Additivity
也许需要消除除’ com.ryo.logger.Bar ‘之外的所有TRACE输出,
解决方案是向配置中添加一个新的记录器定义
<?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>
<Logger name="com.ryo.logger.Bar" level="trace">
<AppenderRef ref="Console"/>
</Logger>
<Root level="error">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
- result
12:11:43.095 [main] TRACE com.ryo.logger.Bar - entry
12:11:43.095 [main] TRACE com.ryo.logger.Bar - entry
12:11:43.096 [main] ERROR com.ryo.logger.Bar - Did it again!
12:11:43.096 [main] ERROR com.ryo.logger.Bar - Did it again!
12:11:43.096 [main] TRACE com.ryo.logger.Bar - exit with(false)
12:11:43.096 [main] TRACE com.ryo.logger.Bar - exit with(false)
12:11:43.096 [main] ERROR com.ryo.logger.App - Didn't do it.
Process finished with exit code 0
Notice that the trace messages from ‘com.ryo.logger.Bar’ appear twice.
change
<Logger name="com.ryo.logger.Bar" level="trace">
<AppenderRef ref="Console"/>
</Logger>
into
<Logger name="com.ryo.logger.Bar" level="trace" additivity="false">
<AppenderRef ref="Console"/>
</Logger>
and result will be
12:16:34.921 [main] TRACE com.ryo.logger.Bar - entry
12:16:34.923 [main] ERROR com.ryo.logger.Bar - Did it again!
12:16:34.923 [main] TRACE com.ryo.logger.Bar - exit with(false)
12:16:34.923 [main] ERROR com.ryo.logger.App - Didn't do it.
Process finished with exit code 0
一旦事件到达将其相加性设置为false的记录器,该事件将不会传递给其任何父记录器。
不管它们的可加性设置如何。
给定记录器的每个启用的日志记录请求将被转发到该记录器的LoggerConfig中的所有appeners以及LoggerConfig的父appeners。
可以通过在配置文件中的Logger声明中设置 additivity="false"
来覆盖此默认行为,以便Appender累积不再是加法。
Use with junit
import junit.framework.TestCase;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class BaseTest extends TestCase {
protected static final Logger logger = LogManager.getLogger(BaseTest.class);
}
Normal Example
- 用户目录占位符:
${sys:user.home}
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="off" monitorInterval="1800">
<properties>
<property name="LOG_HOME">src/log</property>
<property name="BACKUP_HOME">${LOG_HOME}/backup</property>
<property name="SERVER_NAME">global</property>
<!--file names-->
<property name="CONTROLLER_FILE_NAME">controller</property>
</properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%t] %logger{36}:%L - %msg%n"/>
</Console>
<RollingRandomAccessFile name="DevLog" fileName="${LOG_HOME}/${SERVER_NAME}"
filePattern="${LOG_HOME}/${SERVER_NAME}.log-%d{yyyy-MM-dd-HH}.log">
<PatternLayout pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %level [%thread][%file:%line] - %msg%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
<SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
</RollingRandomAccessFile>
<RollingRandomAccessFile name="ControllerLog"
fileName="${LOG_HOME}/${CONTROLLER_FILE_NAME}.log"
filePattern="${LOG_HOME}/$${date:yyyy-MM}/${CONTROLLER_FILE_NAME}-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout
pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%t][%file:%L] - %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
<DefaultRolloverStrategy max="20"/>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<Logger name="com.ryo.interceptor.ControllerLogInterceptor" level="INFO" additivity="false">
<AppenderRef ref="ControllerLog"/>
<AppenderRef ref="Console"/>
</Logger>
<Root level="INFO">
<AppenderRef ref="DevLog"/>
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
Architecture
Log Levels
过滤级别:
Event Level | LoggerConfig Level | ||||||
---|---|---|---|---|---|---|---|
TRACE | DEBUG | INFO | WARN | ERROR | FATAL | OFF | |
ALL | YES | YES | YES | YES | YES | YES | NO |
TRACE | YES | NO | NO | NO | NO | NO | NO |
DEBUG | YES | YES | NO | NO | NO | NO | NO |
INFO | YES | YES | YES | NO | NO | NO | NO |
WARN | YES | YES | YES | YES | NO | NO | NO |
ERROR | YES | YES | YES | YES | YES | NO | NO |
FATAL | YES | YES | YES | YES | YES | YES | NO |
OFF | NO | NO | NO | NO | NO | NO | NO |
Layout
通常情况下,用户不仅希望自定义输出目的地,还希望自定义输出格式。
例如,带有转换模式 %r [%t] %-5p %c - %m%n
的PatternLayout将输出类似于以下内容:
176 [main] INFO org.foo.Bar - Located nearest gas station.
layout用于各种用例,如JSON、XML、HTML和Syslog(包括新的RFC 5424版本)。
参数 | 说明 | 例子 | |
---|---|---|---|
%c
|
列出logger名字空间的全称,如果加上{<层数>}表示列出从最内层算起的指定层数的名字空间 |
log4j配置文件参数举例
|
输出显示媒介
|
假设当前logger名字空间是"a.b.c" | |||
%c | a.b.c | ||
%c{2} | b.c | ||
%20c | (若名字空间长度小于20,则左边用空格填充) | ||
%-20c | (若名字空间长度小于20,则右边用空格填充) | ||
%.30c | (若名字空间长度超过30,截去多余字符) | ||
%20.30c | (若名字空间长度小于20,则左边用空格填充;若名字空间长度超过30,截去多余字符) | ||
%-20.30c | (若名字空间长度小于20,则右边用空格填充;若名字空间长度超过30,截去多余字符) | ||
%C
|
列出调用logger的类的全名(包含包路径) | 假设当前类是"org.apache.xyz.SomeClass" | |
%C | org.apache.xyz.SomeClass | ||
%C{1} | SomeClass | ||
%d
|
显示日志记录时间,{<日期格式>}使用ISO8601定义的日期格式 | %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 | ||
%F
|
显示调用logger的源文件名 | %F | MyClass.java |
%l
|
输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数 | %l | MyClass.main(MyClass.java:129) |
%L
|
显示调用logger的代码行 | %L | 129 |
%m
|
显示输出消息 | %m | This is a message for debug. |
%M
|
显示调用logger的方法名 | %M | main |
%n
|
当前平台下的换行符 | %n | Windows平台下表示rn UNIX平台下表示n |
%p
|
显示该条日志的优先级 | %p | INFO |
%r
|
显示从程序启动时到记录该条日志时已经经过的毫秒数 | %r | 1215 |
%t
|
输出产生该日志事件的线程名 | %t | MyClass |
%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的客户端的日志) |
%%
|
显示一个百分号 | %% | % |
Slf4j
虽然合并会降低速度,但是有时需要支持这种标准。
<!-- log配置:Log4j2 + Slf4j -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.2</version>
</dependency>
<dependency> <!-- 桥接:告诉Slf4j使用Log4j2 -->
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.2</version>
</dependency>
<dependency> <!-- 桥接:告诉commons logging使用Log4j2 -->
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.10</version>
</dependency>