Logger.log: примеры (JAVA)

Метод Logger.log в Java: подробный обзор
Раздел: Логирование
Logger.log(Level level, String msg): void

Описание java.util.logging.Logger.log()

Метод Logger.log в Java чаще всего относится к API java.util.logging.Logger. Он используется для записи сообщений разного уровня важности в систему логирования приложения. Основная задача метода - передача сообщения и сопутствующих данных системе логирования, которая затем направляет их в зарегистрированные Handler (консоль, файл, сеть и т. п.) и применяет Formatter для форматирования вывода.

Типичные сигнатуры в стандартной библиотеке:

  • void log(Level level, String msg)
  • void log(Level level, String msg, Object param)
  • void log(Level level, String msg, Object[] params)
  • void log(Level level, String msg, Throwable thrown)
  • void logp(Level level, String sourceClass, String sourceMethod, String msg)
  • void logrb(Level level, String sourceClass, String sourceMethod, ResourceBundle bundle, String msg, Object... params)

Краткое описание аргументов и поведения:

  • Level level - уровень логирования из перечисления java.util.logging.Level (например, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST). Управляет фильтрацией и видимостью сообщения.
  • String msg - текст сообщения. Может содержать плейсхолдеры для подстановки, если используется версия с params.
  • Object param / Object[] params - значения для подстановки в сообщение (при использовании встроенной поддержки параметров SimpleFormatter или собственных форматов). Передача null допустима, но поведение зависит от Formatter.
  • Throwable thrown - исключение, дополнительная информация о стеке вызовов. Обычно записывается в конце записи лога.
  • String sourceClass / sourceMethod - явное указание класса и метода-источника записи, полезно при проксировании или обёртках логирования.
  • ResourceBundle bundle - ресурсный бандл для локализованных сообщений. При указании будет использован локализованный текст по ключу msg.

Возвращаемое значение: все стандартные варианты возвращают void. Результат работы проявляется в виде записи в подключённые хендлеры и/или внешние системы.

Поведение при фильтрации: если уровень сообщения ниже настроенного уровня логгера или фильтра Handler, сообщение не будет обработано. Конфигурация уровней происходит через LogManager, системные свойства или программно.

Простые примеры вызова Logger.log()

Ниже примеры с различными сигнатурами и ожидаемым выводом в консоль при стандартной конфигурации SimpleFormatter.

Пример 1 - базовый вызов с уровнем и сообщением:

import java.util.logging.*;

public class Example1 {
    private static final Logger LOGGER = Logger.getLogger(Example1.class.getName());
    public static void main(String[] args) {
        LOGGER.log(Level.INFO, "Простейшее сообщение");
    }
}
Jan 01, 2020 12:00:00 PM Example1 main
INFO: Простейшее сообщение

Пример 2 - передача параметра для подстановки (варианты форматирования зависят от Formatter):

public class Example2 {
    private static final Logger LOGGER = Logger.getLogger(Example2.class.getName());
    public static void main(String[] args) {
        LOGGER.log(Level.WARNING, "Пользователь {0} не найден", "alice");
    }
}
Jan 01, 2020 12:00:00 PM Example2 main
WARNING: Пользователь alice не найден

Пример 3 - лог с исключением:

public class Example3 {
    private static final Logger LOGGER = Logger.getLogger(Example3.class.getName());
    public static void main(String[] args) {
        try {
            throw new IllegalStateException("Ошибка состояния");
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "Фатальная ошибка", e);
        }
    }
}
Jan 01, 2020 12:00:00 PM Example3 main
SEVERE: Фатальная ошибка
java.lang.IllegalStateException: Ошибка состояния
    at Example3.main(Example3.java:6)

Пример 4 - явное указание источника (logp):

public class Example4 {
    private static final Logger LOGGER = Logger.getLogger("custom.logger");
    public static void main(String[] args) {
        LOGGER.logp(Level.CONFIG, "com.example.Service", "doWork", "Конфигурационное сообщение");
    }
}
Jan 01, 2020 12:00:00 PM com.example.Service doWork
CONFIG: Конфигурационное сообщение

