java.util.logging.Logger не учитывает уровень java.util.logging.Level?
В обычной среде Java SE 6:
Logger l = Logger.getLogger("nameless");
l.setLevel(Level.ALL);
l.fine("somemessage");
В консоли Eclipse ничего не отображается. Вызовы l.info("")
и выше работают нормально, но всё, что ниже уровня fine
, просто не показывается. В чем может быть проблема? Заранее благодарю.
5 ответ(ов)
Да, вы правы. Даже если уровень логгирования (Logger level) установлен на ALL, обработчик консоли (ConsoleHandler), который по умолчанию используется в логгере, имеет уровень по умолчанию INFO. Это поведение определяется настройками в файле logging.properties
, который располагается в директории JAVA_HOME/jre/lib
.
Чтобы изменить уровень логгирования для ConsoleHandler на более детальный, вам нужно explicitly установить его уровень. Вы можете сделать это следующим образом:
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MyLogger {
private static final Logger logger = Logger.getLogger(MyLogger.class.getName());
static {
// Устанавливаем уровень логгирования для конкретного handler'а
ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.ALL);
logger.addHandler(consoleHandler);
logger.setLevel(Level.ALL);
}
public void logMessages() {
logger.severe("Severe message");
logger.warning("Warning message");
logger.info("Info message");
logger.config("Config message");
logger.fine("Fine message");
logger.finer("Finer message");
logger.finest("Finest message");
}
public static void main(String[] args) {
new MyLogger().logMessages();
}
}
Таким образом, вы гарантированно увидите все сообщения, начиная с уровня ALL, включая более детальные уровни DEBUG и TRACE. Не забудьте, что если вы используете другие обработчики или конфигурации в вашем приложении, вам может потребоваться обновить их соответствующим образом.
Вместо того чтобы перебор всех обработчиков и установки уровня логирования, я предпочитаю установить уровень только для консольного обработчика. Вот пример кода на Java, который демонстрирует этот подход:
// Получаем корневой логгер
Logger topLogger = java.util.logging.Logger.getLogger("");
// Обработчик для консоли (повторно используем, если он уже существует)
Handler consoleHandler = null;
// Проверяем, есть ли уже консольный обработчик
for (Handler handler : topLogger.getHandlers()) {
if (handler instanceof ConsoleHandler) {
// Найден консольный обработчик
consoleHandler = handler;
break;
}
}
if (consoleHandler == null) {
// Консольный обработчик не найден, создаем новый
consoleHandler = new ConsoleHandler();
topLogger.addHandler(consoleHandler);
}
// Устанавливаем уровень логирования для консольного обработчика на FINEST:
consoleHandler.setLevel(java.util.logging.Level.FINEST);
В этом коде мы сначала пытаемся найти существующий консольный обработчик, чтобы избежать создания нового, если он уже есть. Если он не найден, мы создаем новый и добавляем его к корневому логгеру, а затем устанавливаем требуемый уровень логирования. Такой подход позволяет более эффективно управлять обработчиками логирования.
В вашем вопросе правильно подмечено, что оба уровня логирования — уровень логгера и уровень обработчика — важны для того, чтобы сообщения лога отображались корректно. Давайте разберемся подробнее.
Когда вы устанавливаете уровень логгера и обработчика на FINEST
, это позволяет сообщениям с этим уровнем и выше проходить через оба фильтра (логгер и обработчик). Если хотя бы один из уровней ниже необходимого, сообщение будет заблокировано.
Примеры:
Если у вас есть
Logger myLogger
с уровнемFINEST
и обработчикConsoleHandler myHandler
с уровнемINFO
:- При вызове
myLogger.fine("foo")
сообщение успешно проходит через фильтр логгера, но блокируется фильтром обработчика, так как уровеньINFO
выше, чемFINE
. В результате ничего не выводится. - При вызове
myLogger.info("foo")
сообщение проходит оба фильтра, и "foo
" выводится.
- При вызове
Теперь, если у вас
Logger myLogger
с уровнемINFO
и обработчикConsoleHandler myHandler
с уровнемFINEST
:- Вызов
myLogger.fine("foo")
будет заблокирован фильтром логгера (уровеньINFO
не допускаетFINE
), и, следовательно, ничего не выводится. - Вызов
myLogger.info("foo")
проходит оба фильтра, и "foo
" выводится.
- Вызов
Наконец, когда и у логгера, и у обработчика уровень установлен на
FINEST
:- Вызов
myLogger.fine("foo")
проходит оба фильтра и выводит "foo
". - Вызов
myLogger.info("foo")
также проходит оба фильтра, и "foo
" выводится.
- Вызов
Итог:
Вы должны убедиться, что оба уровня логирования (логгера и обработчика) соответствуют или ниже уровня сообщения, которое вы хотите вывести. Если вы хотите, чтобы все уровни выводились, просто установите оба уровня на FINEST
.
Чтобы настроить уровень логирования в Java, необходимо установить уровень логирования как для обработчиков (handlers), так и для самого логгера. Логирование будет выполняться на самом "грубом" из этих уровней. Ниже представлен класс для логирования, который решает эту задачу.
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
public class Log {
private static final Logger logger = Logger.getGlobal();
private static Level logLevel = Level.INFO;
static {
// Удаляем все обработчики по умолчанию (обычно это один консольный обработчик)
Logger rootLogger = Logger.getLogger("");
Handler[] rootHandlers = rootLogger.getHandlers();
for (Handler handler : rootHandlers) {
rootLogger.removeHandler(handler);
}
// Добавляем собственный обработчик
ConsoleHandler handler = new ConsoleHandler();
handler.setLevel(logLevel);
handler.setFormatter(new LogFormatter());
logger.addHandler(handler);
logger.setLevel(logLevel);
}
public static class LogFormatter extends Formatter {
@Override
public String format(LogRecord record) {
String stackTrace = "";
Throwable thrown = record.getThrown();
if (thrown != null) {
StringWriter stacktraceWriter = new StringWriter();
try (PrintWriter writer = new PrintWriter(stacktraceWriter)) {
thrown.printStackTrace(writer);
}
stackTrace = stacktraceWriter.toString();
}
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(record.getMillis()), ZoneId.of("UTC"))
.format(DateTimeFormatter.ISO_ZONED_DATE_TIME) + "\t" + record.getLevel()
+ "\t" + record.getMessage() + "\n" + stackTrace;
}
}
private static final String classname = Log.class.getName();
private static String callerRef() {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
if (stackTraceElements.length < 4) {
return "";
} else {
int i = 1;
for (; i < stackTraceElements.length; i++) {
if (stackTraceElements[i].getClassName().equals(classname)) {
break;
}
}
for (; i < stackTraceElements.length; i++) {
if (!stackTraceElements[i].getClassName().equals(classname)) {
break;
}
}
if (i < stackTraceElements.length) {
return stackTraceElements[i].toString();
} else {
return "[в неизвестном методе]";
}
}
}
public static void setLogLevel(Level newLogLevel) {
logLevel = newLogLevel;
for (Handler handler : logger.getHandlers()) {
handler.setLevel(newLogLevel);
}
Log.logger.setLevel(newLogLevel);
}
public static int getLevelNum() {
return logLevel.intValue();
}
public static int getLevelNum(Level level) {
return level.intValue();
}
public static void fine(String msg) {
logger.log(Level.FINE, msg);
}
public static void info(String msg) {
logger.log(Level.INFO, msg);
}
public static void warning(String msg) {
logger.log(Level.WARNING, msg + "\t " + callerRef());
}
public static void error(String msg) {
logger.log(Level.SEVERE, msg + "\t " + callerRef());
}
public static void exception(String msg, Throwable cause) {
logger.log(Level.SEVERE, msg + "\t " + callerRef(), cause);
}
}
Этот класс создает логгер, который выводит сообщения в консоль в формате, который включает временную метку, уровень логирования и само сообщение. Вы можете настраивать уровень логирования через метод setLogLevel()
. Убедитесь, что уровень логирования установлен как для логгера, так и для его обработчиков, иначе сообщения могут не отображаться.
Другие пользователи уже предоставили хорошие ответы на вопрос, почему это произошло (у ConsoleHandler
есть отдельная переменная уровня). Я повторно использую уровень логирования моему приложению и копирую его вверх по иерархии. Это также предоставляет простой способ обновлять уровни в любое время во время выполнения.
// Устанавливаем одинаковый уровень для всех логгеров и обработчиков вверх до родительского уровня
// OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL
Logger logger = Logger.getLogger(this.getClass().getPackage().getName());
// Level level = Level.parse("FINEST");
Level level = logger.getLevel();
Logger tempLogger = logger;
while(tempLogger != null) {
tempLogger.setLevel(level);
for(Handler handler : tempLogger.getHandlers())
handler.setLevel(level);
tempLogger = tempLogger.getParent();
}
Данный код проходит по всей иерархии логгеров и устанавливает одинаковый уровень логирования как для самих логгеров, так и для их обработчиков. Это позволяет сохранить консистентность настроек уровня логирования и легко обновлять их при необходимости.
Как зарегистрировать SQL-запросы в Spring Boot?
Знаете ли вы о каких-либо инструментах для анализа логов сборки мусора в Java? [закрыто]
Запуск Logback в режиме отладки
Что значит 'synchronized'?
Как объявить массив в одну строку?