Пример руководства по использованию Log4j2 – конфигурация, уровни, аппендеры

Добро пожаловать в Руководство по примерам Apache Log4j2. Если вы спросите у опытного разработчика о самом раздражающем аспекте приложения, ответ может быть связан с ведением журнала. Если в приложении нет подходящего механизма ведения журнала, обслуживание станет настоящим кошмаром. Большинство приложений проходят через этапы разработки, модульного тестирования, интеграционного тестирования. Но когда речь идет о производстве, вы всегда сталкиваетесь с уникальными сценариями и исключениями. Так что единственный способ выяснить, что произошло в конкретном случае, – это отладить через журналы. Многие фреймворки предоставляют некоторый способ ведения журнала по умолчанию, но всегда лучше придерживаться отраслевого стандарта ведения журнала. Apache Log4j – один из самых широко используемых фреймворков для ведения журнала. Apache Log4j 2 – следующая версия, которая намного лучше, чем Log4j.

Руководство по примерам Log4j

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

  1. Обзор Log4j2
  2. Архитектура Log4j2
  3. Конфигурация Log4j2
  4. Уровни Log4j2
  5. Поисковики Log4j2
  6. Аппендеры Log4j2
  7. Фильтры Log4j2
  8. Макеты Log4j2
  9. Какой уровень Log4j2 следует использовать
  10. Краткое руководство по Log4j2

Обзор Log4j2

Использование API регистрации в приложении не является роскошью, это необходимость. Log4j – это библиотека с открытым исходным кодом, опубликованная и лицензированная в рамках Apache Software. Вы можете отлаживать приложение с помощью отладки Eclipse или некоторых других инструментов, но это недостаточно и нецелесообразно в производственной среде. Механизм регистрации предоставит вам несколько преимуществ, которые вы не найдете при обычной отладке.

Category / Operation (Debugging, Logging) Debugging Logging
Human Intervention There’s a need for human intervention No need for human intervention
Persistent Medium Can’t be integrated with persistent storage Can be integrated with persistent storage (Files, Database, NoSQL database, etc.)
May used for Auditing Can’t be used for achieving auditing Can be used for achieving auditing if it’s used efficiently
Sufficient for complicated structure and flow Not sufficient; you may get lost with flow. Sufficient
Productivity Less productive More productive

Как видите выше, использование механизма регистрации будет более эффективным с меньшими затратами на обслуживание. Apache Log4j – ведущий инструмент для регистрации в приложениях Java, поэтому вы должны его использовать.

Архитектура Log4j2

Перед тем как мы приступим к примеру использования Log4j, полезно взглянуть на архитектуру Log4j2. Ниже показаны важные классы в API Log4j2. Вот подробное объяснение представленной выше архитектуры:

  • Приложения будут запрашивать у LogManager объект Logger с определенным именем.

  • LogManager найдет соответствующий контекст LoggerContext, а затем получит из него объект Logger.

  • Если Логгер еще не создан, он будет создан и ассоциирован с LoggerConfig в соответствии с тремя вариантами ниже:

    1. Экземпляр Логгера будет создан и ассоциирован с LoggerConfig, у которого такое же имя. Например, App.class в getLogger(App.class) будет оценен как строка com.journaldev.App. Имя LoggerConfig идентично полному имени класса (компонент программного обеспечения).
    2. Экземпляр Логгера будет создан и ассоциирован с LoggerConfig, у которого такой же родительский пакет. Например, com.journaldev в getLogger("com.journaldev")
    3. Экземпляр Логгера будет создан и ассоциирован с корневым LoggerConfig. Корневой LoggerConfig будет использоваться, когда нет файла конфигурации или когда вы получаете логгер с именем, не определенным в объявлениях логгера.
  • Объекты LoggerConfig создаются из объявления Logger в файле конфигурации. LoggerConfig также используется для обработки LogEvents и делегирования их для их определенных Log4j2 Appenders.

  • Корневой регистратор является исключительным случаем в терминах его существования. Он всегда существует и находится в верхней части любой иерархии регистраторов.

  • Вы можете получить корневой регистратор, используя следующие инструкции:

    Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
    Logger logger = LogManager.getRootLogger();
    
  • Имена регистраторов log4j2 чувствительны к регистру.

  • За исключением корневого регистратора, все регистраторы могут быть получены, передав их имя в LogManager.getLogger().

  • LoggerContext является центральной точкой для системы ведения журнала, поскольку в вашем приложении может быть несколько LoggerContext’ов. Для каждого LoggerContext’a должна быть установлена активная конфигурация.

  • Конфигурация Log4j2 содержит все ресурсы системы ведения журнала; LoggerConfig(s), Appender(s), Filter(s) и многое другое.

  • Вызов LogManager.getLogger() с передачей того же имени всегда будет возвращать ссылку на точно такой же экземпляр регистратора.

  • Настройка системы ведения журнала обычно выполняется при инициализации приложения. Это может принимать различные формы: программно или путем чтения файла конфигурации log4j2.

Каждый регистратор связан с объектом LoggerConfig, набор объектов LoggerConfig составляет иерархию регистраторов. Этот концепт известен как Иерархия регистраторов. Иерархия регистраторов состоит из набора объектов LoggerConfig с отношением родитель-потомок. Верхний элемент в каждой иерархии регистраторов – это Корневой регистратор. Если Log4j2 не находит файл конфигурации, для ведения журнала будет использоваться только Корневой регистратор с уровнем ведения журнала ERROR. Ниже приведено изображение предупреждающего сообщения, которое вы получите в этом случае. Ошибка StatusLogger Не найден файл конфигурации log4j2. Используется конфигурация по умолчанию: ведение только ошибок в консоль. Таблица ниже показывает родительские и дочерние отношения в иерархии регистраторов.

LoggerConfig (Is A) Root com com.journaldev com.journaldev.logging
Root X Child descendant descendant
com Parent X Child descendant
com.journaldev Ancestor Parent X Child
com.journaldev.logging Ancestor Ancestor Parent X

