🐝

Что логировать

Вид сообщений, записываемых в лог, зависит от уровня логирования. В современных Java-приложениях стандартом де-факто для логирования является библиотека SLF4J, поэтому приведены уровни логирования предоставляемые его API:

  • TRACE - Сверх подробное описание работы программы. Используется очень редко
  • DEBUG - Подробное описание работы программы. Используется для отладки
  • INFO - Верхнеуровневое описание основных шагов выполнения программы / запроса.
  • WARN - Оповещение о некритических сбоях и о необычном поведении системы (отсутствие в БД запрашиваемых данных, временная недоступность внешнего сервиса и др.)
  • ERROR - Алерт о критическом сбое, свидетельствующий о наличии серьезного бага в приложении

Что может пойти не так

Излишнее логирование

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

return entities.stream()
  .map(it -> {
      long startTime = System.currentTimeMillis();
      MappedEntity mappedEntity = map(it);
      long finishTime = System.currentTimeMillis();
      log.debug(
        "Mapping of enitity with id '{}' done in {} millis", 
        it.getId(), 
        finishTime - startTime);
      return mappedEntity;
  })
  .collect(toList());

В таких случаях стоит отказаться от логирования, либо зарефакторить его:

long startTime = System.currentTimeMillis();
List<MappedEntity> mappedEntities = entities.stream()
  .map(it -> map(it))
  .collect(toList());
long delay = System.currentTimeMillis() - startTime;
log.debug(
        "Mapping of {} enitities done in {} millis. Average time = {}", 
        entities.size()
        delay,
        (double) delay / entities.size());
return mappedEntities;

Исполнение методов при записи в лог

Иногда при отладке требуется вывести в лог результат выполнения какого-либо действия.

log.debug("Interesting details: {}", getSmth());

В таких случаях метод getSmth() будет вызван, даже если в приложении установлен уровень логирования выше, чем DEBUG.

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

log.debug("Interesting details: {}", () -> getSmth());

К сожалению, такой функционал не доступен в актуальной версии SLF4J. Но более современная библиотека log4j2 уже поддерживает лямбда-выражения и в ней уже можно использовать такие трюки.

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

Чем логировать

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

  • Logback - на сегодня самая популярный логгер. При этом он разработан создателями SLF4J, не имеет дополнительных оберток и адаптеров, а потому работает с SLF4J быстрее, чем другие библиотеки
  • Log4j - предтеча всех библиотек для логирования
  • JUL - стандартная Java реализация
  • Log4j2 - набирающая популярность библиотека, поддерживающая lazy evaluation с помощью лямбда-выражений.

На сегодня репозиторий SLF4J кажется заброшенным. Последний коммит в репозитории был сделан в феврале 2020 г. Релиз 2.0.0, в котором тоже должна появиться поддержка лямбда-выражений, был анонсирован в середине 2019 г. и до сих пор находится в альфе.

Тем временем log4j2 активно развивается и кажется идеальным выбором для новых проектов.

Трассировщики

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

В SLF4J таким трассировщиком является MDC - Mapped Diagnostic Context.

Существуют решения, упрощающие работу с MDC. Одним из таких решений является Spring Cloud Sleuth.

Куда логировать

По стандартам двенадцатифакторных приложений все логи должны записываться только в консоль. Для хранения логов может использоваться Logstash или Graylog.

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

Ссылочки