Последнее обновление: 11 февраля 2026
Автор: Антон Козицкий

Популярные библиотеки для логирования

Обзор популярных библиотек для логирования в Java

Интро и план

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

Важный архитектурный момент

Интерфейс и имплементация, или «лёгкий экскурс в историю»

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

Логгер — это две части: фасад и имплементация.

Фасад состоит из набора интерфейсов и задаёт контракт («правила игры»). Фасад нам нужен для унификации логирования и чтобы определить, как логгер и его составные части будут выглядеть. Имплементация — это конкретный двигатель под капотом, который играет по правилам, определённым интерфейсом.

В Java это популярная практика: когда есть интерфейс и когда есть одна или несколько имплементаций этого интерфейса. Вот несколько примеров для лучшего понимания:

Интерфейс Имплементации
JPA (Java Persistence API) — стандарт для ORM и работы с реляционными БД
Bean Validation (JSR 380 / Jakarta Validation) — стандарт декларативной валидации через аннотации

Можно пойти ещё дальше и вспомнить, что сама Java (а точнее виртуальная машина Java — JVM) — это тоже спецификация (скажем, фасад/интерфейс/контракт). И у этой спецификации есть очень много реализаций от самых разных крупных вендоров. Да, это мир Java — здесь «рулят» правила и контракты. Именно поэтому Java так хорошо себя зарекомендовала в мире Enterprise-решений.

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

  • единый стандарт;
  • возможность простой замены одной имплементации другой, без переписывания кода;
  • здоровая конкуренция — каждый вендор пишет свою имплементацию, которая может иметь свои плюсы и минусы;
  • разделение обязанностей: сообщество — контракт/интерфейс, вендоры — имплементации.

«Сломанный паттерн» в логировании

Ложка дёгтя в бочке мёда. К сожалению, на момент создания первых логгеров ещё не было определено правило, что «сначала контракт, потом имплементация». Это правило пришло позже — в то время, когда первые имплементации логгеров уже увидели свет. Отсюда и возникла некоторая проблема, которая впоследствии всё же была решена. Но всё равно есть некоторый нюанс, о котором мы и поговорим ниже.

Посмотрите на эту таблицу, чтобы понять, о чём я буду говорить дальше:

Название логгера «Родной» интерфейс/фасад и jar Имплементация
Logback SLF4J: slf4j-api.jar logback-classic.jar
Log4j2 Log4j: log4j-api.jar log4j-core.jar
JUL (Java Util Logging) Является частью JDK в пакете java.util.logging. Интерфейсы и имплементация живут вместе в одном пакете.

Здесь нет единства по той самой причине: логгеры появились слишком давно — в то время, когда чёткого разделения ещё не было как такового. Но как быть теперь? Решение есть, и о нём ниже.

Если вы начинаете новый проект, вы можете выбрать подходящий для себя логгер и затем подключить «правильные» фасад и имплементацию — то есть соответствующие друг другу, например: slf4j-api.jar и logback-classic.jar. Скорее всего ваш проект будет подключать зависимости — сторонние библиотеки. В этом случае с высокой долей вероятности эти библиотеки будут использовать разные логгеры, это нормально и ожидаемо. Ваша задача здесь — привести всё это к «общему знаменателю».

Например: вы хотите использовать Log4j2 в качестве логгера для своего проекта. Некоторые зависимости используют Logback — другой логгер. Вам нужно все логи привести к одному формату — к формату Log4j2. Для этой цели были созданы специальные «bridges» — «переходники», которые транслируют логи из одной системы в другую. В секции «Практика» мы коснёмся этой темы. А сейчас предлагаю рассмотреть плюсы и минусы популярных систем логирования.

Обзор популярных Java логгеров

Поговорим про конкретные имплементации. Рассмотрим три самые популярные и самые широко используемые в Java имплементации логгеров: Logback, Log4j2 и Java Util Logging (JUL).

JUL (Java Util Logging)

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

Logback

Это логгер по умолчанию в Spring Boot. Он тоже относится к простым логгерам, которые хорошо делают свою рядовую работу. Включили его в Spring Boot как раз за его простоту и функциональность. Для 90% всех проектов он отлично подходит и выполняет все свои задачи на «ура».

Рекомендация: начинайте все новые проекты именно с Logback. Основное правило здесь такое: вы начинаете проект с логгером Logback, но если в какой-то момент выясняется, что вам не хватает его функционала, то вы переходите на Log4j2.

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

Log4j2

Если нам нужны высокая производительность, либо какие-то особые функции, которых нет в Logback, мы можем подключить Log4j2. Например, для отправки логов по сети или в Kafka нам не нужно подключать дополнительные расширения — всё уже есть в логгере. Если наш проект очень большой и мы ожидаем, что будем очень много логировать, ожидаем высоких нагрузок на наш сервис — в этом случае Log4j2 может стать разумным выбором.

Переходим к практике

SLF4J + Logback

Начнём с того, что подключим фасад:

<dependencies>
  <!-- Source: https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.17</version>
    <scope>compile</scope>
  </dependency>
</dependencies>

Далее добавим класс, из которого будем логировать:

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

public class Main {

  private static final Logger log = LoggerFactory.getLogger(Main.class);

  public static void main(String[] args) {
    log.trace("Logging trace message");
    log.debug("Logging debug message");
    log.info("Logging info message");
    log.warn("Logging warn message");
    log.error("Logging error message");
  }
}

И попробуем запустить наше тестовое приложение:

SLF4J(W): No SLF4J providers were found.
SLF4J(W): Defaulting to no-operation (NOP) logger implementation
SLF4J(W): See https://www.slf4j.org/codes.html#noProviders for further details.

Process finished with exit code 0

Здесь нам сам фасад говорит, что имплементаций не было найдено и поэтому логирование невозможно. Но в то же время ошибку это не создаёт — просто фасад уходит в состояние no-operation («ничего не делаю»).

Добавим имплементацию logback-classic — «родную» имплементацию фасада:

<dependencies>
  <!-- Source: https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.17</version>
    <scope>compile</scope>
  </dependency>
  <!-- Source: https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
  <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.5.32</version>
    <scope>compile</scope>
  </dependency>
</dependencies>

Попробуем запустить наше тестовое приложение ещё раз:

19:16:12.799 [main] DEBUG io.devmentor.learning.Main -- Logging debug message
19:16:12.802 [main] INFO  io.devmentor.learning.Main -- Logging info message
19:16:12.802 [main] WARN  io.devmentor.learning.Main -- Logging warn message
19:16:12.802 [main] ERROR io.devmentor.learning.Main -- Logging error message

Process finished with exit code 0

Вот оно, решение нашей проблемы! Добавляем имплементацию, фасад видит её, регистрирует — и всё начинает работать, логи появляются в консоли.

Почему только четыре лога, если мы написали пять? Дело в том, что у логгера Logback уровень логирования по умолчанию — DEBUG. А это значит, что все логи уровнем ниже (у нас это TRACE) выводиться не будут. Чтобы это поведение изменить, нужно задать дополнительную конфигурацию.

Напомню: ROOT-логгер — это единственный логгер, который создаётся на этапе инициализации. В классе Main мы явно создаём ещё один логгер, но не задаём ему своего уровня логирования — а значит, будет использоваться уровень ROOT. Именно для этого ROOT в Logback и задан уровень DEBUG. В следующей статье туториала мы будем настраивать логгеры и узнаем, что нужно добавить в конфигурацию, чтобы вывести все пять сообщений.

Log4j API + Log4j Impl

Давайте теперь посмотрим на Log4j. Подключим фасад:

<dependencies>
  <!-- Source: https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.25.3</version>
    <scope>compile</scope>
  </dependency>
</dependencies>

Добавим логи в тестовое приложение:

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

public class Main {

  private static final Logger log = LogManager.getLogger(Main.class);

  public static void main(String[] args) {
    log.trace("Logging trace message");
    log.debug("Logging debug message");
    log.info("Logging info message");
    log.warn("Logging warn message");
    log.error("Logging error message");
  }
}

И попробуем запустить. Для этого логгера мы получим такое сообщение:

2026-02-21T12:16:25.988147800Z main ERROR Log4j API could not find a logging provider.

Process finished with exit code 0

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

Давайте добавим провайдер:

<dependencies>
  <!-- Source: https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.25.3</version>
    <scope>compile</scope>
  </dependency>
  <!-- Source: https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.25.3</version>
    <scope>compile</scope>
  </dependency>
</dependencies>

Теперь получаем такой вывод в консоль:

2026-02-22T18:26:01.386235500Z main ERROR Logging error message

Process finished with exit code 0

Логирование заработало! Но только один лог из пяти появился в консоли. Это связано с тем, что в отличие от Logback в Log4j уровень логирования по умолчанию — ERROR. Если нас это не устраивает, нам нужно создать файл конфигурации и задать правила. О том, как этого добиться, мы будем говорить в следующей статье туториала.

Java Util Logging

И последний логгер. Для него, как мы уже говорили выше, не нужны никакие дополнительные зависимости — всё, что нужно, уже есть в самой Java (в JDK, в пакете java.util.logging).

Так выглядит наше тестовое приложение:

package io.devmentor.learning;

import java.util.logging.Level;
import java.util.logging.Logger;

public class Main {

  private static final Logger logger = Logger.getLogger(Main.class.getName());

  public static void main(String[] args) {
    logger.log(Level.ALL,     "Logging all message");
    logger.log(Level.FINEST,  "Logging finest message");
    logger.log(Level.FINER,   "Logging finer message");
    logger.log(Level.FINE,    "Logging fine message");
    logger.log(Level.CONFIG,  "Logging config message");
    logger.log(Level.INFO,    "Logging info message");
    logger.log(Level.WARNING, "Logging warning message");
    logger.log(Level.SEVERE,  "Logging severe message");
    logger.log(Level.OFF,     "Logging off message");
  }
}

Запускаем и получаем такой вывод в консоль:

Mar 12, 2026 8:55:48 AM io.devmentor.learning.Main main
INFO: Logging info message
Mar 12, 2026 8:55:48 AM io.devmentor.learning.Main main
WARNING: Logging warning message
Mar 12, 2026 8:55:48 AM io.devmentor.learning.Main main
SEVERE: Logging severe message
Mar 12, 2026 8:55:48 AM io.devmentor.learning.Main main
OFF: Logging off message

Process finished with exit code 0

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

Выводы

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

Как вы заметили, все три логгера по умолчанию выводят логи в разном формате. Для наглядности я собрал три лога от разных логгеров, идущие друг за другом в одном блоке:

09:12:14.333 [main] INFO  io.devmentor.learning.Main -- Logging info message

2026-03-12T08:12:50.540241400Z main ERROR Logging error message

Mar 12, 2026 8:55:48 AM io.devmentor.learning.Main main
INFO: Logging info message

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

Напомню, что ситуация с разными логгерами может получиться, если мы подключаем зависимости в проект и эти зависимости используют разные логгеры у себя. Для решения этой проблемы были придуманы «переходники» («bridges»). Они позволяют транслировать логи одного типа в другой автоматически. То есть в нашем финальном лог-файле мы всегда видим логи в одинаковом формате — в формате логгера, который мы выбрали для своего приложения.

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