JUL (Java util Logging) - фреймворк для логирования входящий в состав Java SE. Находится в пакете java.util.logging
. Если говорить о Java 9+, то он представляет собой модуль java.logging
.
JUL, несмотря на его присутствие в стандартной поставке Java, как и System.out.println()
или System.err.println()
, не стоит его использовать в реальных проектах, т.к. он появился в Java значительно позже, по сравнению со специализированными решениями (библиотеками) для логирования и значительно уступает им по функциональности и производительности. В современных проектах чаще всего используют связки:
-
Slf4j + Log4j2
-
Slf4j + Logback
JUL появился в Java 1.4. Работа с JUL происходит через класс java.util.logging.Logger
. Получить экземпляр этого класса можно через фабричный метод getLogger()
:
import java.util.logging.Logger;
public class JavaUtilLoggingExample {
public static final Logger logger = Logger.getLogger(JavaUtilLoggingExample.class.getName());
}
Для записи сообщений в лог используются методы полученного экземпляра класса Logger
. Самый главный метод у которого public void log(LogRecord record)
. Все остальные методы логирования вызывают этот метод. Напрямую его использовать не стоит, так как вызывать другие методы гораздо проще, например public void log(Level level, String msg)
. Этому методу достаточно указать уровень логирования и сообщение.
Уровней логирования всего семь:
-
SEVERE (ошибка)
-
WARNING (предупреждение)
-
INFO (информационное сообщение)
-
CONFIG
-
FINE (сообщение об успешной операции)
-
FINER
-
FINEST
Сообщение же может быть любым текстом.
import java.util.logging.Level;
import java.util.logging.Logger;
public class JavaUtilLoggingExample {
public static final Logger logger = Logger.getLogger(JavaUtilLoggingExample.class.getName());
public void printLogWithInfo() {
logger.log(Level.INFO, "Print log with info level");
}
}
Также для каждого из уровней логирования есть огромное множество перегруженных методов с различным числом параметров. Например, тот же самый результат можно получить вызвав метод info()
import java.util.logging.Logger;
public class JavaUtilLoggingExample {
public static final Logger logger = Logger.getLogger(JavaUtilLoggingExample.class.getName());
public void printInfoLog() {
logger.info("Print info log");
}
}
Также можно писать сообщения об ошибках с другими уровнями логирования.
import java.util.logging.Logger;
public class JavaUtilLoggingExample {
public static final Logger logger = Logger.getLogger(JavaUtilLoggingExample.class.getName());
public void printLogWithAnotherLevel() {
logger.severe("Ошибка");
logger.warning("Предупреждение");
logger.info("Информация");
logger.fine("Отладка");
logger.finer("Отладка с деталями");
logger.finest("Отладка с огромным количеством деталей");
}
}
В результате вызова этого метода в консоль будет выведено:
Oct 10, 2022 6:59:22 AM com.rakovets.course.java.core.example.jul.JavaUtilLoggingExample printLogWithAnotherLevel SEVERE: Ошибка Oct 10, 2022 6:59:22 AM com.rakovets.course.java.core.example.jul.JavaUtilLoggingExample printLogWithAnotherLevel WARNING: Предупреждение Oct 10, 2022 6:59:22 AM com.rakovets.course.java.core.example.jul.JavaUtilLoggingExample printLogWithAnotherLevel INFO: Информация
Если необходимо передать исключение в лог, то нужно использовать специальный метод, принимающий Throwable
:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JavaUtilLoggingExample {
public static final Logger logger = Logger.getLogger(JavaUtilLoggingExample.class.getName());
public void catchExceptionAndPrintLog() {
try {
Files.readAllBytes(Paths.get("/file/does/not/exist"));
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error message", ex);
}
}
}
Oct 10, 2022 7:02:21 AM com.rakovets.course.java.core.example.jul.JavaUtilLoggingExample catchExceptionAndPrintLog SEVERE: Error message java.nio.file.NoSuchFileException: /file/does/not/exist at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:92) at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111) at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:116) at java.base/sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:219) at java.base/java.nio.file.Files.newByteChannel(Files.java:371) at java.base/java.nio.file.Files.newByteChannel(Files.java:422) at java.base/java.nio.file.Files.readAllBytes(Files.java:3206) at com.rakovets.course.java.core.example.jul.JavaUtilLoggingExample.catchExceptionAndPrintLog(JavaUtilLoggingExample.java:39) at com.rakovets.course.java.core.example.jul.JavaUtilLoggingExample.main(JavaUtilLoggingExample.java:17)
1. Handler
Каждое сообщение, отправленное в логгер проходит фильтры, которые определяют, должно ли оно попасть в результирующий лог, например, по LogLevel
, затем через обработчики. Обработчики (Handlers) направляют результат лога в файл, консоль или куда-нибудь по сети.
В самой Java уже есть пять таких обработчиков, но можно добавлять свои. Стандартные обработчики:
-
StreamHandler
пишет вOutputStream
. -
ConsoleHandler
пишет вSystem.err
. -
FileHandler
пишет в файл. -
SocketHandler
отправляет по сети на указанный порт. -
MemoryHandler
просто сохраняет в ОЗУ.
2. Formatter
Formatter используются для записи логов в определённом формате, например в XML или HTML.
3. Configuration
Настраивать JUL можно двумя способами:
-
Конфигурационный класс
Нужно указать аргумент-Djava.util.logging.config.class=<имя конфигурационного класса>
при запуске Java программы. -
Конфигурационный файл
Нужно указать аргумент-Djava.util.logging.config.file=<путь к конфигурационному файлу>
при запуске Java программы.
Второй способ более предпочтительный, так как не требует повторной сборки приложения. Если ничего из этого в аргументах JVM не указано, то используется конфигурация по умолчанию.
Файл с конфигурацией, указываемый в -Djava.util.logging.config.file
— это обычный properties-файл. Например:
logging.properties
handlers=java.util.logging.FileHandler,java.util.logging.ConsoleHandler
java.util.logging.FileHandler.level=FINEST
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.FileHandler.encoding=UTF-8
java.util.logging.FileHandler.limit=10000000
java.util.logging.FileHandler.pattern=/var/log/julexample/julexample%u.log
java.util.logging.ConsoleHandler.level=FINEST
-
В свойстве
handlers
указывается список обработчиков (handler-ов), которые нужно добавить к корневому logger. -
Можно более детально описать работу handlers, в частности
FileHandler
иConsoleHandler
. -
В свойстве
level
указывается уровень логирования. В данном случае сообщения с уровнем логирования от SEVERE до FINEST будут попадать в лог. -
В свойстве
formatter
дляFileHandler
указаноSimpleFormatter
. Можно указатьXMLFormatter
или реализовать свой formatter. -
Свойство
encoding
позволяет задать кодировку для логов. -
В
limit
дляFileHandler
указывается ограничение на размер файла логов для ротации в байтах. -
Свойство
pattern
позволяет указать путь к файлу лога дляFileHandler
, где%u
будет заменяться на числа (0
,1
, …), для разрешения конфликтов имени файла.