Похожие механизмы логирования в Java

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

  • java.util.logging (JUL) - стандартный в JDK, прост в настройке, интегрирован с платформой, но менее гибкий по сравнению с внешними библиотеками.
  • Log4j 2 - богатый функционал, поддержка асинхронного логирования, шаблонов, фильтров и расширяемости. Часто предпочтителен для серверных приложений с высокой нагрузкой.
  • SLF4J + Logback - SLF4J предоставляет абстракцию, Logback является популярной реализацией с хорошей производительностью и поддержкой MDC/NDC. Рекомендуется при необходимости смены реализации без изменения кода.

Когда использовать какую систему:

  • Для лёгких приложений и утилит достаточно JUL.
  • Для крупных проектов и продуктов с требованиями к производительности и функционалу лучше Log4j2 или Logback через SLF4J.
  • При необходимости библиотечной совместимости предпочтительна абстракция SLF4J.

Аналоги в других языках и особенности

Краткие аналоги логирования и примеры кода в популярных языках.

  • PHP - error_log() или Monolog. Monolog предоставляет обработчики, форматтеры и уровни.
    // error_log
    error_log("Тестовое сообщение");
    
    // Monolog (пример)
    use Monolog\Logger;
    use Monolog\Handler\StreamHandler;
    $log = new Logger('app');
    $log->pushHandler(new StreamHandler('php://stdout', Logger::INFO));
    $log->info('Привет, мир');
    // error_log вывод зависит от настройки SAPI
    // Monolog вывод:
    [2020-01-01T12:00:00.000000+00:00] app.INFO: Привет, мир []
  • JavaScript - console.log, console.error. В серверных средах можно использовать Winston или Pino.
    console.log('Сообщение');
    console.error('Ошибка');
    Сообщение
    Ошибка
  • Python - модуль logging с методами logger.info(), logger.exception(). Поддерживает конфигурацию через dict или файл.
    import logging
    logger = logging.getLogger('app')
    logging.basicConfig(level=logging.INFO)
    logger.info('Hello %s', 'world')
    INFO:app:Hello world
  • SQL - в чистом SQL логирование не стандартно, но в PL/pgSQL используются RAISE NOTICE, RAISE WARNING и т.д.
    -- PL/pgSQL
    RAISE NOTICE 'Текст сообщения: %', value;
    NOTICE:  Текст сообщения: 42
  • C# - Microsoft.Extensions.Logging. Интерфейс ILogger имеет метод Log(LogLevel, ...) и типизированные вызовы LogInformation.
    using Microsoft.Extensions.Logging;
    ILogger logger = ...;
    logger.LogInformation("Параметр {Id}", 10);
    info: App[0]
          Параметр 10
  • Lua - обычно используется print() или библиотеки-логгеры. Простейший вывод:
    print('Log message')
    Log message
  • Go - пакет log с методами Println, Printf, а также сторонние логгеры (zap, zerolog) для производительности.
    import "log"
    func main() {
        log.Println("Hello from Go")
    }
    
    2009/11/10 23:00:00 Hello from Go
  • Kotlin - обычно используются Java-логгеры (SLF4J, Logback) или обёртки. Пример с SLF4J:
    private val logger = org.slf4j.LoggerFactory.getLogger("App")
    logger.info("Message {}", 1)
    INFO App - Message 1

Отличия от Java: во многих языках встроенные механизмы проще, но внешние библиотеки предоставляют гибкость и производительность, похожую на Log4j2/Logback. В Python и C# встроенные системы ближе по возможностям к Java-экосистеме.

Типичные ошибки при использовании Logger.log()

