JavaRush /Java блог /Java Developer /Зачем нужно логирование
Автор
Jesse Haniel
Главный архитектор программного обеспечения в Tribunal de Justiça da Paraíba

Зачем нужно логирование

Статья из группы Java Developer
Привет! При написании лекций я особо отмечаю, если какая-то конкретная тема обязательно будет использоваться в реальной работе. Зачем нужно логирование - 1 Так вот, ВНИМАНИЕ! Тема, которой мы коснемся сегодня, точно пригодится тебе на всех твоих проектах с первого дня работы. Мы поговорим о логировании. Тема эта совсем не сложная (я бы даже сказал легкая). Но на первой работе и без того будет достаточно стресса, чтобы еще разбираться с очевидными вещами, поэтому лучше досконально разобрать ее сейчас :) Итак, начнем. Что такое логирование? Логирование — это запись куда-то данных о работе программы. Место, куда эти данные записываются называется «лог». Возникает сразу два вопроса — куда и какие данные записываются? Начнем с «куда». Записывать данные о работе программы можно во множество разных мест. Например, ты во время учебы часто выводил данные в консоль с помощью System.out.println(). Это настоящее логирование, хоть и самое простое. Конечно, для клиента или команды поддержки продукта это не очень удобно: они явно не захотят устанавливать IDE и мониторить консоль :) Есть и более привычный человеку формат записи информации — в текстовый файл. Людям гораздо удобнее читать их в таком виде, и уж точно гораздо удобнее хранить! Теперь второй вопрос: какие данные о работе программы должны записываться в лог? А вот здесь все зависит от тебя! Система логирования в Java очень гибкая. Ты можешь настроить ее таким образом, что в лог попадет весь ход работы твоей программы. Это, с одной стороны, хорошо. Но с другой — представь себе, каких размеров могут достичь логи Facebook или Twitter, если туда писать вообще все. У таких крупных компаний наверняка есть возможность хранить даже такое количество информации. Но вообрази, как сложно будет искать информацию об одной критической ошибке в логах на 500 гигабайт текста? Это даже хуже, чем иголка в стоге сена. Поэтому логирование в Java можно настроить так, чтобы в журнал (лог) записывались только данные об ошибках. Или даже только о критических ошибках! Хотя, говорить «логирование в Java» не совсем верно. Дело в том, что потребность ведения логов возникла у программистов раньше, чем этот функционал был добавлен в язык. И к тому времени, как в Java появился собственная библиотека для логирования, все уже пользовались библиотекой log4j. История появления логирования в Java на самом деле очень долгая и познавательная, на досуге можешь почитать этот пост на Хабре. Короче говоря, своя библиотека логирования в Java есть, но ей почти никто не пользуется :) Позже, когда появились несколько разных библиотек логирования, и все программисты начали пользоваться разными, возникла проблема совместимости. Чтобы люди не делали одно и то же с помощью десятка разных библиотек с разными интерфейсами, был создан абстрагирующий фреймворк slf4j («Service Logging Facade For Java»). Абстрагирующим он называется потому, что хотя ты и пользуешься классами slf4j и вызываешь их методы, под капотом у них работают все предыдущие фреймворки логирования: log4j, стандартный java.util.logging и другие. Если тебе в данный момент нужна какая-то специфическая фича log4j, которой нет у других библиотек, но ты не хотел бы при этом жестко привязывать проект именно к этой библиотеке, просто используй slf4j. А о она уже «дернет» методы log4j. Если ты передумаешь и решишь, что фичи log4j тебе больше не нужны, тебе надо только перенастроить «обертку» (то есть slf4j) на использование другой библиотеки. Твой код не перестанет работать, ведь в нем ты вызываешь методы slf4j, а не конкретной библиотеки. Небольшое отступление. Чтобы следующие примеры заработали, тебе нужно скачать библиотеку slf4j отсюда, и библиотеку log4j отсюда. Далее архив нужно распаковать,и добавить нужные нам jar-файлы в classpath через Intellij IDEA. Пункты меню: File -> Project Structure -> Libraries Выбираешь нужные jar-ники и добавляешь в проект (в архивах, которые мы скачали, лежит много jar’ников, посмотри нужные на картинках) Зачем нужно логирование - 2Зачем нужно логирование - 3Примечание — эта инструкция для тех студентов, которые не умеют использовать Maven. Если ты умеешь им пользоваться, лучше попробуй начать с него: это обычно намного проще Если используешь Maven, добавь такую зависимость:

    	<dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.14.0</version>
    	</dependency>
Отлично, с настройками разобрались :) Давай рассмотрим, как работает slf4j. Как же нам сделать так, чтобы ход работы программы куда-то записывался? Для этого нам нужны две вещи — логгер и аппендер. Начнем с первого. Логгер — это объект, который полностью управляет ведением записей. Создать логгер очень легко: это делается с помощью статического метода — LoggerFactory.getLogger(). В качестве параметра в метод нужно передать класс, работа которого будет логироваться. Запустим наш код:

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

public class MyTestClass {

   public static final Logger LOGGER = LoggerFactory.getLogger(MyTestClass.class);

   public static void main(String[] args) {

       LOGGER.info("Test log record!!!");
       LOGGER.error("В программе возникла ошибка!");
   }
}
Вывод в консоль: ERROR StatusLogger No Log4j 2 configuration file found. Using default configuration (logging only errors to the console), or user programmatically provided configurations. Set system property 'log4j2.debug' to show Log4j 2 internal initialization logging. See https://logging.apache.org/log4j/2.x/manual/configuration.html for instructions on how to configure Log4j 2 15:49:08.907 [main] ERROR MyTestClass - В программе возникла ошибка! Что же мы тут видим? Сначала мы видим сообщение об ошибке. Она появилась, потому что сейчас у нас не хватает необходимых настроек. Поэтому наш логгер сейчас умеет выводить только сообщения об ошибках (ERROR) и только в консоль. Метод logger.info() выполнен не был. А вот logger.error() сработал! В консоли появилась текущая дата, метод, где возникла ошибка (main), слово ERROR и наше сообщение! ERROR — это уровень логгрования. В общем, если запись в логе помечена словом ERROR, значит, в этом месте программы произошла ошибка. Если запись помечена словом INFO — значит это просто текущая информация о нормальной работе программы. В библиотеке SLF4J довольно много разных уровней логгирования, которые позволяют гибко настроить ведение журнала. Управлять ими очень легко: вся необходимая логика уже заложена в класс Logger. Тебе достаточно просто вызывать нужные методы. Если ты хочешь залогировать обычное сообщение, вызывай метод logger.info(). Сообщение об ошибке — logger.error(). Вывести предупреждение — logger.warn() Теперь поговорим об аппендере. Аппендер — это место, куда приходят твои данные. Можно сказать, противоположность источнику данных — «точка B». По умолчанию данные выводятся в консоль. Обрати внимание, в предыдущем примере нам не пришлось ничего настраивать: текст появился в консоли сам, но при этом логгер из библиотеки log4j умеет выводить в консоль только сообщения уровня ERROR. Людям же, очевидно, удобнее читать логи из текстового файла и хранить логи в таких же файлах. Чтобы изменить поведение логгера по умолчанию, нам нужно сконфигурировать свой файловый аппендер. Для начала, прямо в папке src нужно создать файл log4j.xml, или в папке resources, если используешь Maven, or in the resources folder, in case you use Maven. С форматом xml ты уже знаком, у нас недавно была лекция про него :) Вот таким будет его содержимое:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
   <Appenders>
       <File name="MyFileAppender" fileName="C:\Users\Username\Desktop\testlog.txt" immediateFlush="false" append="false">
           <PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
       </File>
   </Appenders>
   <Loggers>
       <Root level="INFO">
           <AppenderRef ref="MyFileAppender"/>
       </Root>
   </Loggers>
</Configuration>
Выглядит не особо-то и сложно :) Но давай все-таки пройдемся по содержимому.
<Configuration status="INFO">
Это так называемый status-logger. Он не имеет отношения к нашему логгеру и используется во внутренних процессах log4j. Можешь установить status=”TRACE” вместо status=”INFO”, и в консоль будет выводиться вся информация о внутренней работе log4j (status-logger выводит данные именно в консоль, даже если наш аппендер для программы будет файловым). Нам это сейчас не нужно, поэтому оставим все как есть.

<Appenders>
   <File name="MyFileAppender" fileName="C:\Users\Евгений\Desktop\testlog.txt" append="true">
       <PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
   </File>
</Appenders>
Тут мы создаем наш аппендер. Тег <File> указывает что он будет файловым. name="MyFileAppender" — имя нашего аппендера. fileName="C:\Users\Username\Desktop\testlog.txt" — путь к лог-файлу, куда будут записываться все данные. append="true" — нужно ли дозаписывать ли данные в конец файла. В нашем случае так и будет. Если установить значение false, при каждом новом запуске программы старое содержимое лога будет удаляться. <PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> — это настройки форматирования. Здесь мы с помощью регулярных выражений можем настраивать формат текста в нашем логе.

<Loggers>
       <Root level="INFO">
           <AppenderRef ref="MyFileAppender"/>
       </Root>
</Loggers>
Здесь мы указываем уровень логгирования (root level). У нас установлен уровень INFO: то есть, все сообщения уровней выше INFO (по таблице, которую мы рассматривали выше) в лог не попадут. У нас в программе будет 3 сообщения: одно INFO, одно WARN и одно ERROR. С текущей конфигурацией все 3 сообщения будут записаны в лог. Если ты поменяешь значение root level на ERROR, в лог попадет только последнее сообщение из LOGGER.error(). Кроме того, сюда же помещается ссылка на аппендер. Чтобы создать такую ссылку, нужно внутри тега <Root> создать тег <ApprenderRef> и добавить ему параметр ref=”имя твоего аппендера”. Имя аппендера мы создали вот тут, если ты забыл: <File name="MyFileAppender" А вот и код нашей программы!

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

