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

发布于 2024-11-15 11:35:50 字数 2325 浏览 8 评论 0原文

JavaDocs java.util.logging.Level 状态:


按降序排列的级别为:

  • SEVERE(最高值)
  • WARNING
  • 信息
  • 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 的回答,这个版本符合我的预期。它显示 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));
        }
    }
}

The JavaDocs for java.util.logging.Level state:


The levels in descending order are:

  • SEVERE (highest value)
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST (lowest value)

Source

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

Output

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

Problem statement

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

Question

What needs changing in order to see the FINE (, FINER or FINEST) output?

Update (solution)

Thanks to Vineet Reynolds' answer, this version works according to my expectation. It displays 3 x INFO messages, & 3 x FINE messages.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

扛刀软妹 2024-11-22 11:35:50

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

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

我建议阅读 Java 日志记录概述 指南,以便了解底层设计。该指南涵盖了记录器和处理程序概念之间的区别。

编辑处理程序级别

1.使用配置文件

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

java.util.logging.ConsoleHandler.level = FINER

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

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

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

Loggers only log the message, i.e. they create the log records (or logging requests). They do not publish the messages to the destinations, which is taken care of by the Handlers. Setting the level of a logger, only causes it to create log records matching that level or higher.

You might be using a ConsoleHandler (I couldn't infer where your output is System.err or a file, but I would assume that it is the former), which defaults to publishing log records of the level Level.INFO. You will have to configure this handler, to publish log records of level Level.FINER and higher, for the desired outcome.

I would recommend reading the Java Logging Overview guide, in order to understand the underlying design. The guide covers the difference between the concept of a Logger and a Handler.

Editing the handler level

1. Using the Configuration file

The java.util.logging properties file (by default, this is the logging.properties file in JRE_HOME/lib) can be modified to change the default level of the ConsoleHandler:

java.util.logging.ConsoleHandler.level = FINER

2. Creating handlers at runtime

This is not recommended, for it would result in overriding the global configuration. Using this throughout your code base will result in a possibly unmanageable logger configuration.

Handler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.FINER);
Logger.getAnonymousLogger().addHandler(consoleHandler);
耳钉梦 2024-11-22 11:35:50

原因

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

The Why

java.util.logging has a root logger that defaults to Level.INFO, and a ConsoleHandler attached to it that also defaults to Level.INFO.
FINE is lower than INFO, so fine messages are not displayed by default.


Solution 1

Create a logger for your whole application, e.g. from your package name or use Logger.getGlobal(), and hook your own ConsoleLogger to it.
Then either ask root logger to shut up (to avoid duplicate output of higher level messages), or ask your logger to not forward logs to 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

Solution 2

Alternatively, you may lower the root logger's bar.

You can set them by code:

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

Or with logging configuration file, if you are using it:

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

By lowering the global level, you may start seeing messages from core libraries, such as from some Swing or JavaFX components.
In this case you may set a Filter on the root logger to filter out messages not from your program.

甩你一脸翔 2024-11-22 11:35:50

为什么

正如 @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 及以上的消息:首先由匿名记录器打印,然后由其父级(根记录器)默认设置为 INFOConsoleHandler 。要禁用根记录器,您需要添加这行代码: 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 可用于关闭日志记录。如果新级别为空,则意味着该节点应从具有特定(非空)级别值的最近祖先继承其级别。

WHY

As mentioned by @Sheepy, the reason why it doesn't work is that java.util.logging.Logger has a root logger that defaults to Level.INFO, and the ConsoleHandler attached to that root logger also defaults to Level.INFO. Therefore, in order to see the FINE (, FINER or FINEST) output, you need to set the default value of the root logger and its ConsoleHandler to Level.FINE as follows:

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

The problem of your Update (solution)

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

There are other ways to prevent logs from being processed by default Console handler of the root logger mentioned by @Sheepy, e.g.:

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

But Logger.getLogger("").setLevel( Level.OFF ); won't work because it only blocks the message passed directly to the root logger, not the message comes from a child logger. To illustrate how the Logger Hierarchy works, I draw the following diagram:

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.

