Java логирование ошибок в файл

Обновлено: 02.07.2024

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

  • JUL — java.util.logging
  • log4j
  • JCL — jakarta commons logging
  • Logback
  • SLF4J — simple logging facade for java

System.err.println

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

Java.util.logging

Данный фреймворк включен в стандарт и поставляется вместе с JDK, поэтому ничего дополнительно скачивать и подключать вам не надо. JUL имеет следующие уровни логгирования по возрастанию: FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE, а так же ALL и OFF, включающий и отключающий все уровни соответственно.
Логгер создается вызовом одного из статических методов класса java.util.logging.Logger:


Вторая группа методов имеет следующие вариации:


Для того что бы JUL применил данную конфигурацию нужно передать параметр -Djava.util.logging.config.file = <путь до файла>, либо при старте приложения выполнить код:

Log4j

Данный фреймворк на текущий момент имеет уже вторую версию, которая увы не совместима с первой. Поскольку первая версия log4j существует достаточно давно и, в виду ее большой популярности, существует не мало статей на просторах интернета, сегодня мы рассмотрим вторую. Для использования log4j2 вам необходимо подключить библиотеки log4j-api-2.x и log4j-core-2.x. Log4j имеет несколько отличное от JUL именование уровней логгирования: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, а так же ALL и OFF включающий и отключающий все уровни соответственно.
Логгер создается вызовом статического метода класса org.apache.logging.log4j.Logger:


Логгер умеет принимать помимо привычных нам String, Object и Throwable еще два новых типа — MapMessage и Marker:


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


Методы log в log4j2 выглядят так:

  • BurstFilter
  • CompositeFilter
  • DynamicThresholdFilter
  • MapFilter
  • MarkerFilter
  • RegexFilter
  • StructuredDataFilter
  • ThreadContextMapFilter
  • ThresholdFilter
  • TimeFilter
  • AsyncAppender
  • ConsoleAppender
  • FailoverAppender
  • FileAppender
  • FlumeAppender
  • JDBCAppender
  • JMSAppender
  • JPAAppender
  • MemoryMappedFileAppender
  • NoSQLAppender
  • OutputStreamAppender
  • RandomAccessFileAppender
  • RewriteAppender
  • RollingFileAppender
  • RollingRandomAccessFileAppender
  • RoutingAppender
  • SMTPAppender
  • SocketAppender
  • SyslogAppender

Commons-logging

Довольно старый проект, который представляет собой обертку над JUL и log4j, не привносящая никакого дополнительного функционала. Уровни логгирования у JCL совпадают с log4j, а в случае взаимодействия с JUL происходит следующее сопоставление:


Для использования JCL подключаем commons-logging-1.x.jar. Создаем логгер вызовом метода фабрики:


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


Указать файл конфигурации JCL можно следующим образом:

Logback

Данный фреймворк используется только в связке с оберткой SLF4J, которую мы будем рассматривать позднее. Для начала работы вам необходимы logback-core-1.x.jar и logback-classic-1.x.x.jar, а также slf4j-api-1.x.x.jar.
Взаимодействие с логгером мы будем осуществлять через API предоставляемый оберткой SLF4J. Уровни логгирования совпадают с log4j. Создание логгера в таком случае выглядит следующим образом:


Названия методов совпадают с уровнями логгирования и имеют вид:

SLF4J

В программировании принято логировать практически все. А в Java – так вообще все и даже немного больше.

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

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

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

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

Иногда логи достигают нескольких гигабайт в сутки. Это нормально.

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

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

На основе log4j потом было написано еще несколько логгеров.

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

Весь процесс логирования состоит из трех частей.

Первая часть – это сбор информации.

Вторая часть – это фильтрование собранной информации.

Третья часть – это запись отобранной информации.

Начнем со сбора. Вот типичный пример класса, который ведет лог:

Обрати внимание на слова, выделенные красным.

Строка 3 – создание объекта logger . Такой статический объект создают практически в каждом классе! Ну, разве что кроме классов, которые ничего не делают, а только хранят данные.

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

Строка 7 – в логгер пишется информация о вызове метода. Обрати внимание – это первая строчка метода. Только метод вызвался – сразу пишем информацию в лог.

Мы вызываем метод debug, это значит, что важность информации «уровня DEBUG». Этот факт используется на уровне фильтрации. Об этом я расскажу через пару минут.