Для пояснения родительско-дочерних отношений таблицу выше следует читать следующим образом:

  • Root является родителем для com.
  • Root является предком для com.journaldev.
  • Root является предком для com.journaldev.logging.
  • com является дочерним для Root.
  • com является родителем для com.journaldev.
  • com является предком для com.journaldev.logging.
  • com.journaldev.logging является дочерним для com.journaldev и так далее.

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

Настройка Log4j2

Существует множество способов использования конфигурации Log4j2 в вашем приложении.

  1. Используя файл конфигурации, написанный на XML, JSON, YAML или файл свойств.
  2. Программным способом, создавая фабрику конфигурации и реализацию конфигурации.
  3. Программным способом, вызывая API, предоставленные в интерфейсе конфигурации.
  4. Программным способом, вызывая методы внутреннего класса логгера.

Мы сосредоточимся в основном на файле конфигурации. Однако полезно знать и программный подход, на случай если вы захотите настроить определенную стратегию регистрации для определенного логгера. Прежде всего, рассмотрим случай, когда вы не предоставили файл конфигурации. Реализация Log4j2 предполагает, что существует системная переменная с именем log4j.configurationFile, указывающая местоположение файла конфигурации Log4j2.

package com.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class App
{
    public static void main( String[] args ) {
    	Logger logger = LogManager.getRootLogger();
    	logger.trace("Configuration File Defined To Be :: "+System.getProperty("log4j.configurationFile"));
    }
}

A simple log4j2 configuration file will look like below. configuration.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="trace">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

И вот подробное объяснение для приведенного выше кода:

  • Приложение ссылается на корневой логгер, вызывая метод getRootLogger у LogManager.
  • Ссылка на логгер из LogManager запустила систему Log4j.
  • Log4j будет проверять свойство системы log4j.configurationFile, чтобы определить файл конфигурации log4j2. Конфигурация Log4j может быть написана в форматах JSON, YAML и XML.
  • Мы можем установить свойство системы log4j.configurationFile через System.setProperties("log4j.configurationFile","FILE_PATH") или передав его как параметр JVM, как показано на рисунке ниже. Также обратите внимание на префикс протокола File.

  • В случае, если не определено свойство системы, порядок конфигурации имеет следующий приоритет:
    • Конфигурационная фабрика свойств будет искать log4j2-test.properties в класспасе.
    • Конфигурационная фабрика YAML будет искать log4j2-test.yaml или log4j2-test.yml в класспасе.
    • Конфигурационная фабрика JSON будет искать log4j2-test.jsn или log4j2-test.json в класспасе.
    • Конфигурационная фабрика XML будет искать log4j2-test.xml в класспасе.
    • Конфигурационная фабрика свойств будет искать log4j2.properties в класспасе.
    • Конфигурационная фабрика YAML будет искать log4j2.yml или log4j2.yaml в класспасе.
    • Конфигурационная фабрика JSON будет искать log4j2.jsn или log4j2.json в класспасе.
    • Конфигурационная фабрика XML будет искать log4j2.xml в класспасе.
    • Если конфигурационный файл не был предоставлен, используется DefaultConfiguration, что приводит к набору стандартных поведений:
      • Используется корневой регистратор.
      • Уровень корневого регистратора установлен на ERROR.
      • Корневой регистратор будет передавать сообщения журналирования в консоль.
      • Устанавливается PatternLayout: %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n

Использование файла конфигурации log4j2 делает конфигурацию log4j2 настолько простой, но давайте посмотрим, как мы можем настроить ее программно. Это все о использовании ConfigurationFactory.

package com.journaldev;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
public class App
{
    public static void main( String[] args ) throws FileNotFoundException, IOException {
 
    	// Получите экземпляр фабрики конфигурации; ваши варианты: фабрика конфигурации по умолчанию, XML-фабрика конфигурации,
    	// Фабрика конфигурации Yaml и Json
    	ConfigurationFactory factory =  XmlConfigurationFactory.getInstance();
 
    	// Найдите источник этой конфигурации, этот найденный файл является фиктивным файлом, содержащим только пустой тег конфигурации
    	ConfigurationSource configurationSource = new ConfigurationSource(new FileInputStream(new File("C:/dummyConfiguration.xml")));
 
    	// Получите ссылку на конфигурацию
    	Configuration configuration = factory.getConfiguration(configurationSource);
 
    	// Создайте консольный аппендер по умолчанию
    	ConsoleAppender appender = ConsoleAppender.createDefaultAppenderForLayout(PatternLayout.createDefaultLayout());
 
    	// Добавьте консольный аппендер в конфигурацию
    	configuration.addAppender(appender);
 
    	// Создайте логгерConfig
    	LoggerConfig loggerConfig = new LoggerConfig("com",Level.FATAL,false);
 
    	// Добавьте аппендер
    	loggerConfig.addAppender(appender,null,null);
 
    	// Добавьте логгер и свяжите его с экземпляром loggerConfig
    	configuration.addLogger("com", loggerConfig);
 
    	// Получите экземпляр контекста
    	LoggerContext context = new LoggerContext("JournalDevLoggerContext");
 
    	// Запустите систему ведения журнала
    	context.start(configuration);
 
    	// Получите ссылку на логгер
    	Logger logger = context.getLogger("com");
 
    	// LogEvent сообщения DEBUG
    	logger.log(Level.FATAL, "Logger Name :: "+logger.getName()+" :: Passed Message ::");
 
    	// LogEvent сообщения об ошибке для логгера, настроенного как FATAL
    	logger.log(Level.ERROR, "Logger Name :: "+logger.getName()+" :: Not Passed Message ::");
 
    	// LogEvent сообщения об ошибке, которое будет обработано как Root
    	logger.getParent().log(Level.ERROR, "Root Logger :: Passed Message As Root Is Configured For ERROR Level messages");
    }
}
  • Вы можете использовать любую предоставленную фабрику конфигурации Log4j2 или использовать фабрику по умолчанию. Мы использовали XMLConfigurationFactory, чтобы получить экземпляр ConfigurationFactory.
  • Фабрика предоставит вам экземпляр требуемой ссылки на Configuration, передав соответствующий файл конфигурации.
  • Экземпляр конфигурации будет использоваться совместно с LoggerContext для запуска системы ведения журнала.
  • A console Appender has been configured and added into configuration instance with default layout. This Appender would print out messages into your console.
  • Создан экземпляр LoggerConfig с указанным именем, УРОВНЕМ и без использования фильтра. Созданный аппендер будет назначен для этого экземпляра LoggerConfig.
  • Экземпляр LoggerConfig добавлен в экземпляр конфигурации.
  • A new instance of LoggerContext is created with defined name.
  • Экземпляр конфигурации был передан экземпляру LoggerContext и вызван метод start для последнего.
  • A logger instance has been acquired from LoggerContext. This logger instance will be used to fire set of Log events.
  • Экземпляр Logger сгенерировал три события, которые будут объяснены в разделе Уровни Log4j2.
  • Logger com настроен для вывода сообщений с уровнем FATAL.
  • По умолчанию Root logger настроен для вывода сообщений с уровнем ERROR.
  • Сообщения ERROR не будут регистрироваться логгером ‘com’, потому что его уровень – FATAL.