你的背包 2024-11-22 11:35:50

为什么我的 java 日志记录不工作

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

why is my java logging not working

provides a jar file that will help you work out why your logging in not working as expected.
It gives you a complete dump of what loggers and handlers have been installed and what levels are set and at which level in the logging hierarchy.

醉梦枕江山 2024-11-22 11:35:50

尝试了其他变体,这可能是正确的

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

Tried other variants, this can be proper

    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");
踏雪无痕 2024-11-22 11:35:50

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

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.

东京女 2024-11-22 11:35:50

就可维护性和更改设计而言,此解决方案对我来说似乎更好:

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

    <前><代码># 日志记录
    处理程序 = java.util.logging.ConsoleHandler
    .level = 全部

    # 控制台日志记录
    java.util.logging.ConsoleHandler.level = ALL

  2. 从代码加载属性文件:

    public static java.net.URLretrieveURLOfJarResource(String resourceName) {
       return Thread.currentThread().getContextClassLoader().getResource(resourceName);
    }
    
    公共同步无效initializeLogger(){
       尝试 (InputStream is =retrieveURLOfJarResource("logging.properties").openStream()) {
          LogManager.getLogManager().readConfiguration(is);
       } catch (IOException e) {
          // ...
       }
    }
    

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

  1. Create the logging property file embedding it in the resource project folder, to be included in the jar file:

    # 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) {
          // ...
       }
    }
    
玻璃人 2024-11-22 11:35:50

要更改 logcat 级别:

adb shell setprop log.tag.<YOUR_LOG_TAG> <LEVEL>

例如:

C:\Users\my_name\AppData\Local\Android\Sdk\platform-tools\adb.exe shell setprop log.tag.com.mycompany.myapp VERBOSE

这对于 Logger 有效的原因如下:

根记录器(记录器的父级)正在使用内部记录处理程序<代码>com.android.internal.logging.AndroidHandler。此处理程序在其 publish 方法中检查 Log.isLoggable 并写入 Log。

关于的注意事项:

基本上这是您的记录器的名称。对于全局 Logger,它是 Logger.GLOBAL_LOGGER_NAME。对于匿名记录器,它是“null”。标签限制为 23 个字符。

AndroidHandler 执行从记录器名称到标签的转换:

    private static String loggerNameToTag(String loggerName) {
        // Anonymous logger.
        if (loggerName == null) {
            return "null";
        }

        int length = loggerName.length();
        if (length <= 23) {
            return loggerName;
        }

        int lastPeriod = loggerName.lastIndexOf(".");
        return length - (lastPeriod + 1) <= 23
                ? loggerName.substring(lastPeriod + 1)
                : loggerName.substring(loggerName.length() - 23);
    }

To change the logcat level:

adb shell setprop log.tag.<YOUR_LOG_TAG> <LEVEL>

For example:

C:\Users\my_name\AppData\Local\Android\Sdk\platform-tools\adb.exe shell setprop log.tag.com.mycompany.myapp VERBOSE

Here is why this works for Logger:

The root logger, which is the parent of your logger, is using an internal logging handler com.android.internal.logging.AndroidHandler. This handler in its publish method is checking Log.isLoggable and writing to Log.

Note about <YOUR_LOG_TAG>:

Basically this is the name of your Logger. For global Logger it is Logger.GLOBAL_LOGGER_NAME. For anonymous logger it is "null". Tag is limited to 23 characters.

AndroidHandler does a transformation from the logger name to the tag:

    private static String loggerNameToTag(String loggerName) {
        // Anonymous logger.
        if (loggerName == null) {
            return "null";
        }

        int length = loggerName.length();
        if (length <= 23) {
            return loggerName;
        }

        int lastPeriod = loggerName.lastIndexOf(".");
        return length - (lastPeriod + 1) <= 23
                ? loggerName.substring(lastPeriod + 1)
                : loggerName.substring(loggerName.length() - 23);
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文