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

System.out.println против регистраторов

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

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="foreach" 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, предоставляют несколько элементов управления уровнем ведения журнала:

  • ФАТАЛЬНО
  • ОШИБКА
  • ПРЕДУПРЕЖДАТЬ
  • ИНФОРМАЦИЯ
  • ОТЛАЖИВАТЬ
  • СЛЕД
  • ВСЕ

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

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 foreach article");

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

PrintStream errStream = new PrintStream(new File("errFile.txt"));
System.setErr(errStream);
System.err.println("This is a foreach 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/foreach-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. Заключение

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

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