Logging is a powerful tool for understanding and debugging program’s run-time behavior. Logging is the process of writing log messages during the execution of a program to a central place. This logging allows you to report and persist error and warning messages as well as info messages (e.g., runtime statistics) so that the messages can later be retrieved and analyzed.
In this java logging tutorial, we will learn basic features of Java Logger. We will also look into Java Logger example of different logging levels, Logging Handlers, Formatters, Filters, Log Manager and logging configurations. Here we are used the most popular java logging frameworks, Log4j 2 and Logback, along with their predecessor Log4j, and briefly touches upon SLF4J, a logging facade that provides a common interface for different logging frameworks.
Related Tutorials
1. Overview
Logging is broken into three major pieces: the Logger, Formatter and the Handler (Appender). The Logger is responsible for capturing the message to be logged along with certain metadata and passing it to the logging framework. After receiving the message, the framework calls the Formatter with the message. The Formatter formats it for output. The framework then hands the formatted message to the appropriate Appender for disposition. This might include a console display, writing to disk, appending to a database, or email.
Simpler logging frameworks, like Java Logging Framework by the Object Guy, combine the logger and the appender. This simplifies default operation, but it is less configurable, especially if the project is moved across environments.
1.1 Logger: A Logger is an object that allows the application to log without regard to where the output is sent/stored. The application logs a message by passing an object or an object and an exception with an optional severity level to the logger object under a given a name/identifier.
1.1.1 Logger Name: A logger has a name. The name is usually structured hierarchically, with periods (.) separating the levels. Both log4j and the Java logging API support defining Handlers higher up the hierarchy.
- com
- com.doj
- com.doj.app
- com.doj.app.LoggingExample
1.1.2 Severity level:
- FATAL: Severe errors that cause premature termination. Expect these to be immediately visible on a status console.
- ERROR: Other runtime errors or unexpected conditions. Expect these to be immediately visible on a status console.
- WARNING: Warnings in the application.
- INFO: Interesting runtime events (startup/shutdown). Expect these to be immediately visible on a console, so be conservative and keep to a minimum.
- DEBUG: detailed information on the flow through the system. Expect these to be written to logs only.
- TRACE: more detailed information. Expect these to be written to logs only.
The logging framework maintains the current logging level for each logger. The logging level can be set more or less restrictive. For example, if the logging level is set to “WARNING”, then all messages of that level or higher are logged, ERROR and FATAL.
1.2 Formatters or renderers: A Formatter is an object that formats a given object.
Java comes with two built-in Formatter’s (subclasses of Formatter):
- SimpleFormatter: This formatter generates text messages with basic information. ConsoleHandler uses this formatter class to print log messages to console.
- XMLFormatter: This formatter generates XML message for the log, FileHandler uses XMLFormatter as a default formatter.
public class MyFormatter extends Formatter {
@Override
public String format(LogRecord record) {
return record.getLevel() + ":" + record.getMessage();
}
}
The subclass must override the abstract format() method in the Formatter class. The String returned by the format() is what is forwarded to the external system by the Handler. Exactly how the string should be formatted is up to you.
1.3 Appenders or handlers: Appenders listen for messages at or above a specified minimum severity level. The Appender takes the message it is passed and posts it appropriately.You can add one or more Handler’s to a Logger. When messages are logged via the Logger, the messages are eventually forwarded to the Handler’s, if not rejected by a Filter or the minimum log level of the Logger.
logger.addHandler(new ConsoleHandler());
1.3.1 Built-in Handlers
You can create your own Handler if you want, but Java comes with 4 built-in Handler’s already:
- ConsoleHandler
- FileHandler
- StreamHandler
- SocketHandler
- MemoryHandler
You will most often use the FileHandler, but each of these built-in Handler’s are explained briefly in the following sections.
public class MyHandler extends StreamHandler {
@Override
public void publish(LogRecord record) {
super.publish(record);
}
@Override
public void flush() {
super.flush();
}
@Override
public void close() throws SecurityException {
super.close();
}
}
2. Enabling Logging: Enabling logging inside the project follows three common steps:
- Adding needed libraries
- Configuration
- Placing log statements
3. Log4j 2: Log4j 2 is new and improved version of the Log4j logging framework. The most compelling improvement is the possibility of asynchronous logging. Required Maven dependencies for it.
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.6.2</version>
</dependency>
3.1 Configuration for Log4j 2: The following is required configuration for log4j.
<Configuration status="debug" name="doj" packages="">
<Appenders>
<Console name="stdout" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %p %m%n"/>
</Console>
</Appenders>
</Configuration>
Configuring Log4j 2 is based on the main configuration log4j2.xml file. The first thing to configure is the appender.
These determine where the log message will be routed. Destination can be a console, a file, socket, etc.
Notice the PatternLayout element – this determines how message should look like.
Here %d determines date pattern,
%p – output of log level,
%m – output of logged message
%n – adds new line symbol.
3.2 to enable an appender
<Root level="error">
<AppenderRef ref="STDOUT"/>
</Root>
3.3 Logging to File:
<Appenders>
<File name="fout" fileName="doj.log" append="true">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %m%nw</Pattern>
</PatternLayout>
</File>
</Appenders>
The File appender have several parameters that can be configured:
- file – determines file name of the log file
- append – The default value for this param is true, meaning that by default a File appender will append to an existing file and not truncate it.
PatternLayout that was described in previous example.
3.4 to enable File appender:
<Root level="error">
<AppenderRef ref="STDOUT"/>
<AppenderRef ref="fout"/>
</Root>
4. Asynchronous Logging: If you want to make your Log4j 2 asynchronous you need to add dependency LMAX disruptor library to your pom.xml. LMAX disruptor is a lock-free inter-thread communication library.
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.5</version>
</dependency>
If you want to use LMAX disruptor you need to use <asyncRoot> instead of <Root> in your configuration.
5. Example:
The following is a simple example that demonstrates the use of Log4j for logging:
/**
*
*/
package com.doj.app;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* @author Dinesh.Rajput
*
*/
public class Log4jExample {
private static Logger logger = LogManager.getLogger(Log4jExample.class);
public static void main(String[] args) {
logger.debug("Debug log message");
logger.info("Info log message");
logger.error("Error log message");
}
}
Package Level Configuration: Let’s say you need to show messages with the log level TRACE – for example from a specific package such as com.doj.app:
logger.trace("Trace log message");
To enable logging only for one of packages you need to add the following section before <Root> to your log4j.xml:
<Logger name="com.doj.app" level="debug">
<AppenderRef ref="stdout"/>
</Logger>
6. Logback: Logback is meant to be an improved version of Log4j, developed by the same developer who made Log4j.
Let’s start by adding the following dependency to the pom.xml:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
Let’s now have a look at a Logback configuration example:
<configuration>
# Console appender
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
# Pattern of log message for console appender
<Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n</Pattern>
</layout>
</appender>
# File appender
<appender name="fout" class="ch.qos.logback.core.FileAppender">
<file>doj.log</file>
<append>false</append>
<encoder>
# Pattern of log message for file appender
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n</pattern>
</encoder>
</appender>
# Override log level for specified package
<logger name="com.doj.app" level="TRACE"/>
<root level="INFO">
<appender-ref ref="stdout" />
<appender-ref ref="fout" />
</root>
</configuration>
Logback uses SLF4J as an interface, so you need to import SLF4J’s Logger and LoggerFactory.
7. SLF4J: SLF4J provides a common interface and abstraction for most of the Java logging frameworks. It acts as a facade and provides standardized API for accessing the underlying features of the logging framework.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Log4jExample {
private static Logger logger = LoggerFactory.getLogger(Log4jExample.class);
public static void main(String[] args) {
logger.debug("Debug log message");
logger.info("Info log message");
logger.error("Error log message");
}
}
8. Log4J:
First of all you need to add Log4j library to your projects pom.xml:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
Lets take a look at a complete example of simple Log4j configuration with only one console appender:
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd" >
<log4j:configuration debug="false">
<!--Console appender-->
<appender name="stdout" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %p %m%n"/>
</layout>
</appender>
<!-- File appender-->
<appender name="fout" class="org.apache.log4j.FileAppender">
<param name="file" value="log4j/target/doj-log4j.log"/>
<param name="append" value="false"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n"/>
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %p %m%n"/>
</layout>
</appender>
<!--Override log level for specified package-->
<category name="com.doj.app.log4j">
<priority value="TRACE"/>
</category>
<root>
<level value="DEBUG"/>
<appender-ref ref="stdout"/>
<appender-ref ref="fout"/>
</root>
</log4j:configuration>
<log4j:configuration debug=”false”> is open tag of whole configuration which has one property – debug. It determines whether you want to add Log4j debug information to logs.
Lets take a look at a simple example:
/**
*
*/
package com.doj.app.log4j;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* @author Dinesh.Rajput
*
*/
public class Log4jExample {
private static Logger logger = LogManager.getLogger(Log4jExample.class);
public static void main(String[] args) {
logger.trace("Trace log message");
logger.debug("Debug log message");
logger.info("Info log message");
logger.error("Error log message");
}
}
Summary
This tutorial shows very simple examples of how you can use different logging framework such as Log4j, Log4j2 and Logback. It covers simple configuration examples for all of the mentioned frameworks.
Whole Code for this example:
Happy Learning!!!