Строка 17 – мы перехватили исключение и… сразу же записали его в лог! Именно так и нужно делать.

На этот раз мы вызываем метод error, что сразу придает информации статус «ERROR»

Logger - 2

Обычно настройки логгера log4j задаются в файле log4j.properties.

В этом файле можно задать несколько appender’ов – объектов, в которые будут писаться данные. Есть источники данных, а есть – аппендеры – противоположные по смыслу объекты. Объекты, куда как бы «стекают» данные, если их можно представить в виде воды.

Вот тебе несколько примеров:

Строки 1 и 4 – это комментарии

Там же, через запятую, мы указываем имя объекта (сами придумываем), куда будет писаться лог. В строках 5-8 идут его настройки.

Строка 5 – указываем тип апендера – консоль ( ConsoleAppender ).

Строка 6 – указываем, куда именно будем писать – System.out.

Строка 7 – задаем класс, который будет управлять шаблонами записей – PatternLayout.

Строка 8 – задаем шаблон для записи, который будет использоваться. В примере выше это дата и время.

А вот как выгладит запись в файл:

Строка 5 – указываем тип апендера – файл ( RollingFileAppender ).

Строка 6 – указываем имя файла – куда писать лог.

Строка 7 – указываем максимальный размер лога. При превышении размера, начнет писаться новый файл.

Строка 8 – указываем количество старых файлов логов, которые надо хранить.

Строки 6 и 15 – мы задаем свой уровень фильтрации для каждого апендера.

Шаг 1. Добавляешь в класс импорты:

Шаг 2. Становишься курсором на эти строчки и нажимаешь Alt+Enter в Intellij IDEA

Шаг 3. Выбираешь пункт File jar on web.

Шаг 4. Выбираешь – slf4j-log4j13.jar

Шаг 5. Указываешь, куда скачать библиотеку (jar)

Шаг 6. Пользуешься нужными тебе классами.

