«1. Обзор

Регистрация событий является важным аспектом разработки программного обеспечения. Хотя в экосистеме Java доступно множество фреймворков, Log4J был самым популярным на протяжении десятилетий благодаря гибкости и простоте, которые он обеспечивает.

Log4j 2 — это новая и улучшенная версия классической структуры Log4j.

В этой статье мы познакомим вас с наиболее распространенными приложениями, макетами и фильтрами на практических примерах.

В Log4J2 приложение является просто местом назначения для событий журнала; она может быть простой, как консоль, а может быть сложной, как любая СУБД. Макеты определяют, как будут представлены журналы, а фильтры фильтруют данные в соответствии с различными критериями.

2. Настройка

Чтобы понять несколько компонентов ведения журналов и их настройку, давайте настроим различные тестовые варианты использования, каждый из которых состоит из файла конфигурации log4J2.xml и тестового класса JUnit 4.

Две зависимости 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>

Помимо основного пакета log4j-core, нам нужно включить «test jar», принадлежащий пакету, чтобы получить доступ к правилу контекста, необходимому для тестирования. файлов конфигурации с необычными именами.

3. Конфигурация по умолчанию

ConsoleAppender — это конфигурация по умолчанию основного пакета Log4J 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>

Давайте проанализируем теги в этой простой XML-конфигурации:

    Конфигурация: корневой элемент файла конфигурации Log4J 2 и статус атрибута — это уровень внутренние события Log4J, которые мы хотим регистрировать Appenders: этот элемент содержит один или несколько Appenders. Здесь мы настроим приложение, которое выводит на системную консоль стандартный выход Loggers: Этот элемент может состоять из нескольких настроенных элементов Logger. С помощью специального корневого тега вы можете настроить безымянный стандартный логгер, который будет получать все лог-сообщения от приложения. Для каждого регистратора можно установить минимальный уровень журнала. AppenderRef: этот элемент определяет ссылку на элемент из раздела Appenders. Поэтому атрибут «ref» связан с атрибутом «name» приложения

Соответствующий модульный тест будет таким же простым. Мы получим ссылку на 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 с PatternLayout

Давайте определим новый консольный аппендер с настроенным цветовым шаблоном в отдельном XML-файле и включим его в нашу основную конфигурацию. :

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

В этом файле используются некоторые переменные шаблона, которые во время выполнения заменяются на Log4J 2:

    %style{…}{colorname}: будет напечатан текст в первой паре квадратных скобок (†¦) заданного цвета (название цвета). %highlight{…}{FATAL=colorname, …}: это похоже на переменную «стиль». Но для каждого уровня журнала может быть задан свой цвет. %date{format}: заменяется текущей датой в указанном формате. Здесь мы используем формат даты и времени «ПО УМОЛЧАНИЮ», «гггг-ММ-дд ЧЧ:мм:сс,ССС». %-5level: печатает уровень сообщения журнала с выравниванием по правому краю. %message: Представляет необработанное сообщение журнала

Но в PatternLayout существует гораздо больше переменных и форматов. Вы можете обратиться к официальной документации Log4J 2.

Теперь мы включим заданный модуль консоли в нашу основную конфигурацию:

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

Модульный тест:

@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. Асинхронный модуль добавления файлов с JSONLayout и BurstFilter

Иногда бывает полезно написать логировать сообщения асинхронно. Например, если производительность приложения имеет приоритет над доступностью журналов.

В таких случаях мы можем использовать AsyncAppender.

В нашем примере мы настраиваем асинхронный файл журнала 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>

Обратите внимание, что:

    «JSONLayout настроен таким образом, что записывает одно событие журнала в строку. BurstFilter отбрасывает каждое событие с уровнем «INFO» и выше, если их более двух, но не более 10 отброшенных событий. установить буфер из 80 сообщений журнала; после этого буфер сбрасывается в лог-файл

Давайте посмотрим на соответствующий модульный тест. Мы заполняем добавленный буфер в цикле, позволяем ему записывать на диск и проверять количество строк в файле журнала:

@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 и XMLLayout

Далее мы создадим скользящий журнал. файл. После заданного размера файла файл журнала сжимается и ротируется.

На этот раз мы используем 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>

Обратите внимание на следующее:

    Приложение RollingFile имеет атрибут «filePattern», который используется для именования файлов журналов с ротацией и может быть настроен с помощью переменные-заполнители. В нашем примере он должен содержать дату и счетчик перед суффиксом файла. Конфигурация XMLLayout по умолчанию будет записывать отдельные объекты событий журнала без корневого элемента. Мы используем политику на основе размера для ротации наших файлов журналов.

Наш класс модульного теста будет выглядеть так же, как и в предыдущем разделе:

@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

Допустим, нам нужно отправить зарегистрированные события на удаленную машину по сети. Простейшим способом сделать это с помощью 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>

Атрибуты в теге Syslog:

    name: определяет имя приложения и должно быть уникальным. Поскольку у нас может быть несколько приложений Syslog для одного и того же приложения и формата конфигурации: он может быть либо установлен в BSD, либо RFC5424, а записи Syslog будут отформатированы соответствующим образом. host \u0026 port: имя хоста и порт удаленного сервера Syslog. использовать средство TCP или UPD: в какое средство Syslog будет записано событие connectTimeoutMillis: время ожидания установленного соединения, по умолчанию равно нулю reconnectionDelayMillis: время ожидания перед повторной попыткой соединения могут быть случаи, когда один из приложений не может обработать события журнала, и мы не хотим терять данные. В таких случаях FailoverAppender пригодится.

Например, если приложение Syslog не может отправить события на удаленную машину, вместо потери этих данных мы можем временно вернуться к FileAppender.

FailoverAppender принимает первичный присоединитель и количество вторичных присоединителей. В случае сбоя основного, он пытается обработать событие журнала с вторичными по порядку, пока один из них не завершится успешно или пока не останется вторичных, которые можно было бы попробовать:

Давайте проверим это:

9. JDBC Appender

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

Appender JDBC отправляет события журнала в RDBMS, используя стандартный JDBC. Соединение можно получить либо с помощью любого источника данных JNDI, либо с помощью любой фабрики соединений.

@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.");
}

Базовая конфигурация состоит из DataSource или ConnectionFactory, ColumnConfigs и tableName:

Теперь давайте попробуем:

10. Заключение

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

В этой статье показаны очень простые примеры того, как может использовать различные приложения для ведения журналов, фильтры и макеты с Log4J2 и способы их настройки.

@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);
}

Примеры, сопровождающие статью, доступны на GitHub.

«

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