Skip to content

Creating Log Messages

Estimated time to read: 5 minutes

Since Java 1.4, Java has had a logging library built in. It is located in the java.util.logging package.

Log4J is an alternative logging library that may be used in real wold applications. For more info, see Log4J

Schematic Design

Something in the code triggers a log event

An instance of the Logger class stars processing the log event, it can optionally filter the information first.

Data from the Logger instance is stored in a LogRecord instance.

The data within the LogRecord instance is then passed to a Handler. Optionally, the Handler can have additional rules for filtering and formatting.

The Handler then passes the data to the Log File, where the data is recorded and stored.

A LogManager influences the entire process.

Creating a Logger

The follow code example shows how a logger is created and added to a project

package io.entityfour.logmodule;

import java.util.logging.Logger;

public class LogExample {

    private static final Logger LOGGER = Logger.getLogger(LogExample.class.getName()); // Instantiating the logger here, whilst setting its accessibility to private and final, is a better approach. Note that the instance name is now LOGGER as it is a constant / final.

    public static void main(String[] args) {
        // Logger.getLogger(); // This will set up a basic logger, however, it is common to give the logger a name!

        // Logger.getLogger(LogExample.class.getName()); // Convention is that the logger name is the same as the class name. You can use the <classname>.class.getName() method to do this for you.

        //Logger logger = Logger.getLogger(LogExample.class.getName()); // Instantiate the logger. This can be done here in the method, however, it is better for this to be encapsulated and include above the class. This way, a private modifier can be used so that the logger is exclusive to this class.

        // logger.log(Level.INFO, "This is an example INFO level log)"); // The log() method takes a log level, followed by a message as it arguments. It will log to the console by default!

        LOGGER.log(Level.INFO, "This is a new INFO level log message"); // As the logger has now been created as a constant / final, it can be called by its new instance name.
    }
}

Log Levels

Log levels are used to distinguish between different levels of severity of a situation.

There are seven levels in the Java logging API.

These levels get translated to a number via an Enum, the higher the number; the more severe the situation.

In descending order of severity, these levels are:

  1. SEVERE
  2. WARNING
  3. INFO
  4. CONFIG
  5. FINE
  6. FINER
  7. FINEST

By default, a Logger and Handler are created with INFO as their default level. Levels below this, such as CONFIG, FINE, etc will not be output.

Note

If the Logger instance is set to a non-default level, such as FINE, if the Handler isn't set up with a non-default level of the same or lower value, the Handler will not report the line.

Logger Level Implementation

package io.entityfour.logmodule;

import java.util.logging.Logger;

public class LogExample {

    private static final Logger LOGGER = Logger.getLogger(LogExample.class.getName());

    public static void main(String[] args) {

        LOGGER.setLevel(Level.FINEST); // Set the Logger's level to FINEST
        LOGGER.log(Level.FINEST, "This is a mighty fine message"); // This will not print! Despite the Logger's level being set above, the Handler's level has not been set!
    }
}

The example above will not print anything out to the console.

When using Logger, the default handler is set as the output console which has its default level set to INFO.

The FINEST log level message did not print as it was filtered out by the Handler's level configuration.

Log Handlers

A Handler exists to direct messages to the log location such as the command line, a log file or an external service. However, messages will only be passed to a log location if Handlers filter configuration allows it to. Handlers also format the message using a formatter.

There are many types of handlers, such as:

  • ConsoleHandler (Default)
  • FileHandler
  • StreamHandler
  • SocketHandler
  • MemoryHandler

Console Handler Implementation

package io.entityfour.logmodule;

import java.util.logging.Logger;

public class LogExample {

    private static final Logger LOGGER = Logger.getLogger(LogExample.class.getName());

    static { // This is known as an 'Initializer Block'. Cod e that is declared here will run each time an instance of the object (Class) is created.
        LOGGER.setLevel(Level.FINEST);
        ConsoleHandler consoleHandler = new ConsoleHandler(); // Instantiate a ConsoleHandler
        consoleHandler.setLevel(Level.FINEST); // Set the Handlers level to FINEST
        LOGGER.addHandler(consoleHandler); // Assign the handler to the logger
    }

    public static void main(String[] args) {
        LOGGER.log(Level.FINEST, "This is a mighty fine message"); 
    }
}

File Handler Implementation

package io.entityfour.logmodule;

import java.util.logging.Logger;

public class LogExample {

    private static final Logger LOGGER = Logger.getLogger(LogExample.class.getName());

    static {
        FileHandler fileHandler = null; // Create a FileHandler object with a null value
        try { // Wrap the fileHandler instantiation with try/catch to catch any exceptions
            fileHandler = = new FileHandler(<className>.class.getSimpleName() + ".log");
        } catch (IOException e){
            e.printStackTrace();
        }
        LOGGER.addHandler(fileHandler); // Assign the handler to the logger
    }

    public static void main(String[] args) {
        LOGGER.log(Level.FINEST, "This is a mighty fine message"); 
    }
}

Logging Methods

There are three log methods, with each offering many overloaded constructors for logger creation.

Standard Log
LOGGER.log(Level.<LOGLEVEL>, <STR_MESSAGE>);
Precise Log
LOGGER.logp(Level.<LOGLEVEL>, <CLASSNAME>.class.getName(), <STR_METHODNAME>, <STR_MESSAGE>);
Resource Bundle Log (Localization)
LOGGER.logrb(Level.<INFO>, ResourceBundle.getBundle("en_US"), <STR_MESSAGE>);

Best Practices

Be Precise

The log entry should answer who, when, where, what and the result. For example, User Logged In is not as verbose as User: jSmith, ID:12, Login:success, Date:03/04/2022, Time:11:25:43

No sensitive data

Logs are often stored on external platforms or systems. Logs may be accessed for many purposes by many people. As a result, ensure that sensitive data is not stored in the log file.

Correct log level

It is important to use the correct log level to report a message to the log.

Machine and Human readable

Logs should be Machine and Human readable. This allows the log to be processed via automated processes whilst also allowing someone to read the log naturally. Formatting is key.

Don't log too much

Logging too much can reduce the performance of the application and also fill up the log file with unnecessary detail.

Don't log too little

Conversely, don't log too little!