Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Java Logging Frameworks

Tech 1

Java offers numerous third - party logging frameworks. Following modern design concepts, logging frameworks are typically divided into the Facade part and the Implementation part. The Facade provides abstract API specifications, while the Implementation is responsible for implementing the API to complete specific logging functions. Developers use the APIs provided by the facade when using logging frameworks and can flexibly choose different API implementations according to actual situations.

1. java.util.logging

1.1 Overview

java.util.logging is a logging framework that comes with the Java standard library. No third - party dependencies need to be introduced when using it. Its functions are relatively simple, and it is generally not used in actual projects.

package com.demo;
import java.util.logging.Logger;

class LoggingDemoAppTests {
    private static final Logger appLogger = Logger.getLogger(LoggingDemoAppTests.class.getName());

    public static void main(String[] args) {
        appLogger.info("Logging test message.");
    }

1.2 Configuration

The configuration file of java.util.logging is a .properties file. The default path is $JAVA_HOME/lib/logging.properties. A custom configuration file can be specified through JVM parameters or code.

To specify via JVM parameter -Djava.util.logging.config.file:

java -Djava.util.logging.config.file=path/logging.properties mainApp.jar

To specify via code:

package org.example;
import java.io.InputStream;
import java.util.logging.LogManager;
import java.util.logging.Logger;

public class JavaLoggingTest {
    public static void main(String[] args) {
        try (InputStream input = JavaLoggingTest.class.getResourceAsStream("config/logging.properties")) {
            LogManager.getLogManager().readConfiguration(input);
        } catch (Exception e) {
            e.printStackTrace();
        }

        Logger testLogger = Logger.getLogger(JavaLoggingTest.class.getName());
        testLogger.info("This is an info - level message.");
    }
}

Configuration file content:

# Global log level
.level = INFO

# Log level of the console handler
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

# Log level of the file handler
java.util.logging.FileHandler.level = INFO
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.SimpleFormatter

# Log level of the custom logger
com.example.JavaLoggingTest.level = FINE

2. commons - logging (JCL)

2.1 Overview

commons - logging (JCL) provides a lightweight Log interface that is independent of other logging frameworks. It provides developers with a simple logging abstraction, allowing developers to choose specific logging implementations like using plug - ins.

That is to say, commons - logging is a logging facade and does not provide specific logging implementations. commons - logging provides implementations of the Log interface that wrap other logging APIs and back - ends, including Log4j2.x, Slf4j, java.lang.logging. In addition, it also has implementations of some old - fashioned logging frameworks such as Log4j1.x and Avalon LogKit, but they are disabled by default.

2.2 Configuration

commons - logging mainly has two abstract classes: org.apache.commons.logging.Log and org.apache.commons.logging.LogFactory. Log provides a unified logging interface, and LogFactory, as a factory class, provides methods to create Log. When developers use it, they can manually specify a specific implementation of Log (such as the wrapper implementations of other logging APIs provided by commons - logging mentioned above or implement a Log by themselves). LogFactory, as a factory interface for creating Log, generally we do not need to specify it manually unless we need to use other advanced functions.

JCL provides three types of LogFactory implementations: Log4jApiLogFactory, Slf4jLogFactory, and LogFactoryImpl. By default, JCL will select a LogFactory in the following way:

  • If the Log4j API is included in the Java classpath and the developer has not manually redirected the implementation of the Log4j API to SLF4j, then Log4jApiLogFactory will be used, and the logging calls of JCL will be handed over to the Log4j API for implementation.
  • If SLF4j exists in the classpath, then Slf4jLogFactory will be used, and the logging calls of JCL will be handed over to SLF4j for implementation.
  • If neither exists, JCL will use LogFactoryImpl.

For LogFactoryImpl, it searches for the implementation of Log in the following order until it finds the first available Log and then terminates the search process:

