为什么 Level. FINE 日志消息没有显示?

用于 java.util.logging.Level的 JavaDocs状态:


这些级别按降序排列如下:

  • 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 . . .

问题陈述

My example sets the Level to FINER, so I was expecting to see 2 messages for each loop. Instead I see a single message for each loop (the Level.FINE messages are missing).

提问

为了看到 FINE(,FINERFINEST)输出,需要改变什么?

更新(解决方案)

感谢 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));
}
}
}
131921 次浏览

日志记录器只记录消息,也就是说,它们创建日志记录(或记录请求)。它们不将消息发布到目的地,目的地由处理程序负责。设置日志记录器的级别,只会导致它与该级别或更高的 创造日志记录匹配。

您可能正在使用 ConsoleHandler(我无法推断输出是 System.err 或文件,但我假设它是前者) ,它默认为发布级别 Level.INFO的日志记录。您必须配置此处理程序,以发布级别为 Level.FINER或更高的日志记录,以获得所需的结果。

我建议您阅读 Java 日志记录概述指南,以便理解基础设计。该指南涵盖了 Logger 和 Handler 概念之间的区别。

编辑处理程序级别

1. 使用 Configuration 文件

可以修改 java.util.log 属性文件(默认情况下,这是 JRE_HOME/lib中的 logging.properties文件) ,以更改 Console Handler 的默认级别:

java.util.logging.ConsoleHandler.level = FINER

2. 在运行时创建处理程序

不建议这样做,因为这会导致覆盖全局配置。在整个代码库中使用这种方法将导致可能无法管理的日志记录器配置。

Handler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.FINER);
Logger.getAnonymousLogger().addHandler(consoleHandler);

为什么我的 Java 日志不工作

提供了一个 jar 文件,它将帮助您解决为什么您的登录不能按预期工作。 它提供了已安装的日志记录器和处理程序、设置的级别以及日志记录层次结构中的级别的完整转储。

为什么

Log 有一个默认为 Level.INFO的根日志记录器,还有一个默认为 Level.INFO的附加 Console Handler。 FINE低于 INFO,因此默认情况下不显示精细消息。


解决方案1

为您的整个应用程序创建一个日志记录器,例如从您的包名称或使用 Logger.getGlobal(),并将您自己的 Console Logger 连接到它。 然后要么要求 root 日志记录器关闭(以避免重复输出更高级别的消息) ,要么要求您的日志记录器 不是转发日志到 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

或者,您可以降低根日志记录器的工具条。

You can set them by code:

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 组件的消息。 在这种情况下,您可以在根日志记录器上设置一个 Filter,以过滤出不是来自您的程序的消息。

I found my actual problem and it was not mentioned in any answer: some of my unit-tests were causing logging initialization code to be run multiple times within the same test suite, messing up the logging on the later tests.

尝试其他变种,这样可以适当

    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");

为什么

正如@Sheepy 提到的,它无法工作的原因是 java.util.logging.Logger有一个默认为 Level.INFO的根日志记录器,而连接到该根日志记录器的 ConsoleHandler也默认为 Level.INFO。因此,为了查看 FINE(、 FINERFINEST)输出,您需要将根日志记录器及其 ConsoleHandler的默认值设置为 Level.FINE,如下所示:

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


更新(解决方案)的问题

As mentioned by @mins, you will have the messages printed twice on the console for INFO and above: first by the anonymous logger, then by its parent, the root logger which also has a ConsoleHandler set to INFO by default. To disable the root logger, you need to add this line of code: logger.setUseParentHandlers(false);

还有其他方法可以防止日志被@Sheepy 提到的根日志程序的默认控制台处理程序处理,例如:

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

但是 Logger.getLogger("").setLevel( Level.OFF );不会起作用,因为它只阻止直接传递给根日志记录器的消息,而不阻止来自子日志记录器的消息。为了说明 Logger Hierarchy是如何工作的,我绘制了以下图表:

enter image description here

public void setLevel(Level newLevel) set the log level specifying which message levels will be logged by this logger. Message levels lower than this value will be discarded. The level value Level.OFF can be used to turn off logging. If the new level is null, it means that this node should inherit its level from its nearest ancestor with a specific (non-null) level value.

This solution appears better to me, regarding maintainability and design for change:

  1. 创建日志记录属性文件,将其嵌入到资源项目文件夹中,以包括在 jar 文件中:

    # Logging
    handlers = java.util.logging.ConsoleHandler
    .level = ALL
    
    
    # Console Logging
    java.util.logging.ConsoleHandler.level = ALL
    
  2. Load the property file from code:

    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) {
    // ...
    }
    }