Распространённые проблемы и их проявления.

  • Неправильный уровень (null): вызов с Level равным null приведёт к NullPointerException в стандартной реализации.
    Logger logger = Logger.getLogger("app");
    logger.log(null, "msg");
    Exception in thread "main" java.lang.NullPointerException
        at java.util.logging.Logger.log(Logger.java:...)
  • Ожидание подстановки, когда Formatter не поддерживает params: использование params без соответствующего Formatter может вывести сообщение с плейсхолдерами как есть.
    logger.log(Level.INFO, "User {0} logged in", new Object[]{"bob"});
    INFO: User {0} logged in
  • Конфигурация уровней: сообщения не видны на консоли из-за установленного уровня логгера или Handler ниже порога. Частая причина - непонимание, что существуют уровни и у Logger, и у Handler.
    logger.setLevel(Level.FINE);
    // Handler настроен на INFO
    logger.log(Level.FINE, "Детальная информация");
    // Ничего не выводится
  • Производительность при конкатенации строк: использование строковой конкатенации в аргументе сообщения приводит к ненужной работе, даже если сообщение отфильтровано. Предпочтение отдаётся версиям с параметрами или проверке уровня через isLoggable.
    logger.log(Level.FINE, "Result: " + expensive());
    // Лучше:
    if (logger.isLoggable(Level.FINE)) logger.log(Level.FINE, "Result: {0}", expensive());
    // Первый вариант вызывает expensive() всегда
    // Второй вариант вызывает expensive() только при включённом уровне
  • Неправильная локализация: при использовании logrb ключ сообщения может не быть найден в ResourceBundle, что приведёт к выводу ключа вместо локализованного текста.

Изменения в поведении и эволюция API

В стандартной реализации java.util.logging.Logger существенных изменений в сигнатурах метода log в последних версиях JDK не происходило. Основные улучшения экосистемы связаны с внешними библиотеками:

  • Log4j 2 получил поддержку улучшённого асинхронного логирования, новых конфигов и расширяемых API в ранних версиях 2.x.
  • SLF4J продолжает играть роль фасада, позволяющего менять реализацию без правки клиентского кода; в новых релизах уделяется внимание совместимости и расширенной поддержке параметризации.

В целом, для новых проектов чаще выбираются внешние реализации (Log4j2, Logback) из-за производительности и функционала, тогда как сам JUL остаётся стабильным и совместимым в пределах JDK.

Расширенные и редкие сценарии использования

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

1) Кастомный Formatter и запись в файл:

Пример java
import java.io.IOException;
import java.util.logging.*;

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

    public static void main(String[] args) throws IOException {
        Handler fh = new FileHandler("app.log");
        fh.setFormatter(new Formatter(){
            @Override
            public String format(LogRecord record) {
                return record.getLevel() + ": " + record.getMessage() + "\n";
            }
        });
        LOGGER.addHandler(fh);
        LOGGER.setUseParentHandlers(false); // отключить консольный хендлер
        LOGGER.log(Level.INFO, "Запись в файл");
    }
}
// В файле app.log
INFO: Запись в файл

2) Асинхронная запись через внешнюю библиотеку (Log4j2 пример концепции):

Пример java
// Конфигурация Log4j2 с AsyncAppender даёт минимальное влияние на поток приложения.
// Код взаимодействует через SLF4J: logger.info("...{}");
// Вывод аналогичен синхронному, но запись выполняется в фоне, уменьшается задержка в потоке-источнике

3) Мост между JUL и SLF4J (JUL -> SLF4J) для централизованного управления логами:

Пример java
// Добавление моста в runtime позволяет перенаправить все вызовы java.util.logging в SLF4J/Logback
// В зависимости от реализации требуется добавить jul-to-slf4j и вызвать SLF4JBridgeHandler.install()
// После установки все сообщения от библиотек, использующих JUL, будут обрабатываться выбранной SLF4J-реализацией

4) Использование ResourceBundle для локализации сообщений:

Пример java
// В ресурсах messages_ru.properties
// user.notfound=Пользователь {0} не найден

Logger logger = Logger.getLogger("app", "messages_ru");
logger.logrb(Level.WARNING, "com.app.Service", "method", "user.notfound", "ivan");
WARNING: Пользователь ivan не найден

5) Контекстная информация (аналог MDC) с использованием MDC в SLF4J/Logback:

Пример java
// Пример с SLF4J
org.slf4j.MDC.put("requestId", "abc-123");
logger.info("Запрос обработан");
org.slf4j.MDC.clear();
INFO App - [requestId=abc-123] Запрос обработан

6) Логирование в многопоточной среде: рекомендуется использовать асинхронные аппендеры и минимизировать синхронизацию в коде логирования. Для JUL можно реализовать собственный Handler с буферизацией.

7) Диагностика проблем логирования: включение детального уровня для конкретного логгера и временная замена Handler дают информацию о причинах отсутствия записей.

джава Logger.log function comments

En
Logger.log Запись лог-сообщения