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

Программная конфигурация с Log4j 2

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

1. Введение

В этом руководстве мы рассмотрим различные способы программной настройки Apache Log4j 2.

2. Первоначальная настройка

Чтобы начать использовать Log4j 2, нам просто нужно включить зависимости log4j-core и log4j-slf4j-impl в наш pom.xml :

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.11.0</version>
</dependency>

3. Конфигуратор

После того, как мы настроили Maven, нам нужно создать ConfigurationBuilder , который является классом, который позволяет нам настраивать приложения, фильтры, макеты и регистраторы.

Log4j 2 предоставляет несколько способов получить ConfigurationBuilder .

Начнем с самого прямого пути:

ConfigurationBuilder<BuiltConfiguration> builder
= ConfigurationBuilderFactory.newConfigurationBuilder();

И чтобы начать настройку компонентов, ConfigurationBuilder оснащен соответствующим новым методом, таким как newAppender или newLayout , для каждого компонента.

Некоторые компоненты имеют разные подтипы, например FileAppender или ConsoleAppender, и в API они называются плагинами .

3.1. Настройка приложений

Давайте сообщим сборщику , куда отправлять каждую строку журнала, настроив приложение appender : ``

AppenderComponentBuilder console 
= builder.newAppender("stdout", "Console");

builder.add(console);

AppenderComponentBuilder file
= builder.newAppender("log", "File");
file.addAttribute("fileName", "target/logging.log");

builder.add(file);

Хотя большинство новых методов не поддерживают это, newAppender(name, plugin) позволяет нам дать добавлению имя, которое позже окажется важным. Эти приложения мы назвали stdout и log, хотя могли бы назвать их как угодно.

Мы также сообщили сборщику , какой подключаемый модуль приложения (или, проще говоря, какой тип приложения) использовать. Console и File относятся к приложениям Log4j 2 для записи в стандартный вывод и в файловую систему соответственно.

Хотя Log4j 2 поддерживает несколько приложений , их настройка с помощью Java может быть немного сложной, поскольку AppenderComponentBuilder является универсальным классом для всех типов приложений.

Это позволяет использовать такие методы, как addAttribute и addComponent вместо setFileName и addTriggeringPolicy :

AppenderComponentBuilder rollingFile 
= builder.newAppender("rolling", "RollingFile");
rollingFile.addAttribute("fileName", "rolling.log");
rollingFile.addAttribute("filePattern", "rolling-%d{MM-dd-yy}.log.gz");

builder.add(rollingFile);

И, наконец, не забудьте вызвать builder.add , чтобы добавить его в основную конфигурацию!

3.2. Настройка фильтров

Мы можем добавить фильтры к каждому из наших приложений, которые определяют для каждой строки журнала, следует ли добавлять ее или нет.

Давайте используем плагин MarkerFilter в нашем консольном приложении:

FilterComponentBuilder flow = builder.newFilter(
"MarkerFilter",
Filter.Result.ACCEPT,
Filter.Result.DENY);
flow.addAttribute("marker", "FLOW");

console.add(flow);

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

В данном случае мы сделали это просто, заявив, что если MarkerFilter проходит успешно, то ПРИНИМАЮ строку лога. В противном случае, ОТРИЦАТЬ его.

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

3.3. Настройка макетов

Далее давайте определим макет для каждой строки журнала. В этом случае мы будем использовать плагин PatternLayout :

LayoutComponentBuilder standard 
= builder.newLayout("PatternLayout");
standard.addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable");

console.add(standard);
file.add(standard);
rolling.add(standard);

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

3.4. Настройка корневого регистратора

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

Корневой регистратор — это самый высокий регистратор, вроде Object в Java. Этот регистратор будет использоваться по умолчанию, если он не будет переопределен.

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

RootLoggerComponentBuilder rootLogger 
= builder.newRootLogger(Level.ERROR);
rootLogger.add(builder.newAppenderRef("stdout"));

builder.add(rootLogger);

Чтобы указать нашему регистратору на конкретный аппендер, мы не даем ему экземпляр построителя. Вместо этого мы обращаемся к нему по имени , которое мы дали ему ранее.

3.5. Настройка дополнительных регистраторов

Дочерние регистраторы можно использовать для нацеливания на определенные пакеты или имена регистраторов.

Давайте добавим регистратор для пакета com в наше приложение, установив уровень ведения журнала на DEBUG и отправив его в приложение журнала :

LoggerComponentBuilder logger = builder.newLogger("com", Level.DEBUG);
logger.add(builder.newAppenderRef("log"));
logger.addAttribute("additivity", false);

builder.add(logger);

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

3.6. Настройка других компонентов

Не все компоненты имеют специальный новый метод в ConfigurationBuilder .

Итак, в этом случае мы вызываем newComponent.

Например, поскольку TriggeringPolicyComponentBuilder отсутствует , нам нужно использовать newComponent для чего-то вроде указания нашей политики срабатывания для последовательного добавления файлов:

ComponentBuilder triggeringPolicies = builder.newComponent("Policies")
.addComponent(builder.newComponent("CronTriggeringPolicy")
.addAttribute("schedule", "0 0 0 * * ?"))
.addComponent(builder.newComponent("SizeBasedTriggeringPolicy")
.addAttribute("size", "100M"));

rolling.addComponent(triggeringPolicies);

3.7. XML-эквивалент

ConfigurationBuilder оснащен удобным методом для распечатки эквивалентного XML:

builder.writeXmlConfiguration(System.out);

Запуск вышеуказанной строки распечатывает:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="stdout">
<PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable" />
<MarkerFilter onMatch="ACCEPT" onMisMatch="DENY" marker="FLOW" />
</Console>
<RollingFile name="rolling"
fileName="target/rolling.log"
filePattern="target/archive/rolling-%d{MM-dd-yy}.log.gz">
<PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable" />
<Policies>
<CronTriggeringPolicy schedule="0 0 0 * * ?" />
<SizeBasedTriggeringPolicy size="100M" />
</Policies>
</RollingFile>
<File name="FileSystem" fileName="target/logging.log">
<PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable" />
</File>
</Appenders>
<Loggers>
<Logger name="com" level="DEBUG" additivity="false">
<AppenderRef ref="log" />
</Logger>
<Root level="ERROR" additivity="true">
<AppenderRef ref="stdout" />
</Root>
</Loggers>
</Configuration>

Это удобно, когда мы хотим перепроверить нашу конфигурацию или если мы хотим сохранить нашу конфигурацию, скажем, в файловой системе.

3.8. Собираем все вместе

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

Configurator.initialize(builder.build());

После этого будущие вызовы Log4j 2 будут использовать нашу конфигурацию .

Обратите внимание, что это означает, что нам нужно вызвать Configurator.initialize , прежде чем мы будем делать какие-либо вызовы LogManager.getLogger .

4. Фабрика конфигураций

Теперь, когда мы увидели один способ получить и применить ConfigurationBuilder , давайте рассмотрим еще один:

public class CustomConfigFactory
extends ConfigurationFactory {

public Configuration createConfiguration(
LoggerContext context,
ConfigurationSource src) {

ConfigurationBuilder<BuiltConfiguration> builder = super
.newConfigurationBuilder();

// ... configure appenders, filters, etc.

return builder.build();
}

public String[] getSupportedTypes() {
return new String[] { "*" };
}
}

В этом случае вместо использования ConfigurationBuilderFactory мы создали подкласс ConfigurationFactory , абстрактный класс, предназначенный для создания экземпляров Configuration .

Затем вместо вызова Configurator.initialize , как мы делали в первый раз, нам просто нужно сообщить Log4j 2 о нашей новой фабрике конфигурации.

Есть три способа сделать это:

  • Статическая инициализация
  • Свойство времени выполнения или
  • Аннотация @Plugin _

4.1. Использовать статическую инициализацию

Log4j 2 поддерживает вызов setConfigurationFactory во время статической инициализации:

static {
ConfigurationFactory custom = new CustomConfigFactory();
ConfigurationFactory.setConfigurationFactory(custom);
}

Этот подход имеет то же ограничение, что и последний подход, который мы видели, а именно то, что нам нужно будет вызывать его перед любыми вызовами LogManager.getLogger .

4.2. Используйте свойство времени выполнения

Если у нас есть доступ к команде запуска Java, то Log4j 2 также поддерживает указание ConfigurationFactory для использования с помощью параметра -D :

-Dlog4j2.configurationFactory=com.foreach.log4j2.CustomConfigFactory

Основное преимущество этого подхода заключается в том, что нам не нужно беспокоиться о порядке инициализации, как в первых двух подходах.

4.3. Используйте аннотацию @Plugin

И, наконец, в обстоятельствах, когда мы не хотим возиться с командой запуска Java, добавляя -D , мы можем просто аннотировать нашу CustomConfigurationFactory аннотацией Log4j 2 @Plugin :

@Plugin(
name = "CustomConfigurationFactory",
category = ConfigurationFactory.CATEGORY)
@Order(50)
public class CustomConfigFactory
extends ConfigurationFactory {

// ... rest of implementation
}

Log4j 2 просканирует путь к классам на наличие классов, имеющих аннотацию @Plugin , и, найдя этот класс в категории ConfigurationFactory , будет использовать его.

4.4. Объединение со статической конфигурацией

Еще одно преимущество использования расширения ConfigurationFactory заключается в том, что мы можем легко комбинировать нашу пользовательскую конфигурацию с другими источниками конфигурации, такими как XML:

public Configuration createConfiguration(
LoggerContext context,
ConfigurationSource src) {
return new WithXmlConfiguration(context, src);
}

Параметр источника представляет собой статический файл конфигурации XML или JSON, который Log4j 2 находит, если он есть.

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

public class WithXmlConfiguration extends XmlConfiguration {

@Override
protected void doConfigure() {
super.doConfigure(); // parse xml document

// ... add our custom configuration
}
}

5. Вывод

В этой статье мы рассмотрели, как использовать новый API ConfigurationBuilder , доступный в Log4j 2.

Мы также рассмотрели возможность настройки ConfigurationFactory в сочетании с ConfigurationBuilder для более продвинутых вариантов использования.

Не забудьте проверить мои полные примеры на GitHub .