Перейти к основному содержимому

Введение в Log4j2 — Appenders, макеты и фильтры

· 8 мин. чтения

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 , нам нужно включить «тестовую банку», принадлежащую пакету, чтобы получить доступ к правилу контекста, необходимому для тестирования файлов конфигурации с необычными именами.

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 ' связан с атрибутом appenders ' 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} : это напечатает текст в первой паре квадратных скобок ( ) заданным цветом ( 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 отброшенных событий.
  • AsyncAppender настроен на буфер из 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 и 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. Приложение системного журнала

Допустим, нам нужно отправить зарегистрированное событие на удаленную машину по сети. Самый простой способ сделать это с помощью 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 для одного и того же приложения и конфигурации.
  • format : он может быть установлен в BSD или RFC5424, и записи Syslog будут отформатированы соответствующим образом.
  • host & port : имя хоста и порт удаленного сервера Syslog.
  • протокол : использовать ли TCP или UPD
  • средство : в какое средство системного журнала будет записано событие
  • connectTimeoutMillis : время ожидания установленного соединения, по умолчанию равно нулю
  • reconnectionDelayMillis : время ожидания перед повторной попыткой подключения

8. FailoverAppender

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

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

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

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

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

@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

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

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

<JDBC name="JDBCAppender" tableName="logs">
<ConnectionFactory
class="com.foreach.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>

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

@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. Заключение

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

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