  • Check whether the configuration property org.apache.commons.logging.Log has a value. Developers can set the value of org.apache.commons.logging.Log in Java code, or define a configuration file named commons - logging.properties under the Java classpath and set the value of commons - logging.properties in the configuration file. The configuration in the commons - logging.properties file will be used as the property of LogFactory. If there are multiple configuration files under the classpath, a priority attribute can be specified in the configuration file to indicate the priority. For two files with the same priority, the first one found will be used.
  • Check the value of the system configuraton org.apache.commons.logging.Log. If this system property exists, the value specified by this property will be used as the implementation of Log.
  • Check whether java.logging is available. If available, the Jdk14Logger class will be used as the implementation of Log.
  • If no specific implementation of Log is found in the previous steps, the default implementation class SimpleLog will be used as the implementation of Log.

3. Log4j 1.x

Log4j 1.x is a popular Java logging framework developed by the Apache Software Foundation. It was widely used in early Java projects. It provides powerful logging functions and supports flexible configuration and multiple logging output methods. Log4j 1.x has stopped maintenance and has been replaced by Log4j2.x. We do not need to know too many details about it.

We will focus on how to deal with the logging output of some old projects and third - party libraries that depend on Log4j1.x. If we have an old project project - log4j1 that uses Log4j1.x as its logging framework, and now we have a new project that needs to depend on project - log4j1, but our new project uses SLF4j as the logging framework. How should we deal with the logging output of project - log4j? For this, SLF4j provides an adapter log4j - over - slf4j to redirect the logging operations that call Log4j1.x to the logging output of SLF4j. We only need to introduce the corresponding dependencies in the project and provide the implementation and configuration of SLF4j.

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>log4j - over - slf4j</artifactId>
    <version>2.0.17</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j - api</artifactId>
    <version>2.0.17</version>
</dependency>

Of course, SLF4j, as a logging facade, does not provide specific logging output implementations. As for which framework to use as the specific implementation of SLF4j has no impact on log4j - over - slf4j.

In addition to SLF4j, apache also provides a corresponding library log4j - 1.2 - api to redirect calls to Log4j1.x to the implementation of Log4j2.x. Just introduce the dependencies.

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j - 1.2 - api</artifactId>
    <version>2.24.3</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j - api</artifactId>
    <version>2.24.3</version>
</dependency>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j - core</artifactId>
    <version>2.24.3</version>
</dependency>

If you use Log4j2.x, you need to provide a configuration file in the format of Log4j2.x.

4. Log4j 2.x

4.1 Overview

Log4j2.x is an upgraded version of Apache Log4j1.x. It is a high - performance logging framework that supports asynchronous logging. It solves many problems of Log4j 1.x and introduces many new features. Log4j2.x adopts a design methodd that separates the interface from the implementation, thus improving flexibility and scalability. Among them, log4j - api provides abstract APIs, and log4j - core provides corresponding implementations. When developers use it, they can only introduce log4j - api and use other logging frameworks such as SLF4j + Logback as specific implementations.

Through log4j - to - slf4j, the calls to log4j - api can be redirected to SLF4j, so as to realize the requirement of using log4j - api as the facade and SLF4j + Logback as the specific logging implementation.

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j - to - slf4j</artifactId>
    <version>2.x.x</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j - api</artifactId>
    <version>1.7.36</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback - classic</artifactId>
    <version>1.2.11</version>
</dependency>

At this time, the logging configuration can be carried out according to the format of Logback.

4.2 Configuration

Log4j2.x provides configuration files in multiple formats. By default, Log4j2.x will scan the classpath to find the configuration file in the following name order:

  1. log4j2 - test<contextName>.<extension>
  2. log4j2 - test.<extension>
  3. log4j2<contextName>.<extension>
  4. log4j2.<extension>

Among them, contextName represents context information. Generally, a configuration file named log4j.<extension> can meet the requirements. extension represents the file type, such as log4j2.properties

Log4j2.x's configuration files support xml, json, yaml, and Properties formats. If multiple types of configuration files exist at the same time, the search will be carried out according to the following priority:

  1. XML
  2. JSON
  3. YAML
  4. Properties

To use a JSON - formatted configuration file, the jackson - databind dependency needs to be added. To use a YAML - formatted configuration file, the jackson - dataformat - yaml dependency needs to be added.

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson - databind</artifactId>
    <version>2.18.0</version>
    <scope>runtime</scope>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson - dataformat - yaml</artifactId>
    <version>2.18.0</version>
    <scope>runtime</scope>
</dependency>

XML - type configuration file log4j2.xml

<?xml version="1.0" encoding="UTF - 8"?>
<Configuration xmlns="https://logging.apache.org/xml/ns"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema - instance"
               xsi:schemaLocation="
                   https://logging.apache.org/xml/ns
                   https://logging.apache.org/xml/ns/log4j - config - 2.xsd">
    <Appenders>
        <Console name="CONSOLE">
            <PatternLayout pattern="%p - %m%n"/>
        </Console>
        <File name="MAIN" fileName="logs/main.log">
            <JsonTemplateLayout/>
        </File>
        <File name="DEBUG_LOG" fileName="logs/debug.log">
            <PatternLayout pattern="%d [%t] %p %c - %m%n"/>
        </File>
    </Appenders>
    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="CONSOLE" level="WARN"/>
            <AppenderRef ref="MAIN"/>
        </Root>
        <Logger name="org.example" level="DEBUG">
            <AppenderRef ref="DEBUG_LOG"/>
        </Logger>
    </Loggers>
</Configuration>

JSON - formatted configuration file log4j2.json

{
    "Configuration": {
        "Appenders": {
            "Console": {
                "name": "CONSOLE",
                "PatternLayout": {
                    "pattern": "%p - %m%n"
                }
            },
            "File": [
                {
                    "name": "MAIN",
                    "fileName": "logs/main.log",
                    "JsonTemplateLayout": {}
                },
                {
                    "name": "DEBUG_LOG",
                    "fileName": "logs/debug.log",
                    "PatternLayout": {
                        "pattern": "%d [%t] %p %c - %m%n"
                    }
                }
            ]
        },
        "Loggers": {
            "Root": {
                "level": "INFO",
                "AppenderRef": [
                    {
                        "ref": "CONSOLE",
                        "level": "WARN"
                    },
                    {
                        "ref": "MAIN"
                    }
                ]
            },
            "Logger": {
                "name": "org.example",
                "level": "DEBUG",
                "AppenderRef": {
                    "ref": "DEBUG_LOG"
                }
            }
        }
    }
}

YAML - formatted configuration file log4j2.yaml

Configuration:
    Appenders:
        Console:
            name: "CONSOLE"
            PatternLayout:
                pattern: "%p - %m%n"
        File:
            - name: "MAIN"
              fileName: "logs/main.log"
              JsonTemplateLayout: {}
            - name: "DEBUG_LOG"
              fileName: "logs/debug.log"
              PatternLayout:
                  pattern: "%d [%t] %p %c - %m%n"
    Loggers:
        Root:
            level: "INFO"
            AppenderRef:
                - ref: "CONSOLE"
                  level: "WARN"
                - ref: "MAIN"
        Logger:
            name: "org.example"
            level: "DEBUG"
            AppenderRef:
                ref: "DEBUG_LOG"

Properties - formatted configuration file log4j2.properties

appender.0.type = Console
appender.0.name = CONSOLE
appender.0.layout.type = PatternLayout
appender.0.layout.pattern = %p - %m%n

appender.1.type = File
appender.1.name = MAIN
appender.1.fileName = logs/main.log
appender.1.layout.type = JsonTemplateLayout

appender.2.type = File
appender.2.name = DEBUG_LOG
appender.2.fileName = logs/debug.log
appender.2.layout.type = PatternLayout
appender.2.layout.pattern = %d [%t] %p %c - %m%n

rootLogger.level = INFO
rootLogger.appenderRef.0.ref = CONSOLE
rootLogger.appenderRef.0.level = WARN
rootLogger.appenderRef.1.ref = MAIN

logger.0.name = org.example
logger.0.level = DEBUG
logger.0.appenderRef.0.ref = DEBUG_LOG

5. Slf4j

5.1 Overview

Slf4j is a logging facade (Facade) that only provides logging interface abstraction. To use Slf4j, a specific logging implementation needs to be introduced. Generally, Logback is used as the implementation of Slf4j because Logback is the standard implementation of the SLF4j API.

To use SLF4j + Logback as the logging framework, the following dependencies need to be introduced:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j - api</artifactId>
    <version>2.0.17</version>
</dependency>

<!-- JAVAX EE -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback - classic</artifactId>
    <version>1.3.14</version>
</dependency>

<!-- Jakarta EE -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback - classic</artifactId>
    <version>1.5.15</version>
</dependency>

Logback provides the logback - core module to implement basic logging functions. logback - classic implements the SLF4j API on the basis of logback - core. When used with SLF4j, generally we only need to introduce logback - classic.

In addition to Logback, developers can also use other logging frameworks as the implementation of SLF4j, such as Log4j2.x. Apache provides log4j - slf4j2 - impl to redirect the API calls of SLF4j to Log4j2.x.

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j - slf4j2 - impl</artifactId>
    <version>2.24.3</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j - api</artifactId>
    <version>2.x.x</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j - core</artifactId>
    <version>2.x.x</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j - api</artifactId>
    <version>1.7.36</version>
</dependency>

After bridging SLF4j to Log4j2.x, the configuration file will use the corresponding Log4j2.x configuration file.

It is also possible to use slf4j - log4j12 to redirect SLF4j to Log4j1.x and use slf4j - jdk14 to redirect SLF4j to java.util.logging. However, this is not usually done. The most commonly used combination in projects now is SLF4j + Logback.

5.2 Configuration

LogBack uses XML as the format of the configuration file. By default, Logback will search for configuration files named logback - test.xml or logback.xml in the classpath in order.

logback.xml

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender - ref ref="STDOUT" />
    </root>
</configuration>

6. Other Frameworks

In addition to the above - mentioned commonly used logging frameworks, there are many other logging frameworks in Java. Here, we only introduce a few that are relatively common in actual projects.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.