В ходе моей работы в компании DataArt я, в числе прочего, занимаюсь менторской деятельностью. В частности это включает в себя проверку учебных заданий сделанных практикантами. В последнее время в заданиях наметилась тенденция «странного» использования логеров. Мы с коллегами решили включить в текст задания ссылку на статью с описанием java logging best practices, но оказалось, что такой статьи в которой бы просто и без лишних деталей на практике объяснялось бы как надо писать в лог на Java, вот так вот с ходу не находится.

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

  • Весь код примеров использует java.util.logging framework. Вопрос «Какой из фреймворков логирования ниболее кошерен» я оставлю за кадром. Скажу только что до java.util.logging проще всего дотянуться ибо он уже идет вместе с JRE и на самом деле рассказанное в данной статье с минимальными косметическими правками верно для подавляющего большинства систем логирования.
  • В целом рецепты приведенные в данной статье не являются единственно верными, есть моменты о которых можно поспорить, но в целом эти рецепты используются многие годы, многими разработчиками, во многих проектах и они достаточно хороши чтобы им следовать если у Вас нет каких-то совсем уже серьезных возражений.
  • В статье не рассматриваются такие «продвинутые» топики как:
    • Конфигурирование уровней для отдельных логеров
    • Форматирования логов
    • Асинхронное логирование
    • Создание собственных уровней логирования в Log4J
    • Контекстное логирование
    • И многое другое
    Пример №1
    Хорошо
    1. Логер это статическое поле класса инициализируемое при загрузке класса, имеет простое, короткое имя, важно чтобы во всех Ваших классах переменная логера называлась одинаково (это диктуется общим правилом, одинаковые вещи в программе должны делаться одинаковым образом).
    2. В качестве имени логера я использую имя класса, на самом деле это не единственный способ, можно пытаться организовать какую-то свою иерархию логирования (например transport layer/app layer для подсистем имеющих дело с обменом данными), но как показывает практика выдумывать и главное потом неукоснительно следовать такой иерархии крайне сложно, а вариант с именами логеров совпадающими с именами классов весьма хорош и используется в 99% проектов
    3. Здесь для записи в лог я использую короткий метод .info, а не более общий метод .log, так много лаконичнее
    4. Имя логера берется как SomeClass.class.getName(), а не как «com.dataart.demo.java.logging.SomeClass», оба способа по идее одинаковы, но первый защищает Вас от сюрпризов при рефакторинге имени/пакета класса
    Плохо


    По сути тоже самое но букв больше и читается не так легко.

    Замечание между примерами
    Пример №2
    Хорошо
    Плохо


    Если логировать только ex.toString(), то потом Вы не сможете понять в какой строке изначально сработало исключение.

    Пример №3

    Какие тут есть варианты

    По умолчанию: Файл logging.properties для уровня INFO, вывод в консоль

    создаст вот такие файлы (последняя колонка — размер в байтах)

    Мы указали максимальный размер 50 байтов, в реальной жизни надо скорее указывать не меньше мегабайта, например вот так (я знаю, что 1000000 это чуть меньше мегабайта, но кому охота по памяти писать 1048576, если суть дела это фактически не меняет)

    copy & paste конфиг для реальной жизни, его вполне хватает для большинства service, console и desktop приложений.

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

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

    Вот так

    java Djava.util.logging.config.file=logging.properties com.dataart.application.ClassName

    Но к сожалению менять строку запуска не всегда можно или не всегда удобно. Второй способ тоже неплохо работает.

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

    • JUL — java.util.logging
    • log4j
    • JCL — jakarta commons logging
    • Logback
    • SLF4J — simple logging facade for java

    System.err.println

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

    Java.util.logging

    Данный фреймворк включен в стандарт и поставляется вместе с JDK, поэтому ничего дополнительно скачивать и подключать вам не надо. JUL имеет следующие уровни логгирования по возрастанию: FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE, а так же ALL и OFF, включающий и отключающий все уровни соответственно.
    Логгер создается вызовом одного из статических методов класса java.util.logging.Logger:


    Вторая группа методов имеет следующие вариации:


    Для того что бы JUL применил данную конфигурацию нужно передать параметр -Djava.util.logging.config.file = <путь до файла>, либо при старте приложения выполнить код:

    Log4j

    Данный фреймворк на текущий момент имеет уже вторую версию, которая увы не совместима с первой. Поскольку первая версия log4j существует достаточно давно и, в виду ее большой популярности, существует не мало статей на просторах интернета, сегодня мы рассмотрим вторую. Для использования log4j2 вам необходимо подключить библиотеки log4j-api-2.x и log4j-core-2.x. Log4j имеет несколько отличное от JUL именование уровней логгирования: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, а так же ALL и OFF включающий и отключающий все уровни соответственно.
    Логгер создается вызовом статического метода класса org.apache.logging.log4j.Logger:


    Логгер умеет принимать помимо привычных нам String, Object и Throwable еще два новых типа — MapMessage и Marker:


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


    Методы log в log4j2 выглядят так:

    • BurstFilter
    • CompositeFilter
    • DynamicThresholdFilter
    • MapFilter
    • MarkerFilter
    • RegexFilter
    • StructuredDataFilter
    • ThreadContextMapFilter
    • ThresholdFilter
    • TimeFilter
    • AsyncAppender
    • ConsoleAppender
    • FailoverAppender
    • FileAppender
    • FlumeAppender
    • JDBCAppender
    • JMSAppender
    • JPAAppender
    • MemoryMappedFileAppender
    • NoSQLAppender
    • OutputStreamAppender
    • RandomAccessFileAppender
    • RewriteAppender
    • RollingFileAppender
    • RollingRandomAccessFileAppender
    • RoutingAppender
    • SMTPAppender
    • SocketAppender
    • SyslogAppender

    Commons-logging

    Довольно старый проект, который представляет собой обертку над JUL и log4j, не привносящая никакого дополнительного функционала. Уровни логгирования у JCL совпадают с log4j, а в случае взаимодействия с JUL происходит следующее сопоставление:


    Для использования JCL подключаем commons-logging-1.x.jar. Создаем логгер вызовом метода фабрики:


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


    Указать файл конфигурации JCL можно следующим образом:

    Logback

    Данный фреймворк используется только в связке с оберткой SLF4J, которую мы будем рассматривать позднее. Для начала работы вам необходимы logback-core-1.x.jar и logback-classic-1.x.x.jar, а также slf4j-api-1.x.x.jar.
    Взаимодействие с логгером мы будем осуществлять через API предоставляемый оберткой SLF4J. Уровни логгирования совпадают с log4j. Создание логгера в таком случае выглядит следующим образом:


    Названия методов совпадают с уровнями логгирования и имеют вид:

    SLF4J

    Читайте также: