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 и запись в файл:
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 пример концепции):
// Конфигурация Log4j2 с AsyncAppender даёт минимальное влияние на поток приложения.
// Код взаимодействует через SLF4J: logger.info("...{}");
// Вывод аналогичен синхронному, но запись выполняется в фоне, уменьшается задержка в потоке-источнике
3) Мост между JUL и SLF4J (JUL -> SLF4J) для централизованного управления логами:
// Добавление моста в runtime позволяет перенаправить все вызовы java.util.logging в SLF4J/Logback
// В зависимости от реализации требуется добавить jul-to-slf4j и вызвать SLF4JBridgeHandler.install()
// После установки все сообщения от библиотек, использующих JUL, будут обрабатываться выбранной SLF4J-реализацией
4) Использование ResourceBundle для локализации сообщений:
// В ресурсах 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:
// Пример с SLF4J
org.slf4j.MDC.put("requestId", "abc-123");
logger.info("Запрос обработан");
org.slf4j.MDC.clear();
INFO App - [requestId=abc-123] Запрос обработан
6) Логирование в многопоточной среде: рекомендуется использовать асинхронные аппендеры и минимизировать синхронизацию в коде логирования. Для JUL можно реализовать собственный Handler с буферизацией.
7) Диагностика проблем логирования: включение детального уровня для конкретного логгера и временная замена Handler дают информацию о причинах отсутствия записей.