Why are the Level.FINE logging messages not showing?
Asked Answered
T

8

132

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));
        }
    }
}
Tussle answered 11/6, 2011 at 11:43 Comment(4)
Seems to me that you will have the messages printed twice on the console for INFO and above: first by the anonymous logger, then by its parent, the global logger which also has a ConsoleHandler set to INFO by default. To disable the global logger, you need to add this line of code: logger.setUseParentHandlers(false);Finite
I just want to confirm mins comment about the doubles. you will get two outputs unless you use .setUseParentHandlers(false);Airliah
In your solution, logger has two Handlers (one default at INFO and one added at FINE), so logger.info("something") would print twiceBrame
What is point of adding a logging properties file and setting configuration if everything at the end is governed by root logger only? @andrew-thompsonSecunda
S
140

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);
Slaveholder answered 11/6, 2011 at 11:53 Comment(5)
Thanks. That did the trick. I realize now why I was initially so confused. I had previously worked with logging levels, but my implementation at that time simply dropped every logged message into a list that was displayed with no regard to the Handler.Tussle
You're welcome. And yeah, the design does get to you, if one has been writing loggers that simply dumped strings into a file, console etc.Slaveholder
And changing the logging.properties in the global JRE library is manageable?Agree
Please note, that the level of logger itself must be less restrictive, than handler's level. Before logging Java checks the maximum level between logger and handler. So if you set only handler.setLevel(Level.FINER); you will see nothing because the default logger level is Level.INFO. You must set it to FINER, FINEST or ALL. Say logger.setLevel(Level.ALL);Pennyroyal
I guess this will solve the problem as a one-liner, even when you use anonymous and named loggers in a default environment: Logger.getGlobal().getParent().getHandlers()[0].setLevel(Level.FINER);Sussman
R
33

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.

Rhineland answered 6/8, 2015 at 10:10 Comment(2)
applog.setUseParentHandlers( false ) these code help me ,thanks so much.Gladi
Thank you for specifying that the level should be changed both in the root logger and in the console handler!Pail
U
13

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.

Unsustainable answered 26/1, 2018 at 3:29 Comment(0)
A
4

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.

Aerotherapeutics answered 2/5, 2013 at 23:17 Comment(1)
This is a comment, not an answer.Slash
S
1

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");
Salzhauer answered 15/8, 2017 at 7:5 Comment(1)
This answer and Joe Yichong's point out that the logger first gets the log message, but then passes the message up the chain to the handlers. This is confusing but works OK in practice. The upshot is that most handlers are actually at the root level so they can be controlled more easily by an operator. The technique of grabbing the root logger and setting all of its handlers is what I do when I need to control logging in a program (which you normally should NOT do).Lungwort
C
0

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.

Cammi answered 11/6, 2017 at 17:18 Comment(0)
E
0

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) {
          // ...
       }
    }
    
Ermey answered 10/3, 2018 at 17:5 Comment(0)
O
0

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);
    }
Organometallic answered 25/1, 2023 at 13:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.