tags:

views:

595

answers:

4

I'm developing a Java application that uses java.util.logging for its logging needs. This application uses a multitude of external libraries (JDBC, JMS clients, JSR-160 implementation, etc), some of which also use java.util.logging.

I want to set the log level for my Loggers to ALL when I specify a flag on the command line, but so far I have only found ways to set the level for all loggers, not just mine.

My loggers are all called "com.mycompany.myapp.SomeClass" and when I set the level for "com.mycompany.myapp" to ALL, no extra information is logged. When I set the level for the root logger to ALL, all information for all loggers is logged to the console, which is way too much information!

How can I set my own loggers to ALL without having all those other loggers flood my logfiles?

+2  A: 

Each Logger has a Handler with it's own log Level.

Is it possible that the Handler for your logger is not also set to ALL and is ignoring the messages? (This is messy, I know).

Here's a 2002 article on Java logging from O'Reilly.

Vinnie
Well, when I set log level on my loggers to ALL and leave the root logger alone, nothing happens. If I set the level of the root logger and handler to ALL as well, I get flooded with messages from other loggers than mine.
Gerco Dries
Right, I was just suggesting you set the log level to ALL on the *Handlers* associated with your logger. The Handlers may have their levels at INFO or higher and ignore your CONFIG or FINE messages. I don't know if this will fix your problem, but it's something easy to try. You can get access to the handlers using Logger.getHandlers()
Vinnie
My own loggers don't have any associated handlers. All output is done through the console handler on the root logger.Should I remove that handler and add a new handler to my "root" logger (com.mycompany.myapp)? What will happen if any of those other loggers have a SEVERE or WARNING to report?
Gerco Dries
Just one more comment.... if the root logger is set to something like WARN, but it's handlers are set to something like ALL, and your Logger is set to ALL, I'd think this would work.... you'd get all your messages and only those WARN or higher in the other packages.
Vinnie
Thanks for the suggestion, but that didn't work as expected. The root logger just drops the log events because they are below the WARN level.Removing the root loggers handler completely and adding handlers to my own "root" logger worked just fine. Now I no longer get errors or warnings from any other packages so that might still be a problem in the future. Perhaps I'll add a -vv mode to re-enable those loggers.
Gerco Dries
A: 

Setting the property

com.mycompany.myapp.level = ALL

should do what you want.

But all the loggers will need to be named correcly with the relevant class names! E.g.

package com.mycompany.myapp;

public class MyClass{
    private static Logger theLogger =
         Logger.getLogger(MyClass.class.getName());
    ...
}

Another possible explanation is that your configurations aren't being passed correctly to the logging framework. Try feeding

LogManager.getLogManager().readConfiguration(InputStream);

your logging configuration right at startup. That is what I do and this works for me.

Kris
Would it work for a class called com.mycompany.myapp.foo.Foo as well?
willcodejavaforfood
Sorry, but this doesn't work. The root logger ignores the messages because its level (also it's handlers level) is set to INFO. When I set those to ALL as well, I get all messages from all loggers, not just my own.
Gerco Dries
A: 

Yeah, the JDK’s own logging framework can be a real bitch sometimes. As you correctly noticed the log levels of the root logger’s handlers are the problem. The solution you are proposing in your question is almost a good one:

Logger logger = Logger.getLogger("com.mycompany.myapp");
Handler handler = new ConsoleHandler(); /* or whatever you like. */
handler.setLevel(Level.ALL);
logger.addHandler(handler);
logger.setLevel(Level.ALL);
logger.setUseParentHandlers(false); /* <- important. */

When you create logger “below” that logger, i.e. with names like “com.mycompany.myapp.foo.Foo", they will log only to the logger you created. The parent logger of that logger (the root logger) will not get any log messages from your application but will receive messages from other parts of the JDK such as Swing or AWT.

Bombe
This seems to work great! The only problem I have left is that I have my file handlers attached to my own "root" logger and whenever another logger has a critical message, it will only be logged to the console and not to my log file. I guess I can live with that, these situations are extremely rare.
Gerco Dries
I'm sure this works, but it does make it hard to tweak logging levels around as you'll need to recompile the code to change the levels.
Kris
Gerco, you could try attaching your file handler to the root logger as well. Or you might capture stdout of your application from your shell.
Bombe
Kris, what exactly stops you from iterating through all your loggers and set their levels according to some configuration file? Mind you, what I suggested above is merely the base setup to separate application and JDK logging from one another. You can still use the Logging API to perform all kinds of neat tricks.
Bombe
+2  A: 

Actually, I'm not sure why your having the problems you've described. I've created a simple JUnit test (below) and setting the log levels works exactly as I expect (which also seems inline with the way you expected them to work).

Are you trying to log messages with levels set below INFO in your custom logger? As you can see from the tests I've included, the default logging handler is set to INFO by default. You need to change that Handler's level to see FINE messages (also shown).