Ту же конфигурацию можно выполнить, используя файлы YAML, JSON или свойств. Однако конфигурация файла свойств log4j2 отличается от файла свойств log4j, поэтому убедитесь, что вы не пытаетесь использовать конфигурацию файла свойств log4j с log4j2. Это вызовет следующую ошибку;

ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.

При обработке вышеуказанного кода будет получен следующий вывод:

Logger Name :: com :: Passed Message ::
00:01:27.705 [main] ERROR - Root Logger:: Passed Message As Root Is Configured For ERROR Level messages

Первая строка журнала от логгера com, а вторая – от Root Logger. Сообщение об ошибке логгера com не выводится, потому что его уровень – Fatal.

Уровни Log4j2

Вы можете видеть в приведенных выше примерах кода, что каждый раз, когда мы определяем LoggerConfig, мы также указываем уровень логирования. По умолчанию логирование в log4j2 является аддитивным. Это означает, что все родительские логгеры также будут использоваться, когда используется определенный логгер. Ниже приведена иллюстрация этой ситуации. И вот пояснения по этому поводу:

  • Как мы уже отмечали ранее, каждый логгер имеет связанный с ним экземпляр LoggerConfig. Этот LoggerConfig определен на уровне конфигурации.
  • Уровень логирования может быть определен на уровне LoggerConfig.
  • Вы можете получить логгер по его имени, родительскому пакету или указав сам корневой логгер.
  • Корневой логгер является верхним узлом для каждой иерархии LoggerConfig.
  • После того, как вы получите логгер com.journaldev и инициируете logEvent для записи в журнал, LoggerConfig (net.journaldev) также запишет сообщение, и сообщение будет передано вверх по иерархии без учета уровней логирования родителей. Таким образом, событие логирования будет передано логгерам com и Root, и они также соответственно запишут сообщение в соответствии с определенными уровнями.
  • После того, как вы получите логгер com и инициируете logEvent для записи в журнал, LoggerConfig(com) также запишет сообщение, и сообщение будет передано вверх по иерархии без учета уровней логирования родителей. То есть событие логирования будет передано корневому логгеру, который также запишет сообщение.
  • Та же ситуация и для иерархии net.journaldev.
  • Следующие разделы добавят больше пояснений для концепции добавления.
  • Есть возможность для родителя игнорировать сообщение, используя концепцию фильтрации или установив индикатор добавления в false, поэтому журнальные события не будут передаваться родителям.
  • Есть возможность для регистратора игнорировать сообщение, если уровень соответствующей конфигурации регистратора выше уровня журнальных событий.

Теперь давайте рассмотрим пример, связанный с вышеобъясненной концепцией добавления:

import net.NetApp;
import net.journaldev.NetJournalDevApp;
import com.ComApp;
import com.journaldev.ComJournalDevApp;
public class Main {
	public static void main(String [] args){
		new ComApp();
		new ComJournalDevApp();
		new NetApp();
		new NetJournalDevApp();
	}
}
package com.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class ComJournalDevApp {
	public ComJournalDevApp(){
		Logger logger = LogManager.getLogger(ComJournalDevApp.class);
		logger.trace("COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::");
	}
}
package net;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class NetApp {
	public NetApp(){
		Logger logger = LogManager.getLogger(NetApp.class);
		logger.error("NET :: LEVEL :: NetApp ERROR Message ::");
	}
}
package net.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class NetJournalDevApp {
	public NetJournalDevApp(){
		Logger logger = LogManager.getLogger(NetJournalDevApp.class);
		logger.error("NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::");
	}
}

В то время как файл конфигурации log4j2 выглядит следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="Console"/>
    </Root>
  	<logger name="com" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net.journaldev" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  </Loggers>
</Configuration>

Если вы выполните основной класс, вы увидите следующие результаты:

10:34:47.168 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
10:34:47.168 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.171 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
10:34:47.171 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

Вот подробное объяснение кода, указанного выше:

  • Файл конфигурации содержит пять определенных экземпляров loggerConfig, и это Root, com, com.journaldev, net & net.journaldev. Точно так же, как показана иерархия регистраторов выше.
  • Уровень Root настроен на ERROR, и это фактически значение по умолчанию.
  • Уровни com & com.journaldev настроены на TRACE.
  • Уровни net & net.journaldev настроены на ERROR.
  • Вы могли заметить, что сообщения логгеров ComAPP и ComJournalDevApp отображаются дважды и трижды соответственно. Эти сообщения отображаются в соответствии с иерархией регистраторов для ComApp & ComJournalDevApp, где они находятся в пакетах com & com.journalDev соответственно. У нас есть аналогичный случай с классами NetApp & NetJournalDevApp.
  • Родители передаются, поскольку индикатор добавления установлен по умолчанию в true.

Пространство ведения журнала учитывает уровни событий ведения журнала и уровень loggerConfig в дополнение к иерархии логгеров.

Так что если мы изменим LoggerConfig для com на INFO и оставим программу без изменений:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="Console"/>
    </Root>
  	<logger name="com" level="INFO">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net.journaldev" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  </Loggers>
</Configuration>

То результат будет следующим:

11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.307 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
11:08:10.307 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
  • Конечно, вы заметите, что событие журнала ComAPP было проигнорировано, и это из-за определенного уровня loggerConfig для пакета com. Уровень INFO (400) меньше уровня события журнала, который здесь TRACE (600). Таким образом, сообщение ComApp больше не будет отображаться, и чтобы оно отображалось, необходимо изменить уровень LoggerConfig для com на TRACE (600) или ALL (Integer.MAX_VALUE).

Чтобы убедиться, что события журнала были отображены, уровень LoggerConfig должен быть больше или равен уровню события журнала.

Таблица ниже показывает уровни log4j2 и вес каждого из них:

LEVEL Weight
OFF 0
FATAL 100
ERROR 200
WARN 300
INFO 400
DEBUG 500
TRACE 600
ALL Integer.MAX_VALUE

Конечно, таблица выше намного больше объясняет, чем слова, и она даёт вам основную причину того, почему событие журнала TRACE не отображается, когда уровень LoggerConfig установлен на INFO.

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

Но что произойдет, если мы удалим LoggerConfig для com.journaldev из конфигурации и добавим новый для com.journaldev.logging, чтобы файл конфигурации выглядел так:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="Console"/>
    </Root>
  	<logger name="com" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev.logging" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net.journaldev" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  </Loggers>
</Configuration>

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

  • Когда события регистрации вызваны логгером с именем com.journaldev.logging, LoggerConfig, связанный с этим именем (т. е. com.journaldev.logging), использовался для их обработки и вывода сообщения.
  • Так как атрибут additive LoggerConfig com.journaldev.logging установлен по умолчанию в true, событие регистрации было передано родителю, который в данном случае ссылается на com.journaldev.
  • Поскольку LoggerConfig com.journaldev не определен в конфигурации, никаких действий не происходит, и событие регистрации будет передано вверх до экземпляров LoggerConfig com и затем Root.
  • Com и Root получат событие регистрации и выведут его независимо от уровня, с которым оно было отправлено.

В результате упомянутых точек вы увидите следующие выходные данные:

14:08:37.634 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:08:37.634 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:08:37.636 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:08:37.636 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.638 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:08:37.638 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

И вы можете заметить следующее:

  • Событие регистрации в пакете com показано дважды. Одно для com, а второе для Root.
  • Событие журнала на com.journaldev было показано дважды. Одно для com и второе для Root. Хотя ранее оно происходило три раза, но на данный момент конфигурация LoggerConfig для com.journaldev отсутствует, поэтому логирование могло не произойти в пакете com.journaldev, и событие будет передано для com и Root.
  • Событие журнала на com.journaldev.logging было показано три раза, один раз для пакета com.journaldev.logging, второй раз для com и третий раз для Root. Согласно иерархии логгеров, оно должно было отображаться четыре раза, но из-за отсутствия LoggerConfig для com.journaldev оно отображается три раза.

В случае, если вы определили экземпляр LoggerConfig для com.journaldev без указания уровня, он унаследует уровень от своего родителя.

Но что если вы определили LoggerConfig для com.journaldev в вашем файле конфигурации и пропустили указание уровня LoggerConfig? К счастью, концепция иерархии логгеров спасет вас здесь, и com.journaldev унаследует свое значение уровня от своего родителя. Ниже приведен образец файла конфигурации, а затем таблица уровня логирования для каждой конфигурации логгера.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="Console"/>
    </Root>
  	<logger name="com" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev.logging" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net.journaldev" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  </Loggers>
</Configuration>
Logger Name Assigned LoggerConfig LoggerConfig Level Logger Level
Root Root ERROR ERROR
com com TRACE TRACE
com.journaldev com TRACE TRACE
com.journaldev.logging com.journaldev.logging TRACE TRACE
  • Пакет com.journaldev.logging уже ассоциирован с LoggerConfig с уровнем логирования TRACE.
  • Пакет com.journaldev уже ассоциирован с LoggerConfig без указанного уровня логирования, поэтому он унаследует уровень логирования от родительского, и, безусловно, значение будет TRACE для пакета com.
  • Пакет com уже ассоциирован с Loggerconfig с уровнем логирования TRACE.
  • По умолчанию Root имеет уровень логирования ERROR.
  • В случае, если пакет com не объявлен, LoggerConfig для com.journaldev унаследует уровень логирования от Root.

Ниже приведен результат выполнения при уровне журналирования com.journaldev, наследующем уровень журналирования com:

14:41:37.419 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:41:37.419 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.423 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:41:37.423 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

А ниже приведен результат, если удалить объявление LoggerConfig для пакета com:

14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.811 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:43:28.811 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

Вы можете заметить, что не было зарегистрировано ни одного сообщения для пакетов com и com.journaldev, причины следующие:

  • Удаление LoggerConfig, связанного с пакетом com, приведет к игнорированию всех событий журналирования, упомянутых в этом пакете.
  • Поскольку для пакета com в конфигурации не определен LoggerConfig, LoggerConfig, связанный с com.journaldev, будет наследовать уровень журналирования от родительского. Com не определен, и иерархия регистраторов достигает верхнего уровня, теперь она ссылается на Root. Уровень журналирования по умолчанию у Root — ERROR (200), а уровень событий журналирования в com.journaldev — TRACE (600) – см. ComJournalDevApp – и согласно предыдущему определению уровня LoggerConfig должен быть больше или равен уровню событий журналирования, что неверно, поэтому здесь не будут отображаться сообщения для com.journaldev.

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

X (N/A) LoggerConfig Level OFF(0) FATAL(100) ERROR(200) WARN(300) INFO(400) DEBUG(500) TRACE(600) ALL(MAX)
Event Level X X X X X X X X X
OFF(0) X YES NO NO NO NO NO NO NO
FATAL(100) X NO YES YES YES YES YES YES YES
ERROR(200) X NO NO YES YES YES YES YES YES
WARN(300) X NO NO NO YES YES YES YES YES
INFO(400) X NO NO NO NO YES YES YES YES
DEBUG(500) X NO NO NO NO NO YES YES YES
TRACE(600) X NO NO NO NO NO NO YES YES
ALL(MAX) X NO NO NO NO NO NO NO YES
  • Нет прямого метода, который можно было бы использовать для генерации событий журналирования OFF/ALL.
  • В основном для генерации событий журналирования OFF/ALL можно использовать logger.log(Level.OFF, “Msg”) или logger.log(Level.ALL,“Msg”) соответственно.
  • Метод log отвечает за обработку событий журналирования в соответствии с упомянутым уравнением.

Уравнение обработки гласит: если уровень LoggerConfig больше или равен уровню событий журналирования, событие будет принято для дальнейшей обработки.

Запись журнального события будет принята для дальнейшей обработки – это важно, потому что у вас есть возможность предотвратить обработку некоторого события, даже если оно принято с помощью фильтров Log4j2. Вы можете установить свойство additive в false, чтобы избежать распространения журнального события на родительские регистраторы. Ниже приведен тот же пример, что и ранее, но на этот раз с атрибутом добавления, так что вы можете заметить разницу.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="Console"/>
    </Root>
  	<logger name="com" level="TRACE" additivity="false">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev" additivity="false">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev.logging" level="TRACE" additivity="false">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net" level="ERROR" additivity="false">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net.journaldev" level="ERROR" additivity="false">
  		<AppenderRef ref="Console"/>
  	</logger>
  </Loggers>
</Configuration>

И результат выполнения будет таким:

17:55:30.558 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
17:55:30.560 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
17:55:30.561 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
17:55:30.561 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
17:55:30.562 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

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

Поиск Log4j2

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

  • Поиск контекста
  • Поиск даты
  • Поиск окружения
  • Поиск Java
  • Поиск JNDI
  • Поиск входного аргумента JVM (JMX)
  • Поиск главных аргументов
  • Поиск карты
  • Структурированный поиск данных
  • Поиск системных свойств
  • Поиск веб-информации

Вы можете обратиться к документации Log4j2 для получения дополнительной информации о каждом типе поиска, но давайте рассмотрим здесь несколько примеров, чтобы освоить основы поиска Log4j2. Поиск среды представляет способ передачи значения среды (либо через файл Linux etc/profile, системные переменные Windows или сценарии запуска для приложения). Как известно, у нас есть возможность определить набор переменных среды для использования приложением. Давайте рассмотрим наиболее известные способы определения ваших переменных среды.

  1. Определение переменной среды с помощью средства переменных среды Windows:
    • Щелкните правой кнопкой мыши по значку вашего компьютера и выберите свойства. Домашняя страница панели управления должна быть отображена.
    • Щелкните на ссылке “Дополнительные параметры системы”, а затем откройте окно “Переменные среды”.
    • В разделе Системные переменные определите переменную JournalDevVar со значением JournalDev.
    • Обновите PatternLayout внутри вашего log4j2.xml, чтобы добавить вашу только что добавленную переменную.

  1. Определите свои переменные среды, используя средство запуска скриптов.
    • Вместо использования обычного стандартного сценария вы можете использовать средство запуска скриптов в среде разработки Eclipse. Нажмите на меню Run и выберите Run Configuration.
    • Перейдите во вкладку Environment и определите свои переменные там.

Теперь посмотрите на измененный файл log4j2.xml и обратите внимание на использование переменных среды.

<Console name="Console" target="SYSTEM_OUT">
    <PatternLayout pattern="%d{HH:mm:ss.SSS} $${env:JournalDevVar} $${env:JournalDevSecondVar} [%t] %-5level %logger{36} - %msg%n"/>
</Console>

И результат выполнения будет выглядеть следующим образом:

23:57:02.511 JournalDev www.journaldev.com [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
23:57:02.517 JournalDev www.journaldev.com [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
23:57:02.520 JournalDev www.journaldev.com [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
23:57:02.523 JournalDev www.journaldev.com [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
23:57:02.527 JournalDev www.journaldev.com [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

Но здесь может возникнуть небольшая проблема, особенно когда вы определяете переменные среды ОС и это кэш Eclipse. Идеально, Eclipse кэширует все системные переменные при запуске, и вы можете найти их все в разделе Run – Run Configuration – Environment Tab – нажмите кнопку Select. Поэтому вы можете столкнуться с путаницей, когда вы их определили, но приложение не распознает их. Даже если вы перезапустите Eclipse, вы не получите решение, и чтобы решить эту проблему, вам необходимо выполнить eclipse.exe -clean после установки Eclipse. Чтобы убедиться, что ваши переменные среды определены правильно и ваша система будет находить их с радостью, вы можете использовать соответствующий тип плагина, предоставленный API Log4j2. Создайте экземпляр EnvironmentLookup и запросите у него определенную переменную, и если она определена, то вы легко найдете их.

EnvironmentLookup lookup = new EnvironmentLookup();
LogManager.getRootLogger().error(lookup.lookup("JournalDevSecondVar"));

Аппендеры Log4j2

Вы уже видели ранее, как можно использовать Lookups для внедрения переменных в ваш файл конфигурации. Однако, вам может потребоваться изменить средство, через которое проходят ваши сообщения. Вместо непосредственного использования консоли вы можете использовать такой файл или базу данных, чтобы убедиться, что ваши сообщения сохраняются постоянно. Log4j2 предоставляет множество аппендеров, и вы можете обратиться к документации по log4j2, чтобы получить дополнительные сведения о аппендерах. Кратко перечислим все аппендеры Log4j2 ниже.

  1. ConsoleAppender
  2. AsyncAppender
  3. FailoverAppender
  4. FileAppender
  5. FlumeAppender
  6. JDBCAppender
  7. JMSAppender
  8. JPAAppender
  9. MemoryMappedFileAppender
  10. NoSQLAppender
  11. OutputStreamAppender
  12. RandomAccessFileAppender
  13. RewriteAppender
  14. RollingFileAppender
  15. RollingRandomAccessFileAppender
  16. RoutingAppender
  17. SMTPAppender
  18. SocketAppender
  19. SyslogAppender

Наиболее известные средства, используемые для регистрации событий, – это консоль, файл и база данных. Поскольку файл будет сохранять ваши сообщения, база данных может использоваться для их аудита. Для этой цели данная секция сосредоточится на том, как эффективно использовать JDBCAppender.

JDBCAppender

Основная цель JDBCAppender – записывать события журнала в реляционную таблицу через соединения JDBC. Мы не будем тратить много времени на объяснение того, как можно оптимизировать пулы соединений, так как этот учебник не предназначен для этой цели. Но вы получите полнофункциональный пример, который поможет записывать ваши события журнала в базу данных. Прежде чем мы продолжим, давайте рассмотрим все необходимые параметры и описание каждого из них, чтобы правильно настроить JDBCAppender.

Parameter Name Type Description
Name String Required, The name of the Appender
ignoreExceptions boolean Default value is set to true, making exceptions thrown to be logged also and then ignored. False value means the exception will be propagated for the caller.
filter Filter The filter that should be used to make a decision whether the log events are going to be handled by this Appender or not.
bufferSize int Default value is zero, indicating there’s no buffering have been done upon log events. Value greater than 0 would lead the Appender to buffer log events and then flush them once the buffer reaches the limit specified.
connectionSource ConnectionSource Required, the connections source from which the database connections should be retrieved.
tableName String Required, the name of the Table on which your log events should be persisted.
columnConfigs ColumnConfig[] Required, additional information may be set upon those used columns and how the data should be persisted on each of them. This can be handled with multiple <Column> elements.
Parameter Name Type Description
jndiName String Required, full prefixed JNDI name that the javax.sql.Datasource is bound to.
Parameter Name Type Description
class String Requird, The fully qualified name for a class containg a static factory method for obtaining JDBC connections.
method boolean Required, The name of the static factory method for obtaining JDBC connections.
Parameter Name Type Description
name String Required, the name of the database column
pattern String Ability to specify any legal pattern that Log event would be formatted with
literal String Ability to specify literal value in this column (i.e. SEQ.NEXTVAL)
isEventTimestamp boolean Indicating whether the event would consider Timestamp
isUnicode boolean For unicode purpose as you may refer for Log4j2 documentation for further details
isClob boolean For storing character large object, you may refer for Log4j2 documentation for further details.

Поскольку вам приходится использовать JNDI, в нашем примере будет настроен источник данных для Oracle базы данных и Apache Tomcat 7.

  • Если вы не установили Oracle базу данных в своей среде, будет полезно, если вы сделаете это. Если вы не очень знакомы с Oracle, я рекомендую установить его Express Edition.
  • Установите Apache Tomcat 7 в своей среде.
  • Создайте проект Maven WebApp в своей среде Eclipse.

  • Убедитесь, что ваш проект успешно создан, и если вы заметите какие-либо ошибки в pom, убедитесь, что вы их исправили.
  • Добавьте зависимости Log4j2.
<dependency>
   <groupId>org.apache.logging.log4j</groupId>
   <artifactId>log4j-api</artifactId>
   <version>2.2</version>
</dependency>
<dependency>
   <groupId>org.apache.logging.log4j</groupId>
   <artifactId>log4j-core</artifactId>
   <version>2.2</version>
</dependency>
  • Настройте ваш контекст для включения объявления источника данных MySQL. Согласно документации Apache, этот файл должен быть внутри папки META-INF вашего веб-приложения.

<Context path="/JournalDevWebLogging"
	privileged="true" antiResourceLocking="false" antiJARLocking="false">
	<Resource name="jdbc/JournalDevDB" auth="Container"
			factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
			type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000"
			username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
			url="jdbc:mysql://localhost:3306/journaldev" />
</Context>
  • Настройте вашу базу данных и создайте вашу таблицу журналирования.

CREATE TABLE `logging` (
  `EVENT_ID` int(11) NOT NULL AUTO_INCREMENT,
  `EVENT_DATE` datetime DEFAULT NULL,
  `LEVEL` varchar(45) DEFAULT NULL,
  `LOGGER` varchar(45) DEFAULT NULL,
  `MSG` varchar(45) DEFAULT NULL,
  `THROWABLE` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`EVENT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
  • Настройте ваш файл log4j2.xml следующим образом:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
	<Appenders>
		<Console name="Console" target="SYSTEM_OUT">
			<PatternLayout
				pattern="%d{HH:mm:ss.SSS} $${env:JournalDevVar} $${env:JournalDevSecondVar} [%t] %-5level %logger{36} - %msg%n" />
		</Console>
		<JDBC name="databaseAppender" tableName="journaldev.logging">
			<DataSource jndiName="java:/comp/env/jdbc/JournalDevDB" />
			<Column name="EVENT_DATE" isEventTimestamp="true" />
			<Column name="LEVEL" pattern="%level" />
			<Column name="LOGGER" pattern="%logger" />
			<Column name="MSG" pattern="%message" />
			<Column name="THROWABLE" pattern="%ex{full}" />
		</JDBC>
	</Appenders>
	<Loggers>
		<Root level="ERROR">
			<AppenderRef ref="Console" />
		</Root>
		<logger name="com" level="TRACE" additivity="false">
			<AppenderRef ref="databaseAppender" />
		</logger>
		<logger name="com.journaldev" additivity="false">
			<AppenderRef ref="databaseAppender" />
		</logger>
	</Loggers>
</Configuration>
  • Создайте любой веб-ресурс, который позволит вам получить ссылку на логгер и затем зарегистрировать событие.
package com.journaldev;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class JournalDevServlet extends HttpServlet{
	private static final long serialVersionUID = 1L;
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
			Logger logger = LogManager.getLogger(JournalDevServlet.class);
			logger.trace("JournalDev Database Logging Message !");
	}
}
  • По желанию вы можете настроить ServletContextListener, который обеспечит правильную инициализацию источника данных.
package com.journaldev;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.logging.log4j.LogManager;
public class JournalDevServletContextListener implements ServletContextListener{
	private InitialContext context = null;
	public void contextDestroyed(ServletContextEvent event) {
	}
	public void contextInitialized(ServletContextEvent event) {
		try {
			// Получить начальный контекст
			context = new InitialContext();
			// Получить ссылку на подконтекст env
			Context envContext = (Context)context.lookup("java:comp/env");
			// Получить ссылку на подконтекст jdbc, а затем найти определенный источник данных
			LogManager.getRootLogger().error(((Context)envContext.lookup("jdbc")).lookup("JournalDevDB"));
		} catch (NamingException e) {
			LogManager.getRootLogger().error(e);
		}
	}
}
  • Определите свой Servlet внутри файла web.xml.
  • Запустите приложение и получите доступ к определенному выше Servlet. Вы увидите нижеуказанные журналы.
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.AprLifecycleListener init
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: C:\Program Files\Java\jdk1.6.0_26\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:\Program Files\Java\jdk1.6.0_26\jre\bin;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/bin/server;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/bin;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/lib/amd64;D:\OracleWebCenter\OracleWC\Oracle11g\app\oracle\product\11.2.0\server\bin;;C:\Program Files\Common Files\Microsoft Shared\Windows Live;C:\Program Files (x86)\Common Files\Microsoft Shared\Windows Live;D:\OracleDB\app\product\11.2.0\dbhome_1\bin;org.C:\Program Files (x86)\Common Files\NetSarang;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x86;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x64;D:\SpringRoo\spring-roo-1.2.5.RELEASE\bin;D:\Ant\apache-ant-1.9.2\bin;C:\Python27;C:\Program Files\Java\jdk1.6.0_26\bin;D:\Maven\apache-maven-3.2.1/bin;D:\bower-master\bin;C:\Program Files (x86)\Git\cmd;C:\Program Files\nodejs\;C:\Program Files\Microsoft Windows Performance Toolkit\;D:\Grails\grails-2.4.0\bin;D:\Gradle\gradle-2.0\bin;C:\Program Files (x86)\Windows Live\Shared;C:\Program Files\TortoiseSVN\bin;D:\Strawberry\perl\bin;D:\Strawberry\perl\site\bin;D:\Strawberry\c\bin;C:\Users\mohammad.amr\AppData\Roaming\npm;D:\JournalDev\eclipse;;.
Mar 15, 2015 2:31:41 PM org.apache.tomcat.util.digester.SetPropertiesRule begin
WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.j2ee.server:JournalDevWebLogging' did not find a matching property.
Mar 15, 2015 2:31:41 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
Mar 15, 2015 2:31:41 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["ajp-bio-8009"]
Mar 15, 2015 2:31:41 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 1020 ms
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Catalina
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.35
14:31:43.847 [localhost-startStop-1] ERROR  - org.apache.tomcat.jdbc.pool.DataSource@10fd0a62{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=com.mysql.jdbc.Driver; maxActive=100; maxIdle=30; minIdle=10; initialSize=10; maxWait=10000; testOnBorrow=false; testOnReturn=false; timeBetweenEvictionRunsMillis=5000; numTestsPerEvictionRun=0; minEvictableIdleTimeMillis=60000; testWhileIdle=false; testOnConnect=false; password=root; url=jdbc:mysql://localhost:3306/journaldev; username=root; validationQuery=null; validatorClassName=null; validationInterval=30000; accessToUnderlyingConnectionAllowed=true; removeAbandoned=false; removeAbandonedTimeout=60; logAbandoned=false; connectionProperties=null; initSQL=null; jdbcInterceptors=null; jmxEnabled=true; fairQueue=true; useEquals=true; abandonWhenPercentageFull=0; maxAge=0; useLock=false; dataSource=null; dataSourceJNDI=null; suspectTimeout=0; alternateUsernameAllowed=false; commitOnReturn=false; rollbackOnReturn=false; useDisposableConnectionFacade=true; logValidationErrors=false; propagateInterruptState=false; }
Mar 15, 2015 2:31:43 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]
Mar 15, 2015 2:31:43 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-bio-8009"]
Mar 15, 2015 2:31:43 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 1909 ms

Фильтры Log4j2

Даже если есть кандидат LoggerConfig для обработки события журнала, вы можете настроить его на запрет передачи событий журнала в конечные аппендеры. Это можно сделать с помощью фильтра log4j2. Этот раздел не предназначен для предоставления вам инвазивного, массового и огромного количества учебных пособий по использованию фильтров в Log4j2, так как для этого требуется множество статей, охватывающих каждый из них. Но здесь вы увидите, как использовать самый простой фильтр, чтобы понять концепцию. Один из самых простых фильтров, которые вы можете использовать, – это BurstFilter, который предоставляет вам механизм управления скоростью обработки LogEvents путем тихого отбрасывания событий после достижения максимального предела. В настоящее время вы можете увидеть все подробности, необходимые для использования BurstFilter ниже.

Parameter Name Type Description
level String Level of messages to be filtered
rate float The average number of events per second to allow
maxBurst integer The maximum number of events that can occur before events are filtered for exceeding the average rate. The default is 10 times the rate.
onMatch String Action to take when filter matches. May be Accept, DENY or NEUTRAL. The default is NEUTRAL
onMismatch String Action to tale when filter doesn’t match. May be Accept, DENY or NEUTRAL. The default is NEUTRAL

Теперь посмотрите, где находится BurstFilter внутри вашего аппендера базы данных.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
	<Appenders>
		<Console name="Console" target="SYSTEM_OUT">
			<PatternLayout
				pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
		</Console>
		<JDBC name="databaseAppender" tableName="journaldev.logging">
			<DataSource jndiName="java:/comp/env/jdbc/JournalDevDB" />
			<BurstFilter level="TRACE" rate="20" maxBurst="2"/>
			<Column name="EVENT_DATE" isEventTimestamp="true" />
			<Column name="LEVEL" pattern="%level" />
			<Column name="LOGGER" pattern="%logger" />
			<Column name="MSG" pattern="%message" />
			<Column name="THROWABLE" pattern="%ex{full}" />
		</JDBC>
	</Appenders>
	<Loggers>
		<Root level="ERROR">
			<AppenderRef ref="Console" />
		</Root>
		<logger name="com" level="TRACE" additivity="false">
			<AppenderRef ref="databaseAppender" />
		</logger>
		<logger name="com.journaldev" additivity="false">
			<AppenderRef ref="databaseAppender" />
		</logger>
	</Loggers>
</Configuration>
  • Аппендер базы данных учитывает фильтр BurstFilter, в то время как аппендер консоли этого не делает.
  • При использовании консольного регистратора все события журнала будут записаны полностью, а аппендер базы данных этого делать не будет, так как фильтр BurstFilter отклонит некоторые из них.
  • Отказ в записи LogEvents достигается даже в том случае, если используемые регистраторы являются кандидатами для обработки событий журнала. Это вполне разумно, как показано в JournalDevServlet ниже.

package com.journaldev;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class JournalDevServlet extends HttpServlet{
	private static final long serialVersionUID = 1L;
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
		Logger logger = LogManager.getLogger(JournalDevServlet.class);
		for(int i = 0 ; i < 1000 ; i++){
			logger.trace("Index :: "+i+" :: JournalDev Database Logging Message !");
			LogManager.getRootLogger().error("Index :: "+i+" :: JournalDev Database Logging Message !");
		}
	}
}

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

Макеты Log4j2

Из-за различных аппендеров, которые потребляют события журнала, и особенностей каждого аппендера, макеты создаются таким образом, чтобы формировать событие журнала в формате, который соответствует потребностям того, кто будет потреблять событие журнала. В API Log4j 1.x и Logback макетное преобразование событий журнала производилось в строку, в то время как в макетах Log4j2 был рассмотрен другой способ преобразования; именно преобразование события журнала в массив байтов. Этот новый тип преобразования требует настройки Charset для обеспечения того, что массив байтов содержит правильные значения. Настоятельно рекомендуется вернуться на официальный сайт Apache Log4j2 и узнать больше о макете и различных типах, которые предоставляет Log4j2. В этом разделе мы рассмотрим самый известный макет, который всегда используется большинством наших разработчиков, и, безусловно, вы могли слышать о нем; это PatternLayout.

Макет PatternLayout в Log4j2

Макет шаблона является настраиваемым, гибким строковым шаблоном, предназначенным для форматирования LogEvent. Этот тип форматирования зависит от концепции шаблона преобразования. В этом разделе будет показано наиболее важные функции, которые предоставляет макет шаблона. Шаблон преобразования связан с шаблоном преобразования, который предоставляет printf в языке C. Обычно шаблон преобразования состоит из текста в литералах и выражений управления форматом, называемых спецификаторами преобразования. Ниже приведена фигура, которая показывает, из каких частей состоит шаблон преобразования: Эта фигура выше является попыткой упростить Шаблон Преобразования, но, конечно, лучше обратиться к документации Apache Log4j2 для получения дополнительной информации о компоновках и в частности о Макете Шаблона. Также вы можете обратиться выше для журнальных событий и видеть каждый раз, какой Шаблон Преобразования используется для форматирования сообщений.

Какой уровень Log4j2 следует использовать

Самый большой вопрос, который вы можете задать себе, – когда следует использовать определенный уровень событий журнала. В разработке нормально использовать событие журнала DEBUG, тогда как в производстве мы должны использовать уровень INFO или WARN. Эта таблица ниже должна помочь вам определить, какой уровень log4j2 следует использовать в каком случае.

Log Event Level When It Should Be Used
OFF When no events will be logged
FATAL When a severe error will prevent the application from continuing
ERROR When an error in the application, possibly recoverable
WARN When an event that might possible lead to an error
INFO When an event for informational purposes
DEBUG When a general debugging event required
TRACE When a fine grained debug message, typically capturing the flow through the application
ALL When all events should be logged

Резюме учебного пособия по Log4j2

Log4j2 – это обновленная версия фреймворка Apache Logging. Log4j2 предоставляет набор новых функций и улучшений производительности по сравнению с Log4j1.x. Это учебное пособие по log4j2 призвано помочь вам получить всю необходимую информацию в одном месте. Поскольку некоторые из этих концепций не так легко освоить сразу, мы решили объединить наши усилия, чтобы объяснить концепцию и использовать некоторые примеры для большей ясности. Аппендеры, фильтры, макеты и поиски подпадают под эту правило. Несколько важных моментов Для того чтобы убедиться, что вы сможете запустить приложение ниже и избежать каких-либо препятствий, проверьте следующее:

  • Ваша среда разработки Eclipse настроена для использования Maven.
  • В вашем Apache Tomcat есть файл JAR mysql-connector внутри папки lib вашего домашнего каталога Apache.
  • Вы знаете, как использовать Maven.

Скачать Проект Пример Apache Log4j 2

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

Source:
https://www.digitalocean.com/community/tutorials/log4j2-example-tutorial-configuration-levels-appenders