Рубрики
Без рубрики

Учебник по ведению журнала Java: Основные понятия, которые помогут Вам получить Начатый

Когда дело доходит до устранения неполадок в производительности приложений, показателей уже недостаточно. Чтобы полностью подчиниться… Помечено журналированием, java, журналами, журналом.

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

Регистрация в Java может быть выполнена просто путем простой записи данных в файл, однако это не самый простой или удобный способ регистрации. Существуют платформы для Java, которые предоставляют объект, методы и методы унифицированной конфигурации, которые помогают настраивать ведение журнала, хранить журналы, а в некоторых случаях даже отправлять их в решение для централизации журналов . Кроме того, существуют также уровни абстракции, которые позволяют легко переключать базовую структуру без необходимости изменения реализации. Это может пригодиться, если вам когда–нибудь понадобится заменить библиотеку журналов по какой-либо причине – например, по производительности, конфигурации или даже простоте.

Звучит сложно? Этого не должно быть.

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

  • Уровни абстракции ведения журнала для Java
  • Возможности ведения журнала Java из коробки
  • Библиотеки ведения журналов Java, их конфигурация и использование
  • Регистрация важной информации
  • Решения для централизации журналов.

Давайте начнем!

Рамки ведения журнала: Выбор решения для ведения журнала для Вашего Java-приложения

Лучшее решение для ведения журнала для вашего Java-приложения – это… ну, это сложно. Вы должны подумать и посмотреть на свое текущее окружение и потребности организации. Если у вас уже есть платформа выбора, которую использует большинство ваших приложений, – сделайте это. Очень вероятно, что у вас уже будет установленный формат для ваших журналов. Это также означает, что у вас уже может быть простой способ отправки ваших журналов в решение для централизации журналов по вашему выбору, и вам просто нужно следовать существующему шаблону.

Однако, если в вашей организации нет какой-либо общей системы ведения журнала, перейдите и посмотрите, какой тип ведения журнала используется в используемом вами приложении. Вы используете Elasticsearch? Он использует Log4j 2 . Большинство сторонних приложений также используют Log4j 2? Если это так, подумайте о его использовании. Почему? Потому что вы, вероятно, захотите хранить свои журналы в одном месте, и вам будет проще просто работать с одной конфигурацией или шаблоном. Однако здесь есть подводный камень. Не идите по этому пути, если это означает использование устаревшей технологии, когда уже существует более новая и зрелая альтернатива.

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

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

Уровни Абстракции

Конечно, ваше приложение может использовать базовый API ведения журнала, предоставляемый Java из коробки с помощью пакета java.util.logging . В этом нет ничего плохого, но учтите, что это ограничит ваши возможности в использовании журналов. Некоторые библиотеки обеспечивают простоту настройки форматеров, стандартные отраслевые назначения и высочайшую производительность. Если вы хотите использовать одну из этих платформ, и вы также хотели бы иметь возможность переключать платформу в будущем, вам следует взглянуть на уровень абстракции поверх API ведения журнала.

SLF4J – Простой фасад ведения журнала для Java является одним из таких уровней абстракции. Он предоставляет привязки для распространенных систем ведения журнала, таких как Log4j , Обратный вход и выход из коробки java.util.ведение журнала пакет. Вы можете представить себе процесс написания сообщения журнала следующим упрощенным способом:

Но как бы это выглядело с точки зрения кода? Что ж, это очень хороший вопрос. Давайте начнем с просмотра готового java.util.logging кода. Например, если бы мы хотели просто запустить наше приложение и распечатать что-то в журнале, это выглядело бы следующим образом:

package com.sematext.blog.logging;
import java.util.logging.Level;
import java.util.logging.Logger;

public class JavaUtilsLogging {
  private static Logger LOGGER = Logger.getLogger(JavaUtilsLogging.class.getName());

  public static void main(String[] args) {
    LOGGER.log(Level.INFO, "Hello world!");
  }
}

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

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

Jun 26, 2020 2:58:40 PM com.sematext.blog.logging.JavaUtilsLogging main
INFO: Hello world!

Теперь давайте сделаем то же самое, используя слой абстракции SLF4J:

package com.sematext.blog.logging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JavaSLF4JLogging {
  private static Logger LOGGER = LoggerFactory.getLogger(JavaSLF4JLogging.class);

  public static void main(String[] args) {
    LOGGER.info("Hello world!");
  }
}

На этот раз результат немного отличается и выглядит так:

[main] INFO com.sematext.blog.logging.JavaSLF4JLogging - Hello world!

На этот раз все немного по-другому. Мы используем класс LoggerFactory для извлечения регистратора для нашего класса, и мы используем специальный метод info объекта Logger для записи сообщения журнала с использованием уровня INFO.

Начиная с SLF4J 2.0 был представлен API fluent logging , но на момент написания этого блога он все еще находится в альфа-версии и считается экспериментальным, поэтому мы пока пропустим его, так как API может измениться.

Итак, чтобы закончить со слоем абстракции – это дает нам несколько основных преимуществ по сравнению с использованием java.util.logging :

  • Общий API для ведения журнала всех ваших приложений
  • Простой способ использовать желаемую структуру ведения журнала
  • Простой способ обменять структуру ведения журнала и не проходить весь код при желании переключиться

Что Такое Уровни Журнала?

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

Например, следующие уровни журнала отсортированы от наименее до наиболее важных с некоторым объяснением того, как я их вижу:

  • ТРАССИРОВКА – Очень мелкозернистая информация, используемая только в редких случаях, когда вам нужна полная видимость происходящего. В большинстве случаев уровень ТРАССИРОВКИ будет очень подробным, но вы также можете ожидать много информации о приложении. Используйте для аннотирования шагов в алгоритме, которые не имеют отношения к повседневному использованию.
  • ОТЛАДКА – Менее зернистый, чем уровень ТРАССИРОВКИ, но все же более зернистый, чем вам нужно при обычном повседневном использовании. Уровень ОТЛАДКИ следует использовать для получения информации, которая может быть полезна для устранения неполадок и не нужна для анализа повседневного состояния приложения.
  • ИНФОРМАЦИЯ – Стандартный уровень информации журнала, указывающий на обычное действие приложения – например, “Создал пользователя {} с идентификатором {}” – это пример сообщения журнала на информационном уровне, которое предоставляет вам информацию об определенном процессе, который завершился успешно. В большинстве случаев, если вы не изучаете, как работает ваше приложение, вы можете игнорировать большинство, если не все журналы информационного уровня.
  • ПРЕДУПРЕЖДАТЬ – Уровень журнала, который обычно указывает на состояние приложения, которое может быть проблематичным, или на то, что оно обнаружило необычное выполнение. Что-то может быть не так, но это не значит, что приложение не удалось. Например, сообщение было неправильно проанализировано, потому что оно было неправильным. Выполнение кода продолжается, но мы могли бы зарегистрировать это с помощью уровня ПРЕДУПРЕЖДЕНИЯ, чтобы информировать нас и других о возможных проблемах.
  • ОШИБКА – Уровень журнала, указывающий на проблему в системе, которая не позволяет работать определенным функциям. Например, если вы предоставляете вход через социальные сети в качестве одного из способов входа в свою систему, сбой такого модуля наверняка является журналом уровня ОШИБОК.
  • ФАТАЛЬНЫЙ – Уровень журнала, указывающий, что ваше приложение столкнулось с событием, которое мешает ему работать или важной его части. ФАТАЛЬНЫЙ уровень журнала – это, например, невозможность подключиться к базе данных, на которую полагается ваша система, или к внешней платежной системе, необходимой для проверки корзины в вашей системе электронной коммерции.

Надеюсь, это прольет некоторый свет на то, что такое уровни журнала и как вы можете использовать их в своем приложении.

API ведения журнала Java

API-интерфейсы ведения журнала Java поставляются со стандартным набором ключевых элементов, о которых мы должны знать, чтобы иметь возможность продолжить и обсудить больше, чем просто базовое ведение журнала. Эти элементы классов включают в себя:

  • Регистратор – Регистратор является основным объектом, который приложение использует для выполнения вызовов ведения журнала. Объект регистратора обычно используется для одного класса или одного компонента, чтобы обеспечить привязку контекста к конкретному варианту использования.
  • LogRecord – Сущность, используемая для передачи запросов на ведение журнала между платформой, используемой для ведения журнала, и обработчиками, которые отвечают за доставку журналов.
  • Обработчик – Обработчик используется для экспорта объекта записи журнала в заданное место назначения. Этими местами назначения могут быть память, консоль, файлы и удаленные местоположения с помощью сокетов и различных API. Примером таких обработчиков является обработчик для Системный журнал или обработчик для Elasticsearch.
  • Уровень – Определяет набор стандартных степеней серьезности сообщения журнала, таких как ИНФОРМАЦИЯ, ОШИБКА и т.д. Показывает, насколько важна запись в журнале.
  • Фильтр – Дает контроль над тем, что регистрируется, а что отбрасывается. Это дает приложению возможность подключать функции, управляющие выводом журнала.
  • Форматировщик – Поддерживает форматирование объектов записи журнала. По умолчанию доступны два форматера – SimpleFormatter и XmlFormatter . Первый печатает объект записи журнала в удобочитаемой форме, используя одну или две строки. Второй записывает сообщения в стандартном формате XML.

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

Конфигурация: Как включить ведение журнала в Java?

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

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

Регистратор

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

Создание регистратора

Как мы создаем регистратор? Ну, это на самом деле зависит от выбранной нами системы ведения журнала. Но обычно это очень просто. Например, для API-интерфейсов SLF4J вы просто выполните следующие действия:

... Logger LOGGER = LoggerFactory.getLogger(SomeFancyClassName.class)

При использовании java.util.logging в качестве выбранной структуры ведения журнала мы бы сделали:

... Logger LOGGER = Logger.getLogger(SomeFancyClassName.class.getName())

При использовании Log4j 2 потребуется следующий вызов кода Java:

... Logger LOGGER = LogManager.getLogger(SomeFancyClassName.class)

Для обратного входа мы бы назвали тот же код, что и для SLF4J:

... Logger LOGGER = LoggerFactory.getLogger(SomeFancyClassName.class)

Регистрация Событий

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

Для API-интерфейсов SLF4J вы просто должны сделать следующее:

LOGGER.info("This is an info level log message!")

При использовании java.util.logging в качестве выбранной структуры ведения журнала мы бы сделали:

LOGGER.log(Level.INFO, "This is an info level log message!")

При использовании Log4j 2 мы бы сделали:

LOGGER.info("This is an info level log message!")

Наконец, при использовании обратного входа нам пришлось бы выполнить тот же вызов, что и для SLF4J:

LOGGER.info("This is an info level log message!")

SLF4J: Использование Фасада Для Ведения Журнала

SLF4J или Фасад простого ведения журнала для Java служит уровнем абстракции для различных платформ ведения журнала Java, таких как LOG4J или Logback. Это позволяет подключать различные платформы ведения журнала во время развертывания без необходимости внесения изменений в код.

Чтобы включить SLF4J в свой проект, вам необходимо включить библиотеку slf4j-api и платформу ведения журнала по вашему выбору. Для целей этого сообщения в блоге мы также включим библиотеку slf4j-simple, чтобы мы могли легко показать, как выглядит API SLF4J, не усложняя его дополнительной конфигурацией платформы ведения журнала. slf4j-простой приводит к тому, что на фасаде SLF4J печатаются все сообщения журнала с уровнем информации или выше, которые должны быть напечатаны в системе.ошибка. Из-за этого наш файл Gradle build.gradle выглядит следующим образом (вы можете найти весь проект на Sematext Github учетной записи):

dependencies {
    implementation 'org.slf4j:slf4j-api:1.7.30'
    implementation 'org.slf4j:slf4j-simple:1.7.30'
}

Код, который сгенерирует наше сообщение SLF4Jlog, выглядит следующим образом:

package com.sematext.blog.logging;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SLF4J {
  private static Logger LOGGER = LoggerFactory.getLogger(SLF4J.class);

  public static void main(String[] args) {
    LOGGER.info("This is an info level log message!");
  }
}

Мы уже видели это – мы создаем экземпляр регистратора, используя класс LoggerFactory и его метод getLogger и указывая имя класса. Таким образом, мы привязываем регистратор к имени класса, которое дает нам контекст сообщения журнала. Как только мы это сделаем, мы сможем использовать методы регистратора для создания записи журнала на заданном уровне.

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

[main] INFO com.sematext.blog.logging.SLF4J - This is an info level log message!

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

public class SLF4JParametrized {
  private static Logger LOGGER = LoggerFactory.getLogger(SLF4JParametrized.class);

  public static void main(String[] args) {
    int currentValue = 36;
    LOGGER.info("The parameter value in the log message is {}", currentValue);
  }
}

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

[main] INFO com.sematext.blog.logging.SLF4JParametrized - The parameter value in the log message is 36

Как вы можете видеть, заполнитель параметра был правильно заменен, и сообщение журнала содержит это значение. Это позволяет эффективно создавать сообщения журнала без необходимости объединения строк или использования буферов и средств записи.

Привязка SLF4J к Структуре ведения журнала во время развертывания

При использовании SLF4J вы, вероятно, не будете использовать библиотеку slf4j-simple, и вам захочется использовать ее с выделенной системой ведения журнала, чтобы ваши журналы можно было размещать в выбранном вами месте назначения или даже в нескольких местах назначения по вашему выбору.

Чтобы SLF4J мог работать с выбранной вами платформой ведения журнала в дополнение к библиотеке slf4j-api, вам потребуется одно из следующих:

  • slf4j-jdk1.4-SLF4J_VERSION.jar – привязки для java.util.logging, которые по умолчанию поставляются в комплекте с JDK.
  • slf4j-log4j12-SLF4J_VERSION.jar – привязки для Log4j версии 1.2 и, конечно, требует, чтобы библиотека log4j присутствовала в пути к классам.
  • slf4j-jcl-SLF4J_VERSION.jar – привязки для ведения журнала Jakarta Commons также называются ведением журнала Commons.
  • slf4j-nop-SLF4J_VERSION.jar – привязка, которая автоматически удаляет все сообщения журнала.
  • logback-classic-1.2.3.jar – привязки для структуры ведения журнала обратной связи.
  • logback-classic-1.2.3.jar – привязки для структуры ведения журнала обратной связи.
  • – привязки для LOG4J 2 и SLF4J до версии 1.8. log4j-slf4j18-impl-SLF4J_VERSION.jar