import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.Test;

public class SimpleLoggerTest {

    private void logMessages(Logger logger) {
        logger.warning(getLoggerName(logger) + ": warning message");
        logger.info(getLoggerName(logger) + ": info message");
        logger.fine(getLoggerName(logger) + ": fine message");
    }


    private String getLoggerName(Logger logger) {
        String loggerName = logger.getName();
        if (loggerName.isEmpty()) {
            return "[root logger]";
        }
        return loggerName;
    }

    private void listHandlerLevels(Logger logger) {
        for (Handler handler : logger.getHandlers()) {
            logger.info(getLoggerName(logger) + ": handler level = " + handler.getLevel());
        }
        Logger parentLogger = logger.getParent();
        if (null != parentLogger) {
            for (Handler handler : parentLogger.getHandlers()) {
                logger.info("parent logger handler (" + getLoggerName(parentLogger) + "): handler level = " + handler.getLevel());
            }
        }
    }

    private void setHandlerLevels(Logger logger, Level level) {
        for (Handler handler : logger.getHandlers()) {
            handler.setLevel(level);
        }
        Logger parentLogger = logger.getParent();
        if (null != parentLogger) {
            for (Handler handler : parentLogger.getHandlers()) {
                handler.setLevel(level);
            }
        }
    }
    @Test
    public void testLoggingLevel() {
        Logger myLogger = Logger.getLogger(SimpleLoggerTest.class.getName());
        Logger rootLogger = myLogger.getParent();

        // list the default handler levels
        listHandlerLevels(myLogger);
        listHandlerLevels(rootLogger);

        // log some messages
        logMessages(myLogger);
        logMessages(rootLogger);

        // change the logger levels
        myLogger.setLevel(Level.ALL);
        rootLogger.setLevel(Level.WARNING);

        // list the handler levels again
        listHandlerLevels(myLogger);
        listHandlerLevels(rootLogger);

        // log some messages (again)
        logMessages(myLogger);
        logMessages(rootLogger);

        // change Handler levels to FINE
        setHandlerLevels(myLogger, Level.FINE);

        // list the handler levels (last time)
        listHandlerLevels(myLogger);
        listHandlerLevels(rootLogger);

        // log some messages (last time)
        logMessages(myLogger);
        logMessages(rootLogger);
    }
}

Produces this output...

May 13, 2009 10:46:53 AM SimpleLoggerTest listHandlerLevels
INFO: parent logger handler ([root logger]): handler level = INFO
May 13, 2009 10:46:53 AM java.util.logging.LogManager$RootLogger log
INFO: [root logger]: handler level = INFO
May 13, 2009 10:46:53 AM SimpleLoggerTest logMessages
WARNING: SimpleLoggerTest: warning message
May 13, 2009 10:46:53 AM SimpleLoggerTest logMessages
INFO: SimpleLoggerTest: info message
May 13, 2009 10:46:53 AM java.util.logging.LogManager$RootLogger log
WARNING: [root logger]: warning message
May 13, 2009 10:46:53 AM java.util.logging.LogManager$RootLogger log
INFO: [root logger]: info message
May 13, 2009 10:46:53 AM SimpleLoggerTest listHandlerLevels
INFO: parent logger handler ([root logger]): handler level = INFO
May 13, 2009 10:46:53 AM SimpleLoggerTest logMessages
WARNING: SimpleLoggerTest: warning message
May 13, 2009 10:46:53 AM SimpleLoggerTest logMessages
INFO: SimpleLoggerTest: info message
May 13, 2009 10:46:53 AM java.util.logging.LogManager$RootLogger log
WARNING: [root logger]: warning message
May 13, 2009 10:46:53 AM SimpleLoggerTest listHandlerLevels
INFO: parent logger handler ([root logger]): handler level = FINE
May 13, 2009 10:46:53 AM SimpleLoggerTest logMessages
WARNING: SimpleLoggerTest: warning message
May 13, 2009 10:46:53 AM SimpleLoggerTest logMessages
INFO: SimpleLoggerTest: info message
May 13, 2009 10:46:53 AM SimpleLoggerTest logMessages
FINE: SimpleLoggerTest: fine message
May 13, 2009 10:46:53 AM java.util.logging.LogManager$RootLogger log
WARNING: [root logger]: warning message

This is what I was trying to convey in my other response.

Vinnie
I've used your test program and extended it with another logger "theirLogger" and also called logMessages() with it. It all seems to work exactly as expected. I will have to rethink my understanding of java.util.logging, apparantly.
Gerco Dries
Ok... I'm done thinking. I have come to the conclusion that whoever thought that having the handlers magically "replicated" to all child loggers and make *their* levels more important than the Logger levels were either insane or trying to make everyone else go insane!Anyway, it works now. The first think I'm going to do is find a different logging framework that does make sense to mere mortals.
Gerco Dries