Log4J 2 Configuration: Using JSON

Log4J 2 Configuration: Using JSON

3 Comments

Log4J 2 introduces configuration support through JSON and YAML in addition to properties file and XML. If you are new to Log4J 2, I suggest reading my introductory post on Log4J 2, Introducing Log4J 2 – Enterprise Class Logging. For Log4J 2 configuration using properties file, read my post Log4J 2 Configuration: Using Properties File, and for using XML, Log4J 2 Configuration: Using XML.

In this post, I discuss how to configure Log4J 2 using JSON.

Maven Dependencies for JSON Configuration

To use Log4J2, you need to add the required Log4J 2 dependencies in your Maven POM, as described here. For JSON configuration, you also need Jackson, a suite of data-processing tools for Java. The JSON support for Log4J 2 uses three Jackson packages: Jackson core, Jackson databind, and Jackson annotations.

The following code snippet shows the Maven POM dependencies required to use JSON for configuring Log4J 2.

. . .
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter</artifactId>
   <exclusions>
      <exclusion>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-logging</artifactId>
      </exclusion>
   </exclusions>
</dependency>
<dependency>
   <groupId>org.apache.logging.log4j</groupId>
   <artifactId>log4j-api</artifactId>
   <version>2.5</version>
</dependency>
<dependency>
   <groupId>org.apache.logging.log4j</groupId>
   <artifactId>log4j-core</artifactId>
   <version>2.5</version>
</dependency>
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-core</artifactId>
   <version>2.6.3</version>
</dependency>
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.6.3</version>
</dependency>
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-annotations</artifactId>
   <version>2.6.3</version>
</dependency>
. . .

Creating a Logger

Before we start configuring Log4J 2 using JSON, lets create a logger class that uses the Log4J 2 API to log messages.

Log4J2JsonConf.java

package guru.springframework.blog.log4j2json;


import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4J2JsonConf {
    private static Logger logger = LogManager.getLogger();

    public void performSomeTask(){
        logger.debug("This is a debug message");
        logger.info("This is an info message");
        logger.warn("This is a warn message");
        logger.error("This is an error message");
        logger.fatal("This is a fatal message");
    }
}

We will use JUnit to test the preceding class.

Log4J2JsonConfTest.java

package guru.springframework.blog.log4j2json;

import org.junit.Test;
import static org.junit.Assert.*;

public class Log4J2JsonConfTest {
    @Test
    public void testPerformSomeTask() throws Exception {
        Log4J2JsonConf log4J2JsonConf=new Log4J2JsonConf();
        log4J2JsonConf.performSomeTask();

    }
}

Configuring Console and File Appenders Using JSON

In order to configure Log4J 2 using JSON, you need a JSON configuration file, named either log4j2.json or log4j2.jsn in the project classpath.

The following figure shows the skeleton of a JSON configuration file in the IntelliJ editor.
Skeleton of log4j2.json json configuration for Log4j 2

As shown in the preceding figure, a log4j2.json file is composed of nested JSON objects. At the top is the configuration object that contains the following objects:

  • properties: Defines one or more properties as a JSON array of name-value pairs. The properties can be referred by their names from the different parts of the configuration file.
  • appenders: Configures one or more appenders, such as Console, File, and RollingFile.
  • Loggers: Configures the root logger represented by root along with zero or more application-specific loggers, each represented by logger.

We will configure two appenders to write log messages to the console and a file. We’ll also configure an application-specific logger along with the root logger to use the appenders, like this:

{
  "configuration": {
    "name": "Default",
    "properties": {
      "property": [
        {
          "name": "log-path",
          "value": "logs"
        },
        {
          "name": "archive",
          "value": "${log-path}/archive"
        }
      ]
    },
    "appenders": {
      "Console": {
        "name": "Console-Appender",
        "target": "SYSTEM_OUT",
        "PatternLayout": {
          "pattern": "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"
        }
      },
      "File": {
        "name": "File-Appender",
        "fileName": "${log-path}/logfile.log",
        "PatternLayout": {
          "pattern": "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"
        }
      }
    },
    "loggers": {
      "logger": {
        "name": "guru.springframework.blog.log4j2json",
        "level": "debug",
        "appender-ref": [{"ref": "File-Appender", "level":"error"}]
      },
      "root": {
        "level": "debug",
        "appender-ref": {"ref": "Console-Appender"}
      }
    }
  }
}

In the configuration code above:

  • Line 4 – Line 15: We declared two properties as name-value pairs in the property JSON array.
  • Line 16 – Line 31: We configured the Console and File appenders.
  • Line 32 – Line 43: We configured an application-specific logger for all the logger classes of the guru.springframework.blog.log4j2jsonpackage. This logger writes error and higher level log messages to the file appender. We also configured the root logger to log debug and higher level messages to the console appender.

If we run the Log4J2JsonConfTest test class, Log4J 2 will generate log messages and send them to both the console and a file, as shown in this figure.

Log4J 2 Console and File Output

Configuring a Rolling File Appender via JSON

If you use the file appender for writing logs, the size of log file will grow with time. This can have significant consequences in enterprise applications that typically have very large code bases containing a significant amount of logging code. A long running application can easily produce millions and millions of lines of logging information which can cause the files to become very large. Imagine scanning through a log file with 10s of thousands of lines of log statements to find a specific statement. In order to avoid that situation, you should use the rolling file appender.

A rolling file appender supports writing to a file and rolls the file over according to one of your pre-defined policies. For example, you can define a size-based triggering policy that causes a rollover once the file has reached a specified size. You can also define a time-based triggering policy that causes a rollover once the date/time pattern is no longer applies to the active log file. You can refer the Log4J 2 manual to learn more about the rolling file.

The code to configure a rolling file appender is this.

. . .
"RollingFile": {
  "name": "RollingFile-Appender",
  "fileName": "${log-path}/rollingfile.log",
  "filePattern": "${archive}/rollingfile.log.%d{yyyy-MM-dd-hh-mm}.gz",
  "PatternLayout": {
    "pattern": "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"
  },
  "Policies": {
    "SizeBasedTriggeringPolicy": {
      "size": "1 KB"
    }
  },
  "DefaultRolloverStrategy": {
    "max": "30"
  }
}
. . .

In the code above:

  • Line 3: We used the name property of RollingFile to define a name of this appender that loggers can use.
  • Line 4- Line 5: We used the fileName and filePattern properties to define the name of the file to write to and the pattern of the file name of the archived log file respectively.
  • Line 9 -Line 13: We used Policies to define a sized-based triggering. For testing purpose, we set the max property to roll the log file once its size exceeds 1 KB for.
  • Line 14 – Line 16: We used DefaultRolloverStrategy to instruct Log4J 2 to keep up to 30 rolling files before deleting them.

To use the rolling file appender, add the appender reference to the logger, like this.

. . .
"loggers": {
  "logger": {
    "name": "guru.springframework.blog.log4j2json",
    "level": "debug",
    "appender-ref": [
      {
        "ref": "File-Appender", "level":"error"
      },
    {
      "ref": "RollingFile-Appender", "level":"debug"
    }]
  },
  "root": {
    "level": "debug",
    "appender-ref": {"ref": "Console-Appender"}
  }
}
. . .

In Line 11 of the configuration code above, we added a reference to the rolling file appender. Note that we used a JSON array to define the appender references. This is required because, without an array, Log4J 2 will only catch one appender – the last one.

On running the Log4J2JsonConfTest test class, a rollingfile.log file is generated in the logs folder with debug and higher level log messages. Now if you run the Log4J2JsonConfTest test class couple of more times till the size of the rollingfile.log file exceeds 1 KB, Log4J 2 creates a .gz archive of the generated rolling file in the archive directory.
Archived Rolling File

Logging Additivity

If you have noticed, till now we haven’t used the console appender in our application-specific logger, but log messages are still getting sent to the console. It’s due to additivity. Log messages are getting sent additively to the console by the root logger. You can override this default behavior by setting the additivity property of a logger to false.

The complete code of the log4j2.json file with additivity disabled is this:

{
  "configuration": {
    "name": "Default",
    "properties": {
      "property": [
        {
          "name": "log-path",
          "value": "logs"
        },
        {
          "name": "archive",
          "value": "${log-path}/archive"
        }
      ]
    },
    "appenders": {
      "Console": {
        "name": "Console-Appender",
        "target": "SYSTEM_OUT",
        "PatternLayout": {
          "pattern": "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"
        }
      },
      "File": {
        "name": "File-Appender",
        "fileName": "${log-path}/logfile.log",
        "PatternLayout": {
          "pattern": "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"
        }
      },
      "RollingFile": {
        "name": "RollingFile-Appender",
        "fileName": "${log-path}/rollingfile.log",
        "filePattern": "${archive}/rollingfile.log.%d{yyyy-MM-dd-hh-mm}.gz",
        "PatternLayout": {
          "pattern": "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"
        },
        "Policies": {
          "SizeBasedTriggeringPolicy": {
            "size": "1 KB"
          }
        },
        "DefaultRolloverStrategy": {
          "max": "30"
        }
      }
    },
    "loggers": {
      "logger": {
        "name": "guru.springframework.blog.log4j2json",
        "level": "debug",
        "additivity": "false",
        "appender-ref": [{
          "ref": "Console-Appender", "level":"info"
        },
          {
            "ref": "File-Appender", "level":"error"
          },
        {
          "ref": "RollingFile-Appender", "level":"debug"
        }]
      },
      "root": {
        "level": "debug",
        "appender-ref": {"ref": "Console-Appender"}
      }
    }
  }
}

In Line 54 of the code above, we configured a console appender with the level info for our logger. We also disabled additivity in Line 52 by adding the additivity property with a false value.

Now, when we run the test class, our logger will use the newly configured console appender instead of the one in the root logger. You can run the test class again to check that info and higher log messages are now getting sent to the console, as shown in this figure.

Log4J 2 Additivity Output

Additivity can be somewhat confusing. I suggest reviewing the Log4J 2 documentation on the subject, where they have some good examples how this works.

Summary

JSON is the natural choice for data-interchange in enterprise applications, particularly Web 2.0 applications. There is no evident performance advantage or disadvantage from logging perspective between the various Log4J2 supported formats: properties file, XML, JSON, and YAML. Many argue from configuration perspective that presence of schemas and associated schema validation, which is undoubtedly huge for enterprises, gives XML the edge. Many others support JSON or YAML as they are not only more compact and readable, as compared to XML, but also faster in transmission because it does not come with the extra baggage of tags.

I suggest, as a developer, you should not get tied to a particular format. You may have your own preference, but every enterprise is different. Some may standardize on a format, some may not. Some development teams may prefer JSON over XML, others will like XML over JSON.

About jt

    You May Also Like

    3 comments on “Log4J 2 Configuration: Using JSON

    1. January 26, 2017 at 7:10 pm

      John, I’m using SpringBoot 1.5.0.RC1 (log4j-api and core 2.7 are included) and following these steps. Logging to console works, but I always get the following messages just as the application starts:

      main ERROR Unable to locate plugin type for configuration
      main ERROR Unable to locate plugin for configuration

      If I remove the “configuration” section from the log4j2.json file, the messages go away. What might I be missing? I’ll happily post additional code if you need to see it.

      Thanks, Rob

      Reply

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.