Почему не отображаются сообщения журнала Level.FINE?

110

Документы JavaDocs дляjava.util.logging.Level состояния:


Уровни в порядке убывания:

  • SEVERE (максимальное значение)
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST (наименьшее значение)

Источник

import java.util.logging.*;

class LoggingLevelsBlunder {

    public static void main(String[] args) {
        Logger logger = Logger.getAnonymousLogger();
        logger.setLevel(Level.FINER);
        System.out.println("Logging level is: " + logger.getLevel());
        for (int ii=0; ii<3; ii++) {
            logger.log(Level.FINE, ii + " " + (ii*ii));
            logger.log(Level.INFO, ii + " " + (ii*ii));
        }
    }
}

Вывод

Logging level is: FINER
Jun 11, 2011 9:39:23 PM LoggingLevelsBlunder main
INFO: 0 0
Jun 11, 2011 9:39:24 PM LoggingLevelsBlunder main
INFO: 1 1
Jun 11, 2011 9:39:24 PM LoggingLevelsBlunder main
INFO: 2 4
Press any key to continue . . .

Постановка задачи

В моем примере устанавливается Levelзначение FINER, поэтому я ожидал увидеть по 2 сообщения для каждого цикла. Вместо этого я вижу одно сообщение для каждого цикла ( Level.FINEсообщения отсутствуют).

Вопрос

Что нужно изменить, чтобы увидеть результат FINE(, FINERили FINEST)?

Обновление (решение)

Благодаря ответу Винита Рейнольдса эта версия работает в соответствии с моими ожиданиями. Он отображает 3 INFOсообщения и 3 FINEсообщения.

import java.util.logging.*;

class LoggingLevelsBlunder {

    public static void main(String[] args) {
        Logger logger = Logger.getAnonymousLogger();
        // LOG this level to the log
        logger.setLevel(Level.FINER);

        ConsoleHandler handler = new ConsoleHandler();
        // PUBLISH this level
        handler.setLevel(Level.FINER);
        logger.addHandler(handler);

        System.out.println("Logging level is: " + logger.getLevel());
        for (int ii=0; ii<3; ii++) {
            logger.log(Level.FINE, ii + " " + (ii*ii));
            logger.log(Level.INFO, ii + " " + (ii*ii));
        }
    }
}
Эндрю Томпсон
источник
10
Мне кажется, что сообщения будут напечатаны дважды на консоли для INFO и выше: сначала анонимным регистратором, затем его родителем, глобальным регистратором, у которого также ConsoleHandler установлен на INFO по умолчанию. Чтобы отключить глобальный регистратор, вам нужно добавить эту строку кода: logger.setUseParentHandlers (false);
минут
Я просто хочу подтвердить минус комментарий о дублях. вы получите два выхода, если не используете .setUseParentHandlers (false);
xpagesbeast 07

Ответы:

124

Регистраторы только регистрируют сообщение, т. Е. Создают записи журнала (или запросы регистрации). Они не публикуют сообщения по назначению, о чем заботятся обработчики. Установка уровня регистратора приводит только к тому, что он создает записи журнала, соответствующие этому уровню или выше.

Возможно, вы используете ConsoleHandler(я не мог понять, где ваш вывод - System.err или файл, но я предполагаю, что это первый), который по умолчанию публикует записи журнала уровня Level.INFO. Вам нужно будет настроить этот обработчик, чтобы публиковать записи журнала уровня Level.FINERи выше для желаемого результата.

Я бы рекомендовал прочитать руководство Java Logging Overview , чтобы понять базовый дизайн. В руководстве описывается разница между концепцией Logger и Handler.

Редактирование уровня обработчика

1. Использование файла конфигурации

Файл свойств java.util.logging (по умолчанию это logging.propertiesфайл в JRE_HOME/lib) можно изменить, чтобы изменить уровень ConsoleHandler по умолчанию:

java.util.logging.ConsoleHandler.level = FINER

2. Создание обработчиков во время выполнения

Это не рекомендуется, так как это приведет к отмене глобальной конфигурации. Использование этого во всей базе кода приведет к, возможно, неуправляемой конфигурации регистратора.

Handler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.FINER);
Logger.getAnonymousLogger().addHandler(consoleHandler);
Винит Рейнольдс
источник
2
Спасибо. Это сработало. Теперь я понимаю, почему я изначально был так сбит с толку. Раньше я работал с уровнями ведения журнала, но моя реализация в то время просто отбрасывала каждое зарегистрированное сообщение в список, который отображался безотносительно к Handler.
Эндрю Томпсон
3
Пожалуйста. И да, дизайн действительно подойдет вам, если кто-то писал логгеры, которые просто сбрасывали строки в файл, консоль и т. Д.
Винит Рейнольдс
7
И изменить logging.properties в глобальной библиотеке JRE можно?
Джеймс Андерсон
2
Обратите внимание, что уровень самого регистратора должен быть менее строгим, чем уровень обработчика. Перед регистрацией Java проверяет максимальный уровень между регистратором и обработчиком. Итак, если вы установите только handler.setLevel (Level.FINER); вы ничего не увидите, потому что уровень ведения журнала по умолчанию - Level.INFO. Вы должны установить его на FINER, FINEST или ALL. Скажите logger.setLevel (Level.ALL);
Jeff_Alieffson
Я предполагаю, что это решит проблему как однострочный, даже если вы используете анонимные и именованные регистраторы в среде по умолчанию:Logger.getGlobal().getParent().getHandlers()[0].setLevel(Level.FINER);
Марк Джеронимус
27

Почему

В java.util.logging есть корневой регистратор, который по умолчанию используется Level.INFO, и прикрепленный к нему ConsoleHandler, который также по умолчанию Level.INFO. FINEменьше чем INFO, поэтому мелкие сообщения по умолчанию не отображаются.


Решение 1

Создайте регистратор для всего приложения, например, из имени вашего пакета или использования Logger.getGlobal(), и подключите к нему свой собственный ConsoleLogger. Затем либо попросите корневого регистратора отключиться (чтобы избежать дублирования вывода сообщений более высокого уровня), либо попросите регистратор не пересылать журналы корневому каталогу.

public static final Logger applog = Logger.getGlobal();
...

// Create and set handler
Handler systemOut = new ConsoleHandler();
systemOut.setLevel( Level.ALL );
applog.addHandler( systemOut );
applog.setLevel( Level.ALL );

// Prevent logs from processed by default Console handler.
applog.setUseParentHandlers( false ); // Solution 1
Logger.getLogger("").setLevel( Level.OFF ); // Solution 2

Решение 2

В качестве альтернативы вы можете уменьшить полосу корневого регистратора.

Вы можете установить их по коду:

Logger rootLog = Logger.getLogger("");
rootLog.setLevel( Level.FINE );
rootLog.getHandlers()[0].setLevel( Level.FINE ); // Default console handler

Или с файлом конфигурации журналирования, если вы его используете :

.level = FINE
java.util.logging.ConsoleHandler.level = FINE

Понизив глобальный уровень, вы можете начать видеть сообщения из основных библиотек, например, из некоторых компонентов Swing или JavaFX. В этом случае вы можете установить фильтр в корневом регистраторе, чтобы отфильтровать сообщения не из вашей программы.

Sheepy
источник
applog.setUseParentHandlers (false) этот код мне помогает, большое спасибо.
aolphn
4

почему мой журнал java не работает

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

Мэтью
источник
Это комментарий, а не ответ.
hfontanez
4

ЗАЧЕМ

Как упоминалось @Sheepy, причина, по которой это не работает, заключается в том, что java.util.logging.Loggerу корневого регистратора, который по умолчанию используется Level.INFO, и ConsoleHandlerприкрепленного к этому корневому регистратору также по умолчанию Level.INFO. Поэтому для того, чтобы увидеть FINE(, FINERили FINESTвыход), вам необходимо установить значение по умолчанию для корневого регистратора и его ConsoleHandlerк Level.FINEследующим образом :

Logger.getLogger("").setLevel(Level.FINE);
Logger.getLogger("").getHandlers()[0].setLevel(Level.FINE);


Проблема вашего обновления (решение)

Как упомянуто @mins, у вас будут сообщения, напечатанные дважды на консоли для INFOи выше: сначала анонимным регистратором, затем его родителем, корневым регистратором, который также имеет ConsoleHandlerзначение INFOпо умолчанию. Чтобы отключить корневой регистратор, вам нужно добавить эту строку кода:logger.setUseParentHandlers(false);

Существуют и другие способы предотвратить обработку журналов обработчиком консоли по умолчанию корневого регистратора, упомянутого @Sheepy, например:

Logger.getLogger("").getHandlers()[0].setLevel( Level.OFF );

Но Logger.getLogger("").setLevel( Level.OFF );не будет работать, потому что он блокирует только сообщение, переданное непосредственно корневому регистратору, а не сообщение, поступающее от дочернего регистратора. Чтобы проиллюстрировать, как это Logger Hierarchyработает, я нарисовал следующую диаграмму:

введите описание изображения здесь

public void setLevel(Level newLevel)установить уровень журнала, определяющий, какие уровни сообщений будут регистрироваться этим регистратором. Уровни сообщений ниже этого значения будут отброшены. Значение уровня Level.OFF можно использовать для отключения регистрации. Если новый уровень равен нулю, это означает, что этот узел должен унаследовать свой уровень от своего ближайшего предка с определенным (ненулевым) значением уровня.

Джо Ичонг
источник
0

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

Alex R
источник
0

Пробовал другие варианты, это может быть правильно

    Logger logger = Logger.getLogger(MyClass.class.getName());        
    Level level = Level.ALL;
    for(Handler h : java.util.logging.Logger.getLogger("").getHandlers())    
        h.setLevel(level);
    logger.setLevel(level);
// this must be shown
    logger.fine("fine");
    logger.info("info");
женіхох
источник
0

Это решение кажется мне лучше с точки зрения ремонтопригодности и дизайна для изменений:

  1. Создайте файл свойств ведения журнала, встраивая его в папку проекта ресурсов, чтобы включить его в файл jar:

    # Logging
    handlers = java.util.logging.ConsoleHandler
    .level = ALL
    
    # Console Logging
    java.util.logging.ConsoleHandler.level = ALL
  2. Загрузите файл свойств из кода:

    public static java.net.URL retrieveURLOfJarResource(String resourceName) {
       return Thread.currentThread().getContextClassLoader().getResource(resourceName);
    }
    
    public synchronized void initializeLogger() {
       try (InputStream is = retrieveURLOfJarResource("logging.properties").openStream()) {
          LogManager.getLogManager().readConfiguration(is);
       } catch (IOException e) {
          // ...
       }
    }
user2627331
источник