Чому повідомлення журналу 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) вихід?

Оновлення (рішення)

Завдяки відповіді Vineet Reynolds , ця версія працює відповідно до мого сподівання. Він відображає 3 x INFOповідомлення та 3 x 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 і вище: спочатку анонімним реєстратором, потім його батьком. Щоб відключити глобальний реєстратор, потрібно додати цей рядок коду: logger.setUseParentHandlers (помилковий);
хв

Я просто хочу підтвердити хв. Коментар щодо пар. ви отримаєте два виходи, якщо не будете використовувати .setUseParentHandlers (false);
xpagesbeast

Відповіді:


124

Логгери реєструють лише повідомлення, тобто вони створюють записи журналів (або запити реєстрації). Вони не публікують повідомлення до пунктів призначення, про що опікуються Обробники. Встановлення рівня реєстратора призводить до того, що він створює записи журналів, що відповідають цьому рівню чи вище.

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

Я рекомендую прочитати посібник з огляду журналу Java , щоб зрозуміти базовий дизайн. Посібник охоплює різницю між поняттям 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
Ласкаво просимо. І так, дизайн дійсно потрапить до вас, якщо хтось пише реєстратори, які просто скидали рядки у файл, консоль тощо.
Vineet Reynolds,

7
І зміна властивостей logging.properties у глобальній бібліотеці JRE керована?
Джеймс Андерсон

2
Зверніть увагу, що сам рівень реєстратора повинен бути менш обмежуючим, ніж рівень обробника. Перед входом у систему Java перевіряє максимальний рівень між реєстратором та обробником. Тож якщо ви встановите лише handler.setLevel (Level.FINER); ви нічого не побачите, оскільки типовим рівнем реєстратора є Level.INFO. Ви повинні встановити його НА ЗНАЧЕННЯ, НАЙКРАЩЕ або ВСЕ. Скажіть 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. Потім або попросіть кореневий реєстратор закрити (щоб уникнути дублювання виводу повідомлень вищого рівня), або попросіть реєстратора не пересилати журнали до root.

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. У цьому випадку ви можете встановити фільтр у кореневому журналі, щоб відфільтрувати повідомлення, не з вашої програми.


applog.setUseParentHandlers (помилково) ці коди допомагають мені, дуже дякую.
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

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


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) {
          // ...
       }
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.