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


110

状态的JavaDocsjava.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 . . .

问题陈述

我的示例将设置LevelFINER,因此我希望每个循环看到2条消息。相反,我为每个循环看到一条消息(Level.FINE消息丢失)。

为了查看FINEFINERFINEST)输出,需要进行哪些更改?

更新(解决方案)

感谢Vineet Reynolds的回答,该版本可以按照我的期望工作。它显示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

Answers:


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
Andrew Thompson

3
别客气。是的,如果有人编写的记录器只是将字符串转储到文件,控制台等中,那么设计确实可以解决您的问题
Vineet Reynolds

7
并在全局JRE库中更改logging.properties是可管理的吗?
James Anderson

2
请注意,记录器本身的级别必须比处理程序的级别限制少。记录Java之前,请检查记录器和处理程序之间的最大级别。因此,如果仅设置handler.setLevel(Level.FINER); 您将看不到任何内容,因为默认记录器级别为Level.INFO。您必须将其设置为FINER,FINEST或ALL。说logger.setLevel(Level.ALL);
Jeff_Alieffson 2015年

我想这将以Logger.getGlobal().getParent().getHandlers()[0].setLevel(Level.FINER);
单线

27

为什么

java.util.logging的根记录器默认为Level.INFO,附加的ConsoleHandler也默认为Level.INFOFINE低于INFO,因此默认情况下不会显示精细消息。


解决方案1

为您的整个应用程序创建一个记录器,例如从您的包名称或使用Logger.getGlobal(),然后将您自己的ConsoleLogger挂接到它。然后,要么要求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

或者,您可以降低根记录器的栏。

您可以通过代码设置它们:

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(false)这些代码对我有帮助,非常感谢。
aolphn

4

为什么我的Java日志记录不起作用

提供了一个jar文件,可以帮助您确定为什么登录无法正常工作。它为您提供了有关已安装哪些记录器和处理程序,设置了哪些级别以及在日志记录层次结构中的哪个级别的完整转储。


这是评论,不是答案。
hfontanez

4

为什么

如@Sheepy所述,它不起作用的原因是java.util.logging.Logger有一个默认为的根记录器Level.INFO,并且ConsoleHandler附加到该根记录器上的也默认为Level.INFO。因此,为了看FINE(,FINERFINEST)输出,你需要设置根记录器的默认值,其ConsoleHandlerLevel.FINE如下:

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


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

如@mins所述,您将在控制台上将消息打印两次INFO以上:首先由匿名记录器,然后由其父记录器(默认情况下也ConsoleHandler设置为root)的根记录器INFO。要禁用根记录器,您需要添加以下代码行:logger.setUseParentHandlers(false);

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

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

但是Logger.getLogger("").setLevel( Level.OFF );不会起作用,因为它仅阻止直接传递到根记录器的消息,而不是来自子记录器的消息。为了说明Logger Hierarchy工作原理,我画了下图:

在此处输入图片说明

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


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) {
          // ...
       }
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.