public class MyTestClass {

   public static final Logger LOGGER = LoggerFactory.getLogger(MyTestClass.class);

   public static void main(String[] args) {

       LOGGER.info("Начало работы программы!!!");

       try {
           LOGGER.warn("Внимание! Программа пытается разделить одно число на другое");
           System.out.println(12/0);
       } catch (ArithmeticException x) {

           LOGGER.error("Ошибка! Произошло деление на ноль!");
       }
   }
}
Он, конечно, немного кривоватый (перехват RuntimeException — идея так себе), но для нашей целей отлично подойдет :) Давай запустим наш метод main() 4 раза подряд и посмотрим на наш файл testlog.txt. Создавать его заранее не нужно: библиотека сделает это автоматически. Все заработало! :) Теперь у тебя есть настроенный логгер. Ты можешь поиграться с какими-то написанными тобой ранее программами, добавив вызовы логгера во все методы, и посмотреть на получившийся журнал:) В качестве дополнительного чтения очень рекомендую тебе вот эту статью. Там тема логирования рассмотрена углубленно, и за один раз прочитать ее будет непросто. Но в ней содержится очень много полезной дополнительной информации. Например, ты научишься конфигурировать логгер так, чтобы он создавал новый текстовый файл, если наш файл testlog.txt достиг определенного размера:) А наше занятие на этом завершено! Ты сегодня познакомился с очень важной темой, и эти знания точно пригодятся тебе в дальнейшей работе. До новых встреч! :)
Комментарии (44)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
11 марта 2024
А можно настройку логирования делать не через xml а через property? Просто в интернете показывали именно через проперти и кажется так даже легче
Kiawem Уровень 46
2 марта 2024
вообщем, у всех тех у кого не создавался файл вот код при котором создалось все

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configurator;


public class Main {
    public static final Logger logger = LogManager.getLogger(Main.class);
    public static void main(String[] args) {
        Configurator.initialize(null, "путь до вашего log4j.xml");
        logger.info("Test log");
        try {
            logger.warn("Деление на другое число");
            System.out.println(12 / 0);
        } catch (ArithmeticException e) {
            logger.error("деление на 0", e);
        }
    }
}
7 декабря 2023
Описанное в статье у меня не заработало, с учетом всех комментов ниже, загрузки разных jar-ов итп (Точнее заработало, но путем изменения содержания log4j.xml) Надо уже изучить maven, иначе, подозреваю, таких неудач может быть много.
Kurama Уровень 50
29 января 2023

SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See https://www.slf4j.org/codes.html#noProviders for further details.
SLF4J: Class path contains SLF4J bindings targeting slf4j-api versions 1.7.x or earlier.
SLF4J: Ignoring binding found at [jar:file:/C:/Users/S/IdeaProjects/Libs/log4j-slf4j-impl-2.19.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See https://www.slf4j.org/codes.html#ignoredBindings for an explanation.
Kurama Уровень 50
27 января 2023
Я завис ещё на моменте скачивания... Пришлось глянуть этот видос с 5:40
19 января 2023
создаем maven-проект добавляем зависимости в pom.xml создаем файл log4j2.xml, а не log4j.xml; кладем его а папку resource, не просто в src; правильно прописываем полный путь при создании файла testlog.txt
17 декабря 2022
Я не сумел победить неявное чтение файла конфигурации (ни .properties, ни .xml). Меня спас PropertyConfigurator.configure(). Может пригодится кому...
Kim Уровень 37
7 июля 2022
У вас ошибочка. SLF4j - это Simple Logging , у вас он Service...
Макс Дудин Уровень 41
28 марта 2022
всё таки я его подключил, не прошло и года... =) и даже частично работает
Zheleznyak Maxim Уровень 47
3 февраля 2022
Добрый день, Подскажите пожалуйста. как добавить файл конфигурации Logger`a? способ описанный в лекции (https://javarush.com/groups/posts/2293-zachem-nuzhno-logirovanie) не работает. сейчас удалил файл и хочу добиться: ERROR StatusLogger No Log4j 2 configuration file found. Using default configuration (logging only errors to the console), or user programmatically provided configurations. Set system property 'log4j2.debug' to show Log4j 2 internal initialization logging. See https://logging.apache.org/log4j/2.x/manual/configuration.html for instructions on how to configure Log4j 2 Как создать и явно указать файл конфигурации? Сейчас тупо выводит сообщения в консоль: Мой код:

package com.javarush.task.task34.task3412;

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

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


    public static void main(String[] args) {
        LOGGER.info("Начало работы программы!!!");
        LOGGER.error("Внимание! Программа пытается разделить одно число на другое");
        LOGGER.error("Ошибка! Произошло деление на ноль!");


    }
}
Набор подключенных библиотек: