«1. Почему регистраторы?

При написании программы или разработке производственного корпоративного приложения использование System.out.println кажется самым простым и легким вариантом. В путь к классам не нужно добавлять дополнительные библиотеки и выполнять дополнительные настройки.

Но использование System.out.println сопряжено с несколькими недостатками, которые влияют на его удобство использования во многих ситуациях. В этом уроке мы обсудим, почему и когда мы хотим использовать Logger вместо старых простых System.out и System.err. Мы также покажем несколько быстрых примеров с использованием среды ведения журналов Log4J2.

2. Настройка

Прежде чем мы начнем, давайте рассмотрим необходимые зависимости и конфигурации Maven.

2.1. Зависимости Maven

Начнем с добавления зависимости Log4J2 в наш pom.xml:

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.12.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.12.1</version>
</dependency>

Последние версии log4j-api и log4j-core можно найти на Maven Central.

2.2. Конфигурация Log4J2

Использование System.out не требует дополнительной настройки. Однако, чтобы использовать Log4J2, нам нужен файл конфигурации log4j.xml:

<Configuration status="debug" name="baeldung" packages="">
    <Appenders>
        <Console name="stdout" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %p %m%n"/>
        </Console>
    </Appenders>
    <Root level="error">
        <AppenderRef ref="STDOUT"/>
    </Root>
</Configuration>

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

3. Разделение вывода журнала

3.1. System.out и System.err

Когда мы развертываем наше приложение на сервере, таком как Tomcat, сервер использует собственный регистратор. Если мы используем System.out, журналы попадают в catalina.out. Наше приложение гораздо проще отлаживать, если логи выносить в отдельный файл. С Log4j2 нам нужно включить в конфигурацию файловый аппендер, чтобы сохранять журналы приложений в отдельном файле.

Кроме того, System.out.println не позволяет контролировать или фильтровать, какие журналы следует печатать. Единственный возможный способ разделить журналы — использовать System.out.println для информационных журналов и System.err.println для журналов ошибок:

System.out.println("This is an informational message");
System.err.println("This is an error message");

3.2. Уровни ведения журнала Log4J2

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

    FATAL ERROR WARN INFO DEBUG TRACE ALL

Используя эти уровни, мы можем легко фильтровать, когда и где печатать какую информацию:

logger.trace("Trace log message");
logger.debug("Debug log message");
logger.info("Info log message");
logger.error("Error log message");
logger.warn("Warn log message");
logger.fatal("Fatal log message");

Мы также можем настроить уровни для каждого пакета исходного кода в отдельности. Дополнительные сведения о настройке уровня журнала см. в нашей статье Ведение журналов Java.

4. Запись журналов в файлы

4.1. Перенаправление System.out и System.err

Можно перенаправить System.out.println в файл с помощью метода System.setOut():

PrintStream outStream = new PrintStream(new File("outFile.txt"));
System.setOut(outStream);
System.out.println("This is a baeldung article");

А в случае System.err:

PrintStream errStream = new PrintStream(new File("errFile.txt"));
System.setErr(errStream);
System.err.println("This is a baeldung article error");

При перенаправлении вывода в файл с помощью System.out или System.err мы не можем контролировать размер файла, поэтому файл продолжает расти на протяжении всего времени работы приложения.

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

4.2. Регистрация в файлы с помощью Log4J2

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

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
    <Appenders>
        <File name="fout" fileName="log4j/target/baeldung-log4j2.log"
          immediateFlush="false" append="false">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %p %m%n"/>
        </File>
    <Loggers>
        <AsyncRoot level="DEBUG">
            <AppenderRef ref="stdout"/>
            <AppenderRef ref="fout"/>
        </AsyncRoot>
    </Loggers>
</Configuration>

Или мы можем прокрутить файлы на основе размера, когда они достигнут заданного порога:

...
<RollingFile name="roll-by-size"
  fileName="target/log4j2/roll-by-size/app.log" filePattern="target/log4j2/roll-by-size/app.%i.log.gz"
  ignoreExceptions="false">
    <PatternLayout>
        <Pattern>%d{yyyy-MM-dd HH:mm:ss} %p %m%n</Pattern>
    </PatternLayout>
    <Policies>
        <OnStartupTriggeringPolicy/>
        <SizeBasedTriggeringPolicy size="5 KB"/>
    </Policies>
</RollingFile>

5. Ведение журналов во внешние системы

Как мы видели в предыдущем разделе, фреймворки регистраторов позволяют записывать журналы в файл. Точно так же они также предоставляют приложения для отправки журналов в другие системы и приложения. Это позволяет отправлять журналы в базу данных Kafka Stream или Elasticsearch с помощью приложений Log4J, а не с помощью System.out.println.

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

6. Настройка вывода журнала

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

«Хотя это было бы возможно с System.out.println, это потребовало бы много ручной работы, в то время как фреймворки ведения журналов предоставляют эту функциональность из коробки. С регистраторами мы можем просто определить шаблон в конфигурации регистратора:

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

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

7. Избегайте использования функции printStackTrace() путем протоколирования вывода исключений

Когда мы обрабатываем исключения в нашем коде, нам часто нужно узнать, какие исключения действительно произошли во время выполнения. Для этого есть два распространенных варианта: printStackTrace() или вызов регистратора.

Довольно часто можно увидеть обработку исключений, которая использует printStackTrace() для вывода подробностей об исключении:

try {
    // some code
} catch (Exception e) {
    e.printStackTrace();
}

Проблема здесь в том, что printStackTrace() выводит информацию в System.err, и мы уже сказали мы хотим избежать этого.

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

try {
    // some code
} catch (Exception e) {
    logger.log("Context message", e);
}

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

В этой статье объясняются различные причины использования logger framework и почему бы не полагаться только на System.out.println для журналов наших приложений. Хотя использование System.out.println для небольших тестовых программ оправдано, мы бы предпочли не использовать его в качестве основного источника ведения журналов для корпоративных производственных приложений.

Как всегда, примеры кода в статье доступны на GitHub.