Начиная с версии 1.6 библиотеки SLF4J, если в пути к классам не найдено привязок, API SLF4J начнет автоматически удалять все сообщения журнала. Поэтому, пожалуйста, имейте это в виду.

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

SLF4J поддерживает Сопоставленный диагностический контекст, который представляет собой карту, на которой код приложения предоставляет пары ключ-значение, которые могут быть вставлены платформой ведения журнала в сообщения журнала. Если базовая структура ведения журнала поддерживает MDC, то фасад SLF4J передаст сохраненные пары ключ-значение в используемую структуру ведения журнала.

Если базовая структура ведения журнала поддерживает MDC, то фасад SLF4J передаст сохраненные пары ключ-значение в используемую структуру ведения журнала.

При использовании стандартного пакета java.util.logging нам не нужны какие-либо внешние зависимости. Все, что нам нужно, уже присутствует в дистрибутиве JDK, поэтому мы можем просто перейти к нему и начать включать вход в наше потрясающее приложение.

Есть два способа включить и настроить ведение журнала с помощью пакета java.util.logging – с помощью файла конфигурации или программно. Для демонстрационных целей давайте предположим, что мы хотим видеть наши сообщения журнала в одной строке, начиная с даты и времени, серьезности сообщения журнала и, конечно, самого сообщения журнала.

Настройка java.util.logging с помощью файла конфигурации

Чтобы показать вам, как использовать пакет java.util.logging , мы создали простой проект Java и поделились им в нашем репозитории Github . Код, который генерирует журнал и настраивает наше ведение журнала, выглядит следующим образом:

package com.sematext.blog.loggging;

import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

public class UtilLoggingConfiguration {
  private static Logger LOGGER = Logger.getLogger(UtilLoggingConfiguration.class.getName());

  static {
    try {
      InputStream stream = UtilLoggingConfiguration.class.getClassLoader()
          .getResourceAsStream("logging.properties");
      LogManager.getLogManager().readConfiguration(stream);
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  public static void main(String[] args) throws Exception {
    LOGGER.log(Level.INFO, "An INFO level log!");
  }
}

Мы начнем с инициализации регистратора для вашего класса конфигурации ведения журнала Util, используя имя класса. Нам также необходимо указать имя нашей конфигурации ведения журнала. По умолчанию конфигурация по умолчанию находится в файле JAVA_HOME/jre/lib/logging.properties , и мы не хотим настраивать конфигурацию по умолчанию. Из-за этого я создал новый файл под названием logging.properties, и в статическом блоке мы просто инициализируем его и передаем классу LogManager, используя его метод readConfiguration. Этого достаточно для инициализации конфигурации ведения журнала и получения следующих выходных данных:

[2020-06-29 15:34:49] [INFO   ] An INFO level log!

Конечно, вы бы выполнили инициализацию только один раз, а не в каждом классе. Имейте это в виду.

Теперь это отличается от того, что мы видели ранее в сообщении в блоге, и единственное, что изменилось, – это конфигурация, которую мы включили. Давайте теперь посмотрим на содержимое нашего файла logging.properties:

handlers=java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=[%1$tF %1$tT] [%4$-7s] %5$s %n

Вы можете видеть, что мы настроили обработчик, который используется для установки места назначения записей журнала. В нашем случае это java.util.logging. ConsoleHandler , который выводит журналы на консоль.

Затем мы сказали, какой формат мы хотели бы использовать для нашего обработчика, и мы сказали, что форматером по вашему выбору является java.util.logging. Простой форматер . Это позволяет нам предоставить формат записи журнала.

Наконец, используя свойство format, мы устанавливаем формат в [%1$TF %1$TT] [%4$-7s] %5$s%n . Это очень хороший шаблон, но он может сбить с толку, если вы видите что-то подобное в первый раз. Давайте обсудим это. SimpleFormatter использует метод String.format со следующим списком или аргументами: (формат, дата, источник, регистратор, уровень, сообщение, брошенный).

Таким образом, часть [%1$TF%1$TT] указывает форматеру принять второй аргумент и указать части даты и времени. Затем часть [%4$-7s] считывает уровень журнала и использует 7 пробелов для его форматирования. Таким образом, для ИНФОРМАЦИОННОГО уровня он добавит 3 дополнительных пробела. %5$s указывает программе форматирования принять сообщение записи журнала и распечатать его в виде строки, и, наконец, %n – это печать новой строки. Теперь это должно быть яснее.

Программная настройка java.util.logging

Давайте теперь рассмотрим, как выполнить аналогичное форматирование, настроив средство форматирования на уровне кода Java. На этот раз мы не будем использовать какой-либо файл свойств, но мы настроим наш регистратор с использованием Java.

Код, который это делает, выглядит следующим образом:

package com.sematext.blog.loggging;

import java.util.Date;
import java.util.logging.*;

public class UtilLoggingConfigurationProgramatically {
  private static Logger LOGGER = Logger.getLogger(UtilLoggingConfigurationProgramatically.class.getName());

  static {
    ConsoleHandler handler = new ConsoleHandler();
    handler.setFormatter(new SimpleFormatter() {
      private static final String format = "[%1$tF %1$tT] [%2$-7s] %3$s %n";
      @Override
      public String formatMessage(LogRecord record) {
        return String.format(format,
            new Date(record.getMillis()),
            record.getLevel().getLocalizedName(),
            record.getMessage()
        );
      }
    });
    LOGGER.setUseParentHandlers(false);
    LOGGER.addHandler(handler);
  }

  public static void main(String[] args) throws Exception {
    LOGGER.log(Level.INFO, "An INFO level log!");
  }
}

Разница между этим методом и методом, использующим файл конфигурации, заключается в следующем статическом блоке. Вместо указания местоположения файла конфигурации мы создаем новый экземпляр ConsoleHandler и мы переопределяем метод FormatMessage , который принимает объект LogRecord в качестве аргумента. Мы предоставляем формат, но мы не передаем одинаковое количество аргументов методу String.format, поэтому мы также изменили наш формат. Мы также сказали, что не хотим использовать родительский обработчик, что означает, что мы хотели бы использовать только наш собственный обработчик, и, наконец, мы добавляем наш обработчик, вызывая метод LOGGER.AddHandler . И это все – мы закончили. Вывод приведенного выше кода выглядит следующим образом:

[2020-06-29 16:25:34] [INFO   ] An INFO level log!

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

Log4j Лог4j

Log4j2 и его предшественник Log4j являются наиболее распространенной и широко известной платформой ведения журнала для Java. Он обещает улучшить первую версию библиотеки Log4j и исправить некоторые проблемы, выявленные в рамках обратной регистрации. Он также поддерживает асинхронное ведение журнала. Для целей этого урока я создал простой Java-проект, который использует Log4j 2, вы можете найти его в нашей учетной записи Github .

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

package com.sematext.blog.logging;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4JDefaultConfig {
  private static final Logger LOGGER = LogManager.getLogger(Log4JDefaultConfig.class);

  public static void main(String[] args) {
    LOGGER.info("This is an INFO level log message!");
    LOGGER.error("This is an ERROR level log message!");
  }
}

Для компиляции кода нам нужны две зависимости – API Log4j 2 и Ядро. Наш build.gradle включает в себя следующее:

dependencies {
    implementation 'org.apache.logging.log4j:log4j-api:2.13.3'
    implementation 'org.apache.logging.log4j:log4j-core:2.13.3'
}

Здесь все довольно просто – мы получаем экземпляр регистратора с помощью класса Log4J 2 LogManager и его метода getLogger. Как только у нас это будет, мы используем информацию и методы ошибок регистратора для записи записей журнала. Мы, конечно, могли бы использовать SLF4J, и мы сделаем это в одном из примеров. На данный момент, однако, давайте придерживаться чистого API Log4J 2.

По сравнению с java.util.logging нам не нужно выполнять никаких настроек. По умолчанию Log4j будет использовать ConsoleAppender для записи сообщения журнала в консоль. Запись журнала будет напечатана с помощью Шаблон прикреплен к упомянутому консольному приложению с шаблоном, определенным следующим образом: %d{ЧЧ:мм:ss.SSS} [%t] %-5-уровневый %регистратор {36}– %msg%n . Однако корневой регистратор определен для уровня ОШИБОК. Это означает, что по умолчанию журналы уровня ОШИБОК и выше будут видны только. Сообщения журнала с информацией об уровне, ОТЛАДКОЙ, ТРАССИРОВКОЙ не будут видны.

Существует несколько условий, когда Log4j2 будет использовать конфигурацию по умолчанию, но в целом вы можете ожидать, что она сработает когда нет свойство log4j.configurationfile присутствует в параметрах запуска приложения и когда это свойство не указывает на допустимый файл конфигурации. Кроме того, нет log4j2-теста.[свойства|xml|yaml|json] файлы присутствуют в пути к классу и отсутствуют log4j2.[свойства|xml|yaml|jsn] файлы присутствуют в пути к классу. Да, это означает, что вы можете легко использовать разные конфигурации ведения журнала для тестов, разные конфигурации по умолчанию для вашей кодовой базы, а затем использовать log4j.Файл конфигурации свойство и указывает на конфигурацию для данной среды.

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

09:27:10.312 [main] ERROR com.sematext.blog.logging.Log4JDefaultConfig - This is an ERROR level log message!

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

Настройка Log4J 2

Log4J 2 можно настроить одним из двух способов:

  • С помощью файла конфигурации. По умолчанию Log4J2 понимает конфигурацию, записанную в файлах свойств Java и XML-файлах, но вы также можете включить дополнительные зависимости для работы с JSON или YAML. В этом сообщении в блоге мы будем использовать этот метод.
  • Программно путем создания Фабрики конфигурации и реализаций конфигурации, или с помощью открытых API-интерфейсов в интерфейсе конфигурации, или путем вызова внутренних методов регистратора.

Давайте сначала создадим простой старомодный XML-файл конфигурации, чтобы просто распечатать время, уровень и сообщение, связанное с записью журнала. Log4J2 требует, чтобы мы вызвали этот файл log4j2.xml . Мы помещаем его в папку ресурсов нашего примера проекта и запускаем его. На этот раз результат выглядит следующим образом:



    
        
            
        
    
    
        
            
        
    

Давайте останемся здесь на минуту или две и обсудим, что мы здесь сделали. Мы начали с определения конфигурации. В этом элементе мы определили, что все сообщения, связанные со статусом, будут регистрироваться с уровнем ПРЕДУПРЕЖДЕНИЯ -.

Затем мы определили прилагаемое. Приложение несет ответственность за доставку события LogEvent к месту назначения. У вас может быть несколько таких. В нашем случае раздел Приложения содержит одно приложение консольного типа:


    

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

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


    

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

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

09:29:58.735 INFO  - This is an INFO level log message!
09:29:58.737 ERROR - This is an ERROR level log message!

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

Если бы мы хотели использовать файл свойств вместо XML, мы могли бы просто создать файл log4j2.properties и включить его в путь к классу или просто использовать свойство log4j.configuration и указать на файл свойств по нашему выбору. Для достижения аналогичных результатов, которые мы получили с конфигурацией на основе XML, мы будем использовать следующие свойства:

appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{HH:mm:ss.SSS} %-5level - %msg%n
rootLogger.level = info
rootLogger.appenderRef.stdout.ref = STDOUT

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

Приложение Log4J

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

Не очень полный список добавляющих в Log4J 2:

  • Консоль – записывает данные в System.out или System.err, по умолчанию начиная с первого (рекомендуется при входе в контейнеры)
  • Файл – приложение, которое использует файловый менеджер для записи данных в определенный файл
  • Переходящий файл – приложение, которое записывает данные в определенный файл и переносит файл в соответствии с определенной политикой
  • Файл с отображением в памяти – добавлен в версии 2.1, использует файлы с отображением в памяти и использует диспетчер виртуальной памяти операционной системы для синхронизации изменений в файле с устройством хранения
  • Flume – приложение, которое записывает данные в Apache Flume
  • Кассандра – приложение, которое записывает данные в Apache Cassandra
  • JDBC – записывает данные в базу данных с помощью стандартного драйвера JDBC
  • HTTP – записывает данные в определенную конечную точку HTTP
  • Кафка – записывает данные в Apache Кафка
  • Системный журнал – записывает данные в совместимое с системным журналом место назначения
  • ZeroMQ – записывает данные в ZeroMQ
  • Async – инкапсулирует другое приложение и использует другой поток для записи данных, что приводит к асинхронному ведению журнала

Конечно, мы не будем обсуждать каждое приложение, упомянутое выше, но давайте посмотрим на приложение Fileappender, чтобы узнать, как одновременно записывать сообщения журнала в консоль и файл.

Использование Нескольких Приложений

Давайте предположим, что у нас есть следующий вариант использования – мы хотели бы настроить наше приложение для записи всего в файл, чтобы мы могли использовать его для отправки данных во внешнее решение для управления журналами и анализа , такое как Облако сематекста и мы также хотели бы, чтобы журналы из регистраторов com.sematext.blog печатались на консоли.

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

package com.sematext.blog.logging;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4j2 {
  private static final Logger LOGGER = LogManager.getLogger(Log4j2.class);

  public static void main(String[] args) {
    LOGGER.info("This is an INFO level log message!");
    LOGGER.error("This is an ERROR level log message!");
  }
}

Тот log4j2.xml будет выглядеть следующим образом:



    
        
            
        
        
            
                %d{HH:mm:ss.SSS} [%t] %-5level - %msg%n
            
        
    
    
        
            
        
        
            
        
    

Вы можете видеть, что у меня определены два приложения – одно называется Console, которое добавляет данные в System.out или System.err. Второй называется Файл. Я предоставил свойство конфигурации имени файла, чтобы указать приложению, где должны быть записаны данные, и я сказал, что хочу добавить данные в конец файла. Имейте в виду, что эти два приложения имеют несколько разные шаблоны – файловый также регистрирует поток, в то время как консольный – нет.

У нас также определены два регистратора. У нас есть корневой регистратор, который добавляет данные в наш файловый редактор. Он делает это для всех событий журнала с уровнем информационного журнала и выше. У нас также есть второй регистратор, определенный с именем com.sematext.blog, который добавляет данные с помощью приложения консоли, а также с уровнем ИНФОРМАЦИИ.

После выполнения приведенного выше кода мы увидим следующий вывод на консоли:

15:19:54.730 INFO  - This is an INFO level log message!
15:19:54.732 ERROR - This is an ERROR level log message!

И следующий вывод в файле/tmp/log4j2.log:

15:19:54.730 [main] INFO  - This is an INFO level log message!
15:19:54.732 [main] ERROR - This is an ERROR level log message!

Вы можете видеть, что времена точно такие же, линии разные, хотя так же, как и наши узоры.

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

Компоновки Log4J

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

По умолчанию в Log4j2 доступно несколько макетов (некоторые из них требуют дополнительных зависимостей во время выполнения).:

  • Шаблон – использует строковый шаблон для форматирования событий журнала ( подробнее о макете шаблона )
  • CSV – макет для записи данных в формате CSV
  • GELF – макет для записи событий в расширенном формате журнала Graylog 1.1
  • HTML – макет для записи данных в формате HTML
  • JSON – макет для записи данных в JSON
  • RFC5424 – записывает данные в соответствии с RFC 5424 – расширенный формат системного журнала
  • Сериализованный – сериализует события журнала в массив байтов с помощью сериализации Java
  • Системный журнал – форматирует события журнала в формат, совместимый с системным журналом
  • XML – макет для записи данных в формате XML
  • YAML – макет для записи данных в формате YAML

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



    
        
            
        
    
    
        
            
        
    

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





    
    Log4j Log Messages
    



Log session start time Wed Jul 01 15:45:08 CEST 2020

Time Thread Level Logger Message
652 main INFO com.sematext.blog.logging.Log4j2 This is an INFO level log message!
653 main ERROR com.sematext.blog.logging.Log4j2 This is an ERROR level log message!

Фильтры Log4J

Фильтр позволяет проверять события журнала, чтобы определить, следует ли их публиковать и каким образом. Выполнение фильтра может завершиться одним из трех значений – ПРИНЯТЬ, ОТКЛОНИТЬ или НЕЙТРАЛЬНО.

Фильтры можно настроить в одном из следующих расположений:

  • Непосредственно в конфигурации для контекстных фильтров
  • В регистраторе для конкретной фильтрации регистратора
  • В приложении для конкретной фильтрации приложений
  • В ссылке на приложение, чтобы определить, должно ли событие журнала достичь данного приложения

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

  • Пакет – предоставляет механизм для управления скоростью, с которой обрабатываются события журнала, и их молчаливое удаление является ограничением поражен
  • Составной – предоставляет механизм для объединения нескольких фильтров
  • Порог – позволяет фильтровать на уровне журнала события журнала
  • Динамический порог – аналогичен пороговому фильтру, но позволяет включать дополнительные атрибуты, например пользователь
  • Карта – позволяет фильтровать данные, которые находятся в MapMessage
  • Маркер – сравнивает Маркер, определенный в фильтре, с Маркером в событии журнала и фильтрует на основе этого
  • Нет маркера – позволяет проверять наличие маркера в событии журнала и фильтровать на основе этой информации
  • Регулярное выражение – фильтры на основе определенного регулярного выражения
  • Скрипт – выполняет скрипт, который должен возвращать логический результат
  • Структурированные данные – позволяет фильтровать идентификатор, тип и сообщение события журнала
  • Контекстная карта потока – позволяет фильтровать данные, которые находятся в текущем контексте
  • Time – позволяет ограничить события журнала определенным временем

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



    
        
            
            
        
    
    
        
            
        
    

Выполнение нашего примера кода с приведенной выше конфигурацией Log4j2 приведет к следующему результату:

16:34:30.797 ERROR - This is an ERROR level log message!

Log4J Ведение журнала без мусора

С Log4J 2.6 было введено частично реализованное ведение журнала без мусора. Платформа повторно использует объекты, хранящиеся в ThreadLocal, и пытается повторно использовать буферы при преобразовании текста в байты.

В версии платформы 2.6 для управления возможностями ведения журнала Log4J 2 без мусора используются два свойства. Параметры log4j2.enable Threadlocals , для которых по умолчанию установлено значение true для не-веб-приложений, и log4j2.enabledirectencoders , для которых также по умолчанию установлено значение true, включают оптимизацию в log4j 2.

С версией фреймворка 2.7 было добавлено третье свойство – карта log4j2.garbagefreethreadcontextmap. Если установлено значение true, карта ThreadContext также будет использовать подход без мусора. По умолчанию для этого свойства установлено значение false.

Существует ряд ограничений, когда дело доходит до ведения журнала без мусора в Log4J 2. Доступны не все фильтры, приложения и макеты. Если вы решите использовать его, проверьте Документацию Log4J 2 по этой теме.

Log4J Асинхронное ведение журнала

Асинхронное ведение журнала является новым дополнением к Log4J 2. Цель состоит в том, чтобы как можно скорее вернуться в приложение из вызова метода Logger.log, выполнив операцию ведения журнала в отдельном потоке.

Есть несколько преимуществ и недостатков использования асинхронного ведения журнала. Преимущества заключаются в следующем:

  • Более высокая пиковая пропускная способность
  • Меньшая задержка времени отклика при регистрации

Есть и недостатки:

  • Сложная обработка ошибок
  • Не даст более высокой производительности на машинах с низким количеством процессоров

Если вы регистрируете больше, чем приложение может обработать, скорость регистрации будет выделена самым медленным приложением из-за заполнения очереди Если на этом этапе вы все еще думаете о включении асинхронного ведения журнала, вам необходимо знать, что необходимы дополнительные зависимости времени выполнения. Log4J 2 использует библиотеку Disruptor и требует ее в качестве зависимости времени выполнения. Вам также необходимо установить log4j2.селектор контекста системное свойство в org.apache.ведение журнала.log4j.core.асинхронный. Асинклоггерконтекстселектор .

Контекст потока Log4J

Log4J объединяет Сопоставленный Диагностический контекст с Вложенным Диагностическим контекстом в Контексте потока. Но давайте на минутку обсудим каждый из них отдельно.

Сопоставленный диагностический контекст или MDC – это в основном карта, которую можно использовать для хранения данных контекста конкретного потока, в котором выполняется контекст. Например, мы можем сохранить идентификатор пользователя или шаг алгоритма. В Log4J 2 вместо MDC мы будем использовать контекст потока. Контекст потока используется для связывания нескольких событий с ограниченным количеством информации.

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

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

package com.sematext.blog.logging;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;

public class Log4j2ThreadContext {
  private static final Logger LOGGER = LogManager.getLogger(Log4j2ThreadContext.class);

  public static void main(String[] args) {
    ThreadContext.put("user", "rafal.kuc@sematext.com");
    LOGGER.info("This is the first INFO level log message!");
    ThreadContext.put("executionStep", "one");
    LOGGER.info("This is the second INFO level log message!");
    ThreadContext.put("executionStep", "two");
    LOGGER.info("This is the third INFO level log message!");
  }
}

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



    
        
            
        
    
    
        
            
        
    

Вы можете видеть, что мы добавили [%X{пользователь}] [%[%X{шаг выполнения}] часть нашего шаблона. Это означает, что мы возьмем значения свойств пользователя и шага выполнения из контекста потока и включим их. Если бы мы хотели включить все присутствующие, мы бы просто добавили %X.

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

18:05:40.181 [rafal.kuc@sematext.com] [] INFO  - This is the first INFO level log message!
18:05:40.183 [rafal.kuc@sematext.com] [one] INFO  - This is the second INFO level log message!
18:05:40.183 [rafal.kuc@sematext.com] [two] INFO  - This is the third INFO level log message!

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

Что такое Маркер?

Маркеры – это именованные объекты, которые используются для обогащения данных. В то время как Контекст потока используется для предоставления дополнительной информации о событиях журнала, маркеры можно использовать для обозначения одного оператора журнала. Вы можете пометить событие журнала ВАЖНЫМ маркером, что будет означать, что приложение должно, например, сохранить событие в отдельном файле журнала.

Как это сделать? Посмотрите на следующий пример кода, в котором для этого используется фильтрация:

package com.sematext.blog.logging;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;

public class Log4J2MarkerFiltering {
  private static Logger LOGGER = LoggerFactory.getLogger(Log4J2MarkerFiltering.class);
  private static final Marker IMPORTANT = MarkerFactory.getMarker("IMPORTANT");

  public static void main(String[] args) {
    LOGGER.info("This is a log message that is not important!");
    LOGGER.info(IMPORTANT, "This is a very important log message!");
  }
}

В приведенном выше примере SLF4J используется в качестве выбранного API, а Log4J2 – в качестве платформы ведения журнала. Это означает, что наш раздел зависимостей файла сборки Gradle выглядит следующим образом:

dependencies {
    implementation 'org.slf4j:slf4j-api:1.7.30'
    implementation 'org.apache.logging.log4j:log4j-api:2.13.3'
    implementation 'org.apache.logging.log4j:log4j-core:2.13.3'
    implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.13.3'
}

В приведенном выше примере кода есть две важные вещи. Прежде всего, мы создаем статический маркер:

private static final Marker IMPORTANT = MarkerFactory.getMarker("IMPORTANT");

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

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

LOGGER.info(IMPORTANT, "This is a very important log message!");

После выполнения кода консоль покажет следующий вывод:

12:51:25.905 INFO  - This is a log message that is not important!
12:51:25.907 INFO  - This is a very important log message!

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

12:51:25.907 [main] INFO  - This is a very important log message!

Именно то, что мы хотели!

Имейте в виду, что маркеры доступны не только в Log4J. Как вы можете видеть в приведенном выше примере, они исходят от SLF4J, поэтому совместимые платформы SLF4J должны их поддерживать. Фактически, следующая структура ведения журнала, которую мы обсудим – Logback, также поддерживает маркеры, и мы рассмотрим это во время обсуждения.

Обратный вход

Войдите в систему начинается там, где заканчивается первая версия Log4J, и обещает внести в нее улучшения. Для целей этого поста в блоге я создал простой проект и поделился им в нашей учетной записи Github .

Обратный вход использует SLF4J в качестве фасада регистрации. Чтобы использовать его, нам нужно включить его в качестве зависимости проекта. В дополнение к этому нам нужны библиотеки logback-core и logback-classic. Наш раздел зависимостей файла сборки Gradle выглядит следующим образом:

dependencies {
    implementation 'ch.qos.logback:logback-core:1.2.3'
    implementation 'ch.qos.logback:logback-classic:1.2.3'
    implementation 'org.slf4j:slf4j-api:1.7.30'
}

Я также создал простой класс с целью показать, как работает структура ведения журнала обратной связи:

package com.sematext.blog.logging;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Logback {
  private static final Logger LOGGER = LoggerFactory.getLogger(Logback.class);

  public static void main(String[] args) {
    LOGGER.info("This is an INFO level log message!");
    LOGGER.error("This is an ERROR level log message!");
  }
}

Как вы можете видеть, мы генерируем два сообщения журнала. Но перед этим мы начнем с создания регистратора с помощью класса SLF4J LoggerFactory и его метода getLogger. Как только мы это сделаем, мы сможем начать генерировать сообщения журнала, используя соответствующие методы ведения журнала. Мы уже обсуждали это, когда рассматривали SLF4J ранее, поэтому я пропущу более подробное обсуждение.

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

12:44:39.162 [main] INFO com.sematext.blog.logging.Logback - This is an INFO level log message!
12:44:39.165 [main] ERROR com.sematext.blog.logging.Logback - This is an ERROR level log message!

Поведение по умолчанию будет активировано, если logback-test.xml файл не найден в пути к классам и обратном входе.groovy не найден в пути к классам и logback.xml файл отсутствует в пути к классу. В конфигурации по умолчанию используется приложение ConsoleAppender, которое выводит сообщения журнала на стандартный вывод и использует кодер PatternLayoutEncoder с шаблоном, используемым для форматирования сообщений журнала, определенным следующим образом %d{HH:mm:ss.SSS} [%поток] %-5-уровневый %регистратор {36} – %msg%n . Регистратор по умолчанию назначен на уровень журнала ОТЛАДКИ, поэтому он может быть довольно подробным.

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

Для настройки обратного входа мы можем использовать как стандартные XML-файлы, так и заводные. Мы будем использовать метод XML. Давайте просто создадим logback.xml файл в папке ресурсов нашего проекта со следующим содержимым:


    
        
            %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
        
    
    
        
    

Вы уже можете увидеть некоторое сходство с Log4j 2. У нас есть элемент корневой конфигурации, который отвечает за сохранение всей конфигурации. Внутри мы видим две секции. Первый – это раздел приложения. Мы создали одно приложение с именем STDOUT, которое выводит вывод на консоль и использует кодировщик с заданным шаблоном. Кодировщик отвечает за форматирование записи журнала. Наконец, мы определили корневой элемент, чтобы использовать уровень ИНФОРМАЦИИ в качестве уровня журнала по умолчанию и по умолчанию отправлять все сообщения в приложение с именем STDOUT.

Вывод выполнения приведенного выше кода с созданным logback.xml файл, присутствующий в пути к классу, приводит к следующему результату:

12:49:47.730 INFO  com.sematext.blog.logging.Logback - This is an INFO level log message!
12:49:47.732 ERROR com.sematext.blog.logging.Logback - This is an ERROR level log message!

Приложения для обратной регистрации

Приложение для обратной регистрации – это компонент, который используется для записи событий в журнал. У них есть свое имя и один метод, который может обработать событие.

Библиотека logback-core закладывает основу для приложений Logback и предоставляет несколько классов, готовых к использованию. Это:

  • ConsoleAppender – добавляет события журнала в System.out или System.err
  • OutputStreamAppender – добавляет события журнала в java.io . Поток вывода, предоставляющий базовые услуги для других приложений
  • FileAppender – добавляет события журнала в файл
  • RollingFileAppender – добавляет события журнала в файл с возможностью автоматического переноса файла

Библиотека logback-classic, которую мы включили в наш пример проекта, расширяет список доступных приложений и предоставляет приложения, которые могут отправлять данные во внешние системы:

  • SocketAppender – добавляет события журнала в сокет
  • SSLSocketAppender – добавляет события журнала в сокет, используя безопасное соединение
  • SMTPAppender – собирает данные в пакеты и отправляет содержимое пакета на определяемую пользователем электронную почту после указанного пользователем события происходит
  • DBAppender – добавляет данные в таблицы базы данных
  • SyslogAppender – добавляет данные в место назначения, совместимое с системным журналом
  • SiftingAppender – приложение, которое может разделять ведение журнала в соответствии с заданным атрибутом среды выполнения
  • AsyncAppender – асинхронно добавляет события в журналы

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

Использование Нескольких Приложений

Давайте предположим, что у нас есть следующий вариант использования – мы хотели бы настроить наше приложение для записи всего в файл, чтобы мы могли использовать его для отправки данных во внешнее решение для управления журналами и анализа , такое как Облако сематекста и мы также хотели бы, чтобы журналы из регистраторов com.sematext.blog печатались на консоли.

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

package com.sematext.blog.logging;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Logback {
  private static final Logger LOGGER = LoggerFactory.getLogger(Logback.class);

  public static void main(String[] args) {
    LOGGER.info("This is an INFO level log message!");
    LOGGER.error("This is an ERROR level log message!");
  }
}

Но на этот раз, logback.xml будет выглядеть немного по-другому:


    
        
            %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
        
    
    
        /tmp/logback.log
        true
        true
        
            %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
        
    
    
        
    
    
        
    

Вы можете видеть, что у меня определены два приложения – одно называется console, которое добавляет данные в System.out или System.err. Второй называется файлом и использует ch.qos.logback.core. FileAppender для записи событий журнала в файл. Я предоставил свойство конфигурации файла, чтобы указать приложению, где должны быть записаны данные, я указал, что хочу добавить данные, и я хочу немедленную очистку, чтобы увеличить пропускную способность ведения журнала. Я также добавил кодировщик для форматирования своих данных и включил поток в строку журнала.

Разница между предыдущими примерами также заключается в определении регистраторов. У нас есть корневой регистратор, который добавляет данные в наш файловый редактор. Он делает это для всех событий журнала с уровнем информационного журнала и выше. У нас также есть второй регистратор, определенный с именем com.sematext.blog, который добавляет данные с помощью приложения консоли.

После выполнения приведенного выше кода мы увидим следующий вывод на консоли:

12:54:09.077 INFO  com.sematext.blog.logging.Logback - This is an INFO level log message!
12:54:09.078 ERROR com.sematext.blog.logging.Logback - This is an ERROR level log message!

И следующий вывод в файле/tmp/logback.log:

12:54:09.077 [main] INFO  com.sematext.blog.logging.Logback - This is an INFO level log message!
12:54:09.078 [main] ERROR com.sematext.blog.logging.Logback - This is an ERROR level log message!

Вы можете видеть, что время точно такое же, линии разные, хотя так же, как и наши кодеры.

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

Кодеры обратного входа

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

Прямо сейчас в Logback доступны два кодера:

  • PatternLayoutEncoder – кодировщик, который принимает шаблон и кодирует событие журнала на основе этого шаблона
  • LayoutWrappingEncoder – кодировщик, который закрывает разрыв между текущей версией обратного входа и версиями до 0.9.19, в которых вместо шаблонов использовались экземпляры макета.

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


    
        
            %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
            true
        
    
    
        
    

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

13:16:30.724 INFO  com.sematext.blog.logging.Logback - This is an INFO level log message!
13:16:30.726 ERROR com.sematext.blog.logging.Logback - This is an ERROR level log message!

Макеты обратного входа

Макет обратного входа – это компонент, который отвечает за преобразование входящего события в строку. Вы можете написать свой собственный макет и включить его в приложение, используя ch.qos.logback.code.encoder. Компоновочный упаковочный кодер:


    
        
    

Или вы можете использовать шаблон, который является гибким и предоставляет нам множество способов форматирования наших событий журнала. Фактически, мы уже использовали PatternLayoutEncoder в наших примерах обратного входа! Если вас интересуют все доступные параметры форматирования, ознакомьтесь с документацией Logback по макетам .

Фильтры обратного входа

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

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

package com.sematext.blog.logging.logback;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;

public class SampleFilter extends Filter {
  @Override
  public FilterReply decide(ILoggingEvent event) {
    if (event.getMessage().contains("ERROR")) {
      return FilterReply.ACCEPT;
    }
    return FilterReply.DENY;
  }
}

Вы можете видеть, что мы расширяем класс Filter и предоставляем реализацию метода decide (событие iloggingevent). В нашем случае мы просто проверяем, содержит ли сообщение заданное строковое значение, и если оно содержит, мы принимаем сообщение, возвращая FilterReply. принимать . В противном случае мы отклоняем событие журнала, запустив FilterReply. отрицать .

Мы можем включить фильтр в наше приложение, включив тег фильтра и указав класс:


    
        
            %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
        
        
    
    
        
    

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

13:52:12.451 ERROR com.sematext.blog.logging.Logback - This is an ERROR level log message!

Есть также готовые фильтры. Фильтр ch.qos.logback.classic.. Фильтр уровня позволяет нам фильтровать события на основе точного соответствия уровню журнала. Например, чтобы отклонить все журналы уровня ИНФОРМАЦИИ, мы могли бы использовать следующую конфигурацию:


    
        
            %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
        
        
            INFO
            DENY
            ACCEPT
        
    
    
        
    

ch.qos.обратный вход.классический.фильтр. Пороговый фильтр позволяет нам фильтровать события журнала ниже указанного порога. Например, чтобы отбросить все события журнала, уровень которых ниже, чем ПРЕДУПРЕЖДЕНИЕ, поэтому в и за его пределами мы могли бы использовать следующий фильтр:


    
        
            %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
        
        
            WARN
        
    
    
        
    

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

Сопоставленные диагностические контексты в обратном входе

Как мы уже обсуждали при обсуждении Log4J2, MDC или Сопоставленные диагностические контексты – это способ для разработчиков предоставить дополнительную контекстную информацию, которая будет включена вместе с событиями журнала, если мы пожелаем. MDC можно использовать для различения выходных данных журнала из разных источников – например, в средах с высокой степенью параллелизма. Управление MDC осуществляется на основе каждого потока.

Обратный вход использует API SLF4J для использования MDC. Например, давайте рассмотрим следующий код:

package com.sematext.blog.logging;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

public class LogbackMDC {
  private static final Logger LOGGER = LoggerFactory.getLogger(LogbackMDC.class);

  public static void main(String[] args) {
    MDC.put("user", "rafal.kuc@sematext.com");
    LOGGER.info("This is the first INFO level log message!");
    MDC.put("executionStep", "one");
    LOGGER.info("This is the second INFO level log message!");
    MDC.put("executionStep", "two");
    LOGGER.info("This is the third INFO level log message!");
  }
}

Мы использовали org.slf4j. Класс MDC и его метод put для предоставления дополнительной контекстной информации. В нашем случае мы предоставляем два свойства – user и executionStep.

Чтобы иметь возможность отображать добавленную контекстную информацию, нам нужно изменить наш шаблон, например, так:


    
        
            %d{HH:mm:ss.SSS} [%X{user}] [%X{executionStep}] %-5level %logger{36} - %msg%n
        
    
    
        
    

Я добавил два новых элемента: [%X{пользователь}] [%[%X{шаг выполнения}] . С помощью %{name_of_the_mdc_property} мы можем легко включить нашу дополнительную контекстную информацию.

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

14:14:10.687 [rafal.kuc@sematext.com] [] INFO  com.sematext.blog.logging.LogbackMDC - This is the first INFO level log message!
14:14:10.688 [rafal.kuc@sematext.com] [one] INFO  com.sematext.blog.logging.LogbackMDC - This is the second INFO level log message!
14:14:10.688 [rafal.kuc@sematext.com] [two] INFO  com.sematext.blog.logging.LogbackMDC - This is the third INFO level log message!

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

Конечно, это всего лишь простой пример, и сопоставленные диагностические контексты можно использовать в сложных сценариях, таких как распределенные архитектуры клиент-сервер. Если вам интересно узнать больше, ознакомьтесь с документацией Logback, посвященной MDC . Вы также можете использовать MDC в качестве значения дискриминатора для Siftingappender и направлять свои журналы на основе этого.

Маркеры в обратном журнале

Когда мы обсуждали Log4J 2, мы видели пример использования маркеров для фильтрации. Я также обещал вернуться к этой теме и показать вам другой вариант использования. Например, на этот раз мы попробуем отправить электронное письмо, когда появится сообщение журнала с важным маркером.

Во-первых, нам нужно иметь для этого какой-то код:

package com.sematext.blog.logging;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;

public class LogbackMarker {
  private static Logger LOGGER = LoggerFactory.getLogger(LogbackMarker.class);
  private static final Marker IMPORTANT = MarkerFactory.getMarker("IMPORTANT");

  public static void main(String[] args) {
    LOGGER.info("This is a log message that is not important!");
    LOGGER.info(IMPORTANT, "This is a very important log message!");
  }
}

Мы уже видели этот пример. Код таков, но на этот раз мы используем SLF4J в качестве API по нашему выбору, а Logback – в качестве платформы ведения журнала.

Ваш logback.xml файл будет выглядеть следующим образом:


    
        
            %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
        
    
    
        
            importance
            not_important
        
        
            
                /tmp/logback-${importance}.log
                false
                true
                
                    %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
                
            
        
    
    
        
    
    
        
    

На этот раз конфигурация немного сложнее. Мы использовали SiftingAppender, чтобы иметь возможность создавать два файла, в зависимости от значения, возвращаемого нашим маркердискриминатором. Наша реализация дискриминатора проста и возвращает имя маркера, если ВАЖНЫЙ маркер присутствует в сообщении журнала, и значение по умолчанию, если его нет. Код выглядит следующим образом:

package com.sematext.blog.logging.logback;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.sift.AbstractDiscriminator;
import org.slf4j.Marker;

public class MarkerDiscriminator extends AbstractDiscriminator {
  private String key;
  private String defaultValue;

  @Override
  public String getDiscriminatingValue(ILoggingEvent iLoggingEvent) {
    Marker marker = iLoggingEvent.getMarker();
    if (marker != null && marker.contains("IMPORTANT")) {
      return marker.getName();
    }
    return defaultValue;
  }

  public String getKey() { return key; }
  public void setKey(String key) { this.key = key; }
  public String getDefaultValue() { return defaultValue; }
  public void setDefaultValue(String defaultValue) { this.defaultValue = defaultValue; }
}

Однако давайте вернемся к вашей конфигурации обратного входа. Приложение sift, которое у нас есть, использует то же имя переменной, что и ключ в нашем дискриминаторе. Это означает, что для каждого из значений, возвращаемых дискриминатором, будет создано новое дополнение файла. В нашем случае будет два файла: /tmp/logback-ВАЖНО.log и /tmp/обратный вход-не_импортант.log .

После выполнения кода файл /tmp/logback-IMPORTANT.log будет содержать следующее сообщение журнала:

11:08:47.543 [main] INFO  c.s.blog.logging.LogbackMarker - This is a very important log message!

В то время как второй файл, файл/tmp/logback-not_important.log будет содержать следующее сообщение журнала:

11:08:47.537 [main] INFO  c.s.blog.logging.LogbackMarker - This is a log message that is not important!

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

11:08:47.537 INFO  c.s.blog.logging.LogbackMarker - This is a log message that is not important!
11:08:47.543 INFO  c.s.blog.logging.LogbackMarker - This is a very important log message!

Как вы можете видеть на этом простом примере, все работает так, как мы хотели.

Ведение журнала и мониторинг Java с помощью решений для управления журналами

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

Полностью управляемое решение для централизации журналов даст вам свободу в том, что вам не нужно управлять еще одной, обычно довольно сложной, частью вашей инфраструктуры. Это позволит вам управлять множеством источников для ваших журналов. Возможно, вам захочется включить журналы, такие как JVM журналы сборки мусора , в ваше решение для управляемых журналов. После их включения для ваших приложений и систем, работающих на JVM, вы захотите разместить их в одном месте для корреляции, анализа , и чтобы помочь вам настройте сборку мусора в экземплярах JVM. Оповещение о журналах, агрегирование данных, сохранение и повторный запуск запросов, подключение вашего любимого программного обеспечения для управления инцидентами. Сопоставление журналов данных с метриками исходящий из приложений JVM , системы и инфраструктуры , реального пользователя и Конечные точки API – это то, на что способны такие платформы, как Облако сематекста . И, конечно, помните, что журналы приложений – это еще не все.

Выводы

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

В этой статье мы начали с основ ведения журнала в ваших Java-приложениях. Мы узнали, какие есть варианты, когда дело доходит до ведения журнала Java, как добавить ведение журнала в ваше приложение, как его настроить. Мы также рассмотрели некоторые расширенные функции фреймворков ведения журнала, такие как Log4j и Logback. Мы узнали о SLF4J и о том, как он помогает в безопасной проверке приложения на предмет будущих изменений.

Я надеюсь, что эта статья дала вам представление о том, как работать с журналами приложений Java и почему вам следует начать работать с ними прямо сейчас, если вы еще этого не сделали. Удачи!

Оригинал: “https://dev.to/gr0/java-logging-tutorial-basic-concepts-to-help-you-get-started-3mkh”