slf4j
The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j) allowing the end user to plug in the desired logging framework at deployment time.
log4j2
Apache Log4j 2 is an upgrade to Log4j that provides significant improvements over its predecessor, Log4j 1.x, and provides many of the improvements available in Logback while fixing some inherent problems in Logback’s architecture.
Just use log4j2 instead of log4j and logback, reasons as following.
-
Both Log4j 1.x and Logback will lose events while reconfiguring. Log4j 2 will not.
-
In multi-threaded scenarios Asynchronous Loggers have 10 times higher throughput and orders of magnitude lower latency than Log4j 1.x and Logback.
-
Log4j 1.x has known deadlock issues. Many of these are fixed in Logback but many Logback classes still require synchronization at a fairly high level.
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
Perhaps it is desired to eliminate all the TRACE output from everything except com.ryo.logger.Bar
,
the solution is to add a new logger definition to the configuration
<?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
Once an event reaches a logger with its additivity set to false the event will not be passed to any of its parent loggers, regardless of their additivity setting.
Each enabled logging request for a given logger will be forwarded to all the appenders in that Logger’s LoggerConfig as well as the Appenders of the LoggerConfig’s parents.
It is possible to override this default behavior so that Appender accumulation is no longer additive by setting additivity="false"
on the Logger declaration in the configuration file.
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
Some article for more.
- 用户目录占位符:
${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
After to know the simple way to use, let’s learn more about loj4j2.
Log Levels
The table below illustrates how Level filtering works.
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
More often than not, users wish to customize not only the output destination but also the output format.
For example, the PatternLayout with the conversion pattern %r [%t] %-5p %c - %m%n
will output something akin to:
176 [main] INFO org.foo.Bar - Located nearest gas station.
The layout for various use cases such as JSON, XML, HTML, and Syslog (including the new RFC 5424 version).
参数 | 说明 | 例子 | |
---|---|---|---|
%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>