January 5, 2017

JDK Logger/Java Logging API

Background

Since JDK 1.4 there is a default Logging API in Java. The entire Logging API is contained in the package java.util.logging.

Reference: https://docs.oracle.com/javase/8/docs/technotes/guides/logging/overview.html

Usage

import java.util.logging.Level;
import java.util.logging.Logger;

public class LoggerTest {

    private static final Logger log = Logger.getLogger(LoggerTest.class.getName());

    public void something() throws Exception {
        if (log.isLoggable(Level.FINEST)) {
            log.finest("Enter something()...");
        }

        log.info("Logging result");

        log.warning("Another logging result");

        log.log(Level.SEVERE, "Nullpoint here", new NullPointerException("divide by zero"));

        if (log.isLoggable(Level.FINEST)) {
            log.finest("Exit something()");
        }
    }
}

Default Logging Configuration

Java comes with a default logging configuration file, that only contains a ConsoleHandler and writes to standard error.

$JAVA_HOME/lib/logging.properties

############################################################
#       Default Logging Configuration File
#
# You can use a different file by specifying a filename
# with the java.util.logging.config.file system property.  
# For example java -Djava.util.logging.config.file=myfile
############################################################

############################################################
#       Global properties
############################################################

# "handlers" specifies a comma separated list of log Handler 
# classes.  These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler

# To also add the FileHandler, use the following line instead.
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers.  For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

# Example to customize the SimpleFormatter output format 
# to print one-line log message like this:
#     <level>: <log message> [<date/time>]
#
# java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n

############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################

# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
com.xyz.foo.level = SEVERE

Configuration

To change default configuration, create a new configuration file and add a system property java.util.logging.config.file to you java process, which point to yours configuration file. Example:

java ... -Djava.util.logging.config.file=/tmp/logging.properties ...

The available handlers/appenders and their configuration are:

# Default logging.properties $JRE_HOME/lib/logging.properties
# java -Djava.util.logging.config.file=logging.properties
handlers                                   = java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# Global Logging Level.
.level                                     = INFO

# See https://docs.oracle.com/javase/8/docs/api/java/util/logging/FileHandler.html
# Do NOT limit the messages here, it's controlled by global and/or specific classes
java.util.logging.FileHandler.level        = ALL
java.util.logging.FileHandler.formatter    = java.util.logging.SimpleFormatter
java.util.logging.FileHandler.encoding     = UTF-8
java.util.logging.FileHandler.limit        = 50000
java.util.logging.FileHandler.count        = 10
java.util.logging.FileHandler.pattern      = %h/java-%g.log
java.util.logging.FileHandler.append       = true

# See https://docs.oracle.com/javase/8/docs/api/java/util/logging/ConsoleHandler.html
# Do NOT limit the messages here, it's controlled by global and/or specific classes
java.util.logging.ConsoleHandler.level     = ALL
java.util.logging.ConsoleHandler.encoding  = UTF-8

# See https://docs.oracle.com/javase/8/docs/api/java/util/logging/StreamHandler.html
# Do NOT limit the messages here, it's controlled by global and/or specific classes
java.util.logging.StreamHandler.level      = ALL
java.util.logging.StreamHandler.encoding   = UTF-8

# See https://docs.oracle.com/javase/8/docs/api/java/util/logging/SocketHandler.html
# Do NOT limit the messages here, it's controlled by global and/or specific classes
java.util.logging.SocketHandler.level      = ALL
java.util.logging.SocketHandler.encoding   = UTF-8
java.util.logging.SocketHandler.host       =
java.util.logging.SocketHandler.port       =

# See https://docs.oracle.com/javase/8/docs/api/java/util/logging/MemoryHandler.html
# Do NOT limit the messages here, it's controlled by global and/or specific classes
java.util.logging.MemoryHandler.level      = ALL
java.util.logging.MemoryHandler.size       =
java.util.logging.MemoryHandler.push       =
java.util.logging.MemoryHandler.target     =

# Customize the SimpleFormatter output format
# See https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax 
java.util.logging.SimpleFormatter.format   = %1$tF'T'%1$tT.%1$tL%1$tz [%4$s] %2$s - %5$s %6$s%n
#return String.format(format,                                    // java.util.logging.SimpleFormatter.format
#                     dat,                                       // %1$t<conversion>
#                     source,                                    // %2$s    
#                     record.getLoggerName(),                    // %3$s
#                     record.getLevel().getLocalizedLevelName(), // %4$s
#                     message,                                   // %5$s
#                     throwable);                                // %6$s

# Specific logging level for classes or packages
se.magnuskkarlsson.examples.lambda.level = FINEST

Log Levels

Java Logging API has the following levels: OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL

You define a global log level with .level, then if you need specific configuration you add: <package>[.class].level=<level>

NOTE: ConsoleHandler is special, for that you need to set ConsoleHandler.level for others you don't!

Log Formatter

There exists only two formatter and you typically want to use the SimpleFormatter.

NOTE: Most handlers uses the SimpleFormatter as default, but some do not. For them you need to set the formatter configuration.

Configure java.util.logging.SimpleFormatter

The default format is quite odd, so you want to change that.

The configuration is done with java.util.logging.SimpleFormatter.format. And the value is for the java call in java.util.logging.SimpleFormatter.format(LogRecord)

return String.format(format,                                    // java.util.logging.SimpleFormatter.format
                     dat,                                       // %1$t<conversion>
                     source,                                    // %2$s    
                     record.getLoggerName(),                    // %3$s
                     record.getLevel().getLocalizedLevelName(), // %4$s
                     message,                                   // %5$s
                     throwable);                                // %6$s

To understand the java.lang.String.format(String, Object...) syntax, read https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax.

No comments: