Java Logging with MDC
In this article, we will show you how to use MDC logging in java using detailed examples and suggestions.
1. Introduction
Most of the current real-world applications have more than one client. These clients talk to the servers simultaneously. In a typical scenario on the server we will have different threads dealing with these separate clients. One way to separate the logs of one client from another is to instantiate the logger for every client. We can clearly see that this is not a scalable solution as it will make the code ugly, will affect performance, and is a big management overhead.
Another more efficient way is to uniquely stamp each log request serving the given client.
2. MDC class
This class hides and serves as a substitute for the underlying logging system’s MDC implementation. If the underlying logging system offers MDC functionality, then SLF4J’s MDC
, i.e. this class, will delegate to the underlying system’s MDC. Note that at this time, only two logging systems, namely log4j and logback, offer MDC functionality. For java.util.logging
which does not support MDC, BasicMDCAdapter
will be used. For other systems, i.e. slf4j-simple
and slf4j-nop
, NOPMDCAdapter
will be used.
Thus, as a SLF4J user, you can take advantage of MDC in the presence of log4j, logback, or java.util.logging, but without forcing these systems as dependencies upon your users. The MDC
class contains only static methods. It lets the developer place information in a diagnostic context that can be subsequently retrieved by certain logback components. The MDC
manages contextual information on a per thread basis. Typically, while starting to service a new client request, the developer will insert pertinent contextual information, such as the client id, client’s IP address, request parameters etc. into the MDC
. Logback components, if appropriately configured, will automatically include this information in each log entry.
3. Example
In this section we will see a working example of using MDC for logging. We will create a simple java maven project. Use your favorite IDE – I am using IntelliJ IDEA. Open a new Project
Choose project name and location
IntelliJ will create a sample maven project for you with a directory structure similar to below:
Now first let us add the dependencies in our pom file. We will need logback and slf4j:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>JCG-logging-with-mdc</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.23</version>
</dependency>
</dependencies>
</project>
Now add a logback.xml
file in the resources folder so that it is available in the classpath:
logback.xml
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%X{project} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
Make note of <pattern>
we are using here. We are using a placeholder {project}
, this will be populated via MDC
.
Now let us create a very simple main class:
LogbackMdcExample.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
public class LogbackMdcExample {
private static final Logger LOG = LoggerFactory.getLogger(LogbackMdcExample.class);
public static void main(String[] args) {
MDC.put("project", "Java Code Geeks");
LOG.info("Test logging");
}
}
The first line in the main method puts a diagnostic context value (second parameter) as identified with the key (first) parameter into the current thread’s diagnostic context map. The key parameter cannot be null. The val (second) parameter can be null only if the underlying implementation supports it. This method delegates all work to the MDC of the underlying logging system – in this case logback.
You can place as many value/key associations in the MDC
as you wish. Multiple insertions with the same key will overwrite older values.
Let’s run this class and see the results. You will see in the console something similar to as below:
Java Code Geeks - Test logging
The part before the hyphen is being populated via MDC.
4. Summary
Mapped Diagnostic Contexts shine brightest within client-server architectures. Typically, multiple clients will be served by multiple threads on the server. Although the methods in the MDC
class are static, the diagnostic context is managed on a per thread basis, allowing each server thread to bear a distinct MDC
stamp. MDC
operations such as put()
and get()
affect only the MDC
of the current thread, and the children of the current thread. The MDC
in other threads remain unaffected. Given that MDC
information is managed on a per thread basis, each thread will have its own copy of the MDC
. Thus, there is no need for the developer to worry about thread-safety or synchronization when programming with the MDC
because it handles these issues safely and transparently.
5. Download
This was an example of using MDC for logging in Java application.
You can download the full source code of this example here: Java Logging with MDC