Intro to Log4j2 – Appenders, Layouts and Filters – Log4j2介绍 – Appenders, Layouts and Filters

最后修改: 2017年 2月 28日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Logging events is a critical aspect of software development. While there are lots of frameworks available in Java ecosystem, Log4J has been the most popular for decades, due to the flexibility and simplicity it provides.

记录事件是软件开发的一个重要方面。虽然在Java生态系统中有很多框架,但由于Log4J提供的灵活性和简单性,几十年来它一直是最受欢迎的。

Log4j 2 is a new and improved version of the classic Log4j framework.

Log4j 2是经典Log4j框架的一个新的改进版本。

In this article, we’ll introduce the most common appenders, layouts, and filters via practical examples.

在这篇文章中,我们将通过实际例子介绍最常见的应用者、布局和过滤器。

In Log4J2, an appender is simply a destination for log events; it can be as simple as a console and can be complex like any RDBMS. Layouts determine how the logs will be presented and filters filter the data according to the various criterion.

在Log4J2中,appender只是一个日志事件的目的地;它可以像控制台那样简单,也可以像任何RDBMS那样复杂。布局决定了日志的呈现方式,过滤器根据各种标准过滤数据。

2. Setup

2.设置

In order to understand several logging components and their configuration let’s set up different test use-cases, each consisting of a log4J2.xml configuration file and a JUnit 4 test class.

为了了解几个日志组件及其配置,让我们设置不同的测试用例,每个用例由一个log4J2.xml配置文件和一个JUnit 4测试类组成。

Two maven dependencies are common to all examples:

所有例子中都有两个maven依赖项。

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.7</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.7</version>
    <type>test-jar</type>
    <scope>test</scope>
</dependency>

Besides the main log4j-core package we need to include the ‘test jar’ belonging to the package to gain access to a context rule needed for testing of uncommonly named configuration files.

除了主log4j-core包,我们需要包括属于该包的’test jar’,以获得测试不常见的配置文件所需的上下文规则。

3. Default Configuration

3.缺省配置

ConsoleAppender is the default configuration of the Log4J 2 core package. It logs messages to the system console in a simple pattern:

ConsoleAppenderLog4J 2核心软件包的默认配置。它以一种简单的模式将消息记录到系统控制台。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="ConsoleAppender" target="SYSTEM_OUT">
            <PatternLayout 
              pattern="%d [%t] %-5level %logger{36} - %msg%n%throwable"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="ERROR">
            <AppenderRef ref="ConsoleAppender"/>
        </Root>
    </Loggers>
</Configuration>

Let’s analyze the tags in this simple XML configuration:

让我们分析一下这个简单的XML配置中的标签。

  • Configuration: The root element of a Log4J 2 configuration file and attribute status is the level of the internal Log4J events, that we want to log
  • Appenders: This element is holding one or more appenders. Here we’ll configure an appender that outputs to the system console at standard out
  • Loggers: This element can consist of multiple configured Logger elements. With the special Root tag, you can configure a nameless standard logger that will receive all log messages from the application. Each logger can be set to a minimum log level
  • AppenderRef: This element defines a reference to an element from the Appenders section. Therefore the attribute ‘ref‘ is linked with an appenders ‘name‘ attribute

The corresponding unit test will be similarly simple. We’ll obtain a Logger reference and print two messages:

相应的单元测试也将同样简单。我们将获得一个Logger引用,并打印两条信息。

@Test
public void givenLoggerWithDefaultConfig_whenLogToConsole_thanOK()
  throws Exception {
    Logger logger = LogManager.getLogger(getClass());
    Exception e = new RuntimeException("This is only a test!");

    logger.info("This is a simple message at INFO level. " +
      "It will be hidden.");
    logger.error("This is a simple message at ERROR level. " +
    "This is the minimum visible level.", e);
}

4. ConsoleAppender With PatternLayout

4、ConsoleAppenderPatternLayout

Let’s define a new console appender with a customized color pattern in a separate XML file, and include that in our main configuration:

让我们在一个单独的XML文件中定义一个新的具有自定义颜色模式的控制台appender,并将其纳入我们的主配置。

<?xml version="1.0" encoding="UTF-8"?>
<Console name="ConsoleAppender" target="SYSTEM_OUT">
    <PatternLayout pattern="%style{%date{DEFAULT}}{yellow}
      %highlight{%-5level}{FATAL=bg_red, ERROR=red, WARN=yellow, INFO=green} 
      %message"/>
</Console>

This file is using some pattern variables that gets replaced by Log4J 2 at runtime:

这个文件使用了一些模式变量,在运行时被Log4J 2替换。

  • %style{…}{colorname}: This will print the text in the first bracket pair () in a given color (colorname).
  • %highlight{…}{FATAL=colorname, …}: This is similar to the ‘style’ variable. But a different color can be given for each log level.
  • %date{format}: This gets replaced by the current date in the specified format. Here we’re using the ‘DEFAULT’ DateTime format, yyyy-MM-dd HH:mm:ss,SSS’.
  • %-5level: Prints the level of the log message in a right-aligned fashion.
  • %message: Represents the raw log message

But there exists many more variables and formatting in the PatternLayout. You can refer them to the Log4J 2‘s official documentation.

但在PatternLayout中存在更多的变量和格式化。你可以参考Log4J 2>的官方文档。

Now we’ll include the defined console appender into our main configuration:

现在我们将把定义好的控制台应用程序纳入我们的主配置。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" xmlns:xi="http://www.w3.org/2001/XInclude">
    <Appenders>
        <xi:include href="log4j2-includes/
          console-appender_pattern-layout_colored.xml"/>
    </Appenders>
    <Loggers>
        <Root level="DEBUG">
            <AppenderRef ref="ConsoleAppender"/>
        </Root>
    </Loggers>
</Configuration>

The unit test:

单元测试。

@Test
public void givenLoggerWithConsoleConfig_whenLogToConsoleInColors_thanOK() 
  throws Exception {
    Logger logger = LogManager.getLogger("CONSOLE_PATTERN_APPENDER_MARKER");
    logger.trace("This is a colored message at TRACE level.");
    ...
}

5. Async File Appender With JSONLayout and BurstFilter

5.使用JSONLayoutBurstFilter的异步文件Appender

Sometimes it’s useful to write log messages in an asynchronous manner. For example, if application performance has priority over the availability of logs.

有时,以异步的方式写日志信息是很有用的。例如,如果应用程序的性能优先于日志的可用性。

In such use-cases, we can use an AsyncAppender.

在这种使用情况下,我们可以使用一个AsyncAppender.

For our example, we’re configuring an asynchronous JSON log file. Furthermore, we’ll include a burst filter that limits the log output at a specified rate:

对于我们的例子,我们要配置一个异步的JSON日志文件。此外,我们将包括一个突发过滤器,将日志输出限制在一个指定的速率。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        ...
        <File name="JSONLogfileAppender" fileName="target/logfile.json">
            <JSONLayout compact="true" eventEol="true"/>
            <BurstFilter level="INFO" rate="2" maxBurst="10"/>
        </File>
        <Async name="AsyncAppender" bufferSize="80">
            <AppenderRef ref="JSONLogfileAppender"/>
        </Async>
    </Appenders>
    <Loggers>
        ...
        <Logger name="ASYNC_JSON_FILE_APPENDER" level="INFO"
          additivity="false">
            <AppenderRef ref="AsyncAppender" />
        </Logger>
        <Root level="INFO">
            <AppenderRef ref="ConsoleAppender"/>
        </Root>
    </Loggers>
</Configuration>

Notice that:

注意到这一点。

  • The JSONLayout is configured in a way, that writes one log event per row
  • The BurstFilter will drop every event with ‘INFO’ level and above if there are more than two of them, but at a maximum of 10 dropped events
  • The AsyncAppender is set to a buffer of 80 log messages; after that, the buffer is flushed to the log file

Let’s take a look at the corresponding unit test. We’re filling the appended buffer in a loop, let it write to disk and inspect the line count of the log file:

让我们看一下相应的单元测试。我们在一个循环中填充附加的缓冲区,让它写到磁盘,并检查日志文件的行数。

@Test
public void givenLoggerWithAsyncConfig_whenLogToJsonFile_thanOK() 
  throws Exception {
    Logger logger = LogManager.getLogger("ASYNC_JSON_FILE_APPENDER");

    final int count = 88;
    for (int i = 0; i < count; i++) {
        logger.info("This is async JSON message #{} at INFO level.", count);
    }
    
    long logEventsCount 
      = Files.lines(Paths.get("target/logfile.json")).count();
    assertTrue(logEventsCount > 0 && logEventsCount <= count);
}

6. RollingFile Appender and XMLLayout

6.RollingFile Appender和XMLLayout

Next, we’ll create a rolling log file. After a configured file size, the log file gets compressed and rotated.

接下来,我们将创建一个滚动日志文件。在配置好的文件大小之后,日志文件会被压缩和旋转。

This time we’re using an XML layout:

这次我们使用一个XML布局。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <RollingFile name="XMLRollingfileAppender"
          fileName="target/logfile.xml"
          filePattern="target/logfile-%d{yyyy-MM-dd}-%i.log.gz">
            <XMLLayout/>
            <Policies>
                <SizeBasedTriggeringPolicy size="17 kB"/>
            </Policies>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Logger name="XML_ROLLING_FILE_APPENDER" 
       level="INFO" additivity="false">
            <AppenderRef ref="XMLRollingfileAppender" />
        </Logger>
        <Root level="TRACE">
            <AppenderRef ref="ConsoleAppender"/>
        </Root>
    </Loggers>
</Configuration>

Notice that:

注意到这一点。

  • The RollingFile appender has a ‘filePattern’ attribute, which is used to name rotated log files and can be configured with placeholder variables. In our example, it should contain a date and a counter before the file suffix.
  • The default configuration of XMLLayout will write single log event objects without the root element.
  • We’re using a size based policy for rotating our log files.

Our unit test class will look like the one from the previous section:

我们的单元测试类将看起来像上一节的那个。

@Test
public void givenLoggerWithRollingFileConfig_whenLogToXMLFile_thanOK()
  throws Exception {
    Logger logger = LogManager.getLogger("XML_ROLLING_FILE_APPENDER");
    final int count = 88;
    for (int i = 0; i < count; i++) {
        logger.info(
          "This is rolling file XML message #{} at INFO level.", i);
    }
}

7. Syslog Appender

7.Syslog Appender

Let’s say we need to send logged event’s to a remote machine over the network. The simplest way to do that using Log4J2 would be using it’s Syslog Appender:

比方说,我们需要通过网络将记录的事件发送到远程机器上。使用Log4J2最简单的方法是使用它的Syslog Appender:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        ...
        <Syslog name="Syslog" 
          format="RFC5424" host="localhost" port="514" 
          protocol="TCP" facility="local3" connectTimeoutMillis="10000" 
          reconnectionDelayMillis="5000">
        </Syslog>
    </Appenders>
    <Loggers>
        ...
        <Logger name="FAIL_OVER_SYSLOG_APPENDER" 
          level="INFO" 
          additivity="false">
            <AppenderRef ref="FailoverAppender" />
        </Logger>
        <Root level="TRACE">
            <AppenderRef ref="Syslog" />
        </Root>
    </Loggers>
</Configuration>

The attributes in the Syslog tag:

Syslog标签中的属性。

  • name: defines the name of the appender, and must be unique. Since we can have multiple Syslog appenders for the same application and configuration
  • format: it can be either set to BSD or RFC5424, and the Syslog records would be formatted accordingly
  • host & port: the hostname and port of the remote Syslog server machine
  • protocol: whether to use TCP or UPD
  • facility: to which Syslog facility the event will be written
  • connectTimeoutMillis: time period of waiting for an established connection, defaults to zero
  • reconnectionDelayMillis: time to wait before re-attempting connection

8. FailoverAppender

8.FailoverAppender

Now there may be instances where one appender fails to process the log events and we do not want to lose the data. In such cases, the FailoverAppender comes handy.

现在可能会有这样的情况:一个appender无法处理日志事件,而我们不想丢失数据。在这种情况下,FailoverAppender就很方便了。

For example, if the Syslog appender fails to send events to the remote machine, instead of losing that data we might fall back to FileAppender temporarily.

例如,如果Syslogappender未能向远程机器发送事件,我们可能会暂时退回到FileAppender,而不是丢失这些数据。

The FailoverAppender takes a primary appender and number of secondary appenders. In case the primary fails, it tries to process the log event with secondary ones in order until one succeeds or there aren’t any secondaries to try:

FailoverAppender需要一个主appender和若干次要appender。如果主应用器失败了,它将尝试用次应用器依次处理日志事件,直到有一个成功或没有任何次应用器可以尝试。

<Failover name="FailoverAppender" primary="Syslog">
    <Failovers>
        <AppenderRef ref="ConsoleAppender" />
    </Failovers>
</Failover>

Let’s test it:

让我们来测试一下。

@Test
public void givenLoggerWithFailoverConfig_whenLog_thanOK()
  throws Exception {
    Logger logger = LogManager.getLogger("FAIL_OVER_SYSLOG_APPENDER");
    Exception e = new RuntimeException("This is only a test!"); 

    logger.trace("This is a syslog message at TRACE level.");
    logger.debug("This is a syslog message at DEBUG level.");
    logger.info("This is a syslog message at INFO level. 
      This is the minimum visible level.");
    logger.warn("This is a syslog message at WARN level.");
    logger.error("This is a syslog message at ERROR level.", e);
    logger.fatal("This is a syslog message at FATAL level.");
}

9. JDBC Appender

9.JDBC Appender

The JDBC appender sends log events to an RDBMS, using standard JDBC. The connection can be obtained either using any JNDI Datasource or any connection factory.

JDBC appender使用标准的JDBC将日志事件发送到RDBMS。连接可以使用任何JNDI数据源或任何连接工厂获得。

The basic configuration consists of a DataSource or ConnectionFactory, ColumnConfigs, and tableName:

基本配置包括一个DataSourceConnectionFactory、ColumnConfigs和tableName:

<JDBC name="JDBCAppender" tableName="logs">
    <ConnectionFactory 
      class="com.baeldung.logging.log4j2.tests.jdbc.ConnectionFactory" 
      method="getConnection" />
    <Column name="when" isEventTimestamp="true" />
    <Column name="logger" pattern="%logger" />
    <Column name="level" pattern="%level" />
    <Column name="message" pattern="%message" />
    <Column name="throwable" pattern="%ex{full}" />
</JDBC>

Now let’s try out:

现在我们来试一试。

@Test
public void givenLoggerWithJdbcConfig_whenLogToDataSource_thanOK()
  throws Exception {
    Logger logger = LogManager.getLogger("JDBC_APPENDER");
    final int count = 88;
    for (int i = 0; i < count; i++) {
        logger.info("This is JDBC message #{} at INFO level.", count);
    }

    Connection connection = ConnectionFactory.getConnection();
    ResultSet resultSet = connection.createStatement()
      .executeQuery("SELECT COUNT(*) AS ROW_COUNT FROM logs");
    int logCount = 0;
    if (resultSet.next()) {
        logCount = resultSet.getInt("ROW_COUNT");
    }
    assertTrue(logCount == count);
}

10. Conclusion

10.结论

This article shows very simple examples of how you can use different logging appenders, filter and layouts with Log4J2 and ways to configure them.

本文展示了非常简单的例子,说明如何使用Log4J2的不同日志应用者、过滤器和布局以及配置它们的方法。

The examples that accompany the article are available over on GitHub.

与文章配套的例子可以在GitHub上找到