«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. ConfigurationBuilder

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

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

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

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

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

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

3.1. Настройка аппендеров

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

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 и отправив его в приложение журнала: logger должен наследовать такие свойства, как уровень ведения журнала и типы приложений, от своих предков.

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

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

builder.add(logger);

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

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

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

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

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

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.8. Собираем все вместе

builder.writeXmlConfiguration(System.out);

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

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

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

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

4. ConfigurationFactory

Configurator.initialize(builder.build());

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

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

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

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

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[] { "*" };
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

4.4. Сочетание со статической конфигурацией

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

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

  // ... rest of implementation
}

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

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

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

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

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

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

public class WithXmlConfiguration extends XmlConfiguration {
 
    @Override
    protected void doConfigure() {
        super.doConfigure(); // parse xml document

        // ... add our custom configuration
    }
}

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

«

We also took a look at customizing ConfigurationFactory in combination with ConfigurationBuilder for more advanced use cases.

Don’t forget to check out my complete examples over on GitHub.