拓展阅读

Log4j2 系统学习

Logback 系统学习

Slf4j

Slf4j-02-slf4j 与 logback 整合

SLF4j MDC-日志添加唯一标识

分布式链路追踪-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)

允许最终用户在部署时插入所需的日志框架。

slf4j

log4j2

Apache Log4j 2是Log4j的升级版,在其前身Log4j 1.x的基础上提供了重大改进。

并提供了Logback中可用的许多改进,同时修复了Logback架构中的一些固有问题。

log4j2

只需使用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

summary zh_CN

log4j set zh_CN

  • 用户目录占位符: ${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

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

虽然合并会降低速度,但是有时需要支持这种标准。

Slf4j+log4j2

slf4j+log4j2

<!-- 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>