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

时间:2011-06-11 11:43:44

标签: java logging java.util.logging

JavaDocs for 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(,FINERFINEST)输出,需要更改哪些内容?

更新(解决方案)

感谢Vineet Reynolds' answer,这个版本符合我的期望。它显示3x INFO条消息,&amp; 3x 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));
        }
    }
}

7 个答案:

答案 0 :(得分:118)

记录器仅记录消息,即它们创建日志记录(或记录请求)。它们不会将消息发布到目标,这由处理程序负责。设置记录器的级别只会使其创建匹配该级别或更高级别的日志记录。

您可能正在使用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);

答案 1 :(得分:24)

为什么

java.util.logging有一个默认为Level.INFO的根记录器,以及一个默认为Level.INFO的ConsoleHandler。 FINE低于INFO,因此默认情况下不会显示精美邮件。

解决方案1 ​​

为整个应用程序创建一个记录器,例如从您的包名称或使用Logger.getGlobal(),并将您自己的ConsoleLogger挂钩到它。 然后要求root logger关闭(以避免重复输出更高级别的消息),或者让您的记录器not forward logs以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

或者使用日志配置文件,如果您是using it

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

通过降低全局级别,您可能会开始看到来自核心库的消息,例如来自某些Swing或JavaFX组件的消息。 在这种情况下,您可以在根记录器上设置Filter以过滤掉不是来自您的程序的消息。

答案 2 :(得分:4)

why is my java logging not working

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

答案 3 :(得分:3)

<强> WHY

正如@Sheepy所提到的,它不起作用的原因是java.util.logging.Logger有一个根记录器,默认为Level.INFO,而ConsoleHandler附加到该根记录器也默认为Level.INFO。因此,要查看FINE(,FINERFINEST)输出,您需要将根记录器的默认值及其ConsoleHandler设置为{{1如下:

Level.FINE


您的更新问题(解决方案)

如@mins所述,您将在Logger.getLogger("").setLevel(Level.FINE); Logger.getLogger("").getHandlers()[0].setLevel(Level.FINE); 及更高版本的控制台上打印两次消息:首先是匿名记录器,然后是其父级,根记录器还有{{1}默认设置为INFO。要禁用根记录器,您需要添加以下代码行:ConsoleHandler

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

INFO

logger.setUseParentHandlers(false);无法正常工作,因为它只会阻止直接传递给根记录器的消息,而不是来自子记录器的消息。为了说明Logger.getLogger("").getHandlers()[0].setLevel( Level.OFF ); 的工作原理,我绘制了下图:

enter image description here

public void setLevel(Level newLevel)设置日志级别,指定此记录器将记录哪些消息级别。低于此值的消息级别将被丢弃。级别值Level.OFF可用于关闭日志记录。如果新级别为null,则表示此节点应从具有特定(非空)级别值的最近祖先继承其级别。

答案 4 :(得分:0)

我发现了我的实际问题并且在任何答案中都没有提到:我的一些单元测试导致日志记录初始化代码在同一个测试套件中多次运行,搞乱了以后测试的日志记录。

答案 5 :(得分: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");

答案 6 :(得分: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) {
          // ...
       }
    }