Technical Resources
Educational Resources
APM Integrated Experience
Connect with Us
Log4j 2 is one of the most widely used logging libraries for Java. It offers developers impressive performance and unparalleled flexibility, and it integrates with almost all logging platforms. This article discusses Log4j 2 configuration and how to use this logging library to manage log files.
One of the most powerful features of Log4j is its configuration capabilities. You can configure it via several different file formats, or you can let it use its default configuration.
Log4j supports Java properties, YAML, JSON, and XML configuration files. The same ConfigurationFactory class loads the files, and except for the properties files, the files are structured similarly. The properties files are different because the syntax doesn’t have the same support for structured data.
Log4j configuration offers various options, so let’s start by going over the library’s rules to determine which file to use. First, Log4j checks the log4j.configurationFile system property. If it’s set, it will load the specified file and use it. Next, it looks on the classpath for a configuration file. Configuration files are named either log4j2-test or log4j2. Log4j searches for log4j2-test first. The type of file is identified by the extension. Log4j 2 looks for Java properties with the .properties extension first. Next, it looks for YAML with .yaml or .yml, or JSON with .json or .jsn. Finally, it looks for XML with an .xml extension. If no file is found, the default configuration is to log error and fatal messages to the console. These rules seem complicated. But we can condense them.
1. Check for a configuration property. If you want to use a custom configuration file name, this is your best bet.
2. Check for a test configuration file in the classpath. Look for properties, YAML, JSON, or XML, in that order.
3. Check for a production configuration file in the classpath. Look for properties, YAML, JSON, or XML, in that order.
4. Log to the console.
If you do the math, Log4j has 12 different default configuration file names. If you don’t use log4j.configurationFile: then your application may load the wrong file. This could lead to surprises. Before you deploy your code, make sure your application is loading the right file. Either scan the classpath and make sure there’s only one Log4j 2 configuration file or, better yet, use the log4j.configurationFile property.
When it comes to Log4j 2 configuration, you have multiple options. For the examples, I’ll use YAML. Since Log4j uses a similar model for the other languages, you can still follow along and learn how the configurations work.
Let’s start with a configuration file that sets up Log4j with the default config.
Configuration:
status: warn
Appenders:
Console:
name: Console
target: SYSTEM_OUT
PatternLayout:
Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
Loggers:
Root:
level: error
AppenderRef:
ref: Console
From the top of the file, this config tells Log4j to do the following:
– Log internal messages that are WARN level or higher.
– Log them to the console using standard output.
– Format the message with a time stamp, the thread name, the level, the logger name, and then the message.
– Use this logger as the Root logger in the hierarchy.
Let’s cover this configuration in detail.
Log4j places loggers in a hierarchy, similar to class hierarchy in Java. All loggers are descendants of Root. Loggers that aren’t configured inherit the Root config. So, if you don’t specify a Log4j 2 configuration, all messages logged at the error level or higher go to the console. We’ll go over the logging level hierarchy later. Let’s say you want to troubleshoot a problem in your code but don’t want to turn on debug logging for the entire application because it would make the logs hard to manage.
Configuration:
status: warn
Appenders:
Console:
name: Console
target: SYSTEM_OUT
PatternLayout:
Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
Loggers:
logger:
name: com.company.BuggyClass
level: debug
additivity: false
AppenderRef:
ref: Console
Root:
level: error
AppenderRef:
ref: Console
The new Logger adds a new logger to the hierarchy. The logger created by com.company.BuggyClass logs debug messages, while the rest of the app falls through the Root logger and only logs error messages. This is a simple two-level hierarchy, but a logger hierarchy can have multiple levels. We’ll look at how below.
Log4j handles log levels, or severity, by priority. Trace is the lowest, and fatal is the highest. Here are the possible levels, in order from lowest to highest priority.
1. Trace
2. Debug
3. Info
4. Warn
5. Error
6. Fatal
If a logger is configured for debug, it will log any message that is debug through fatal. If it’s set for error, it will log only error or fatal messages.
Appenders are what send log messages where they belong. The default configuration supplies a console appender.
Appenders:
Console:
name: Console
target: SYSTEM_OUT
PatternLayout:
Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
A file appender puts messages in a file.
Appenders:
File:
name: File_Appender
fileName: buggyclass.log
PatternLayout:
Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
Appenders have names for associating them with loggers. They have PatternLayouts that define how the messages are formatted. They also have type-specific values, like target for the console appender and fileName for the file appender. Let’s revisit our earlier config and see how appenders are associated with loggers.
Configuration:
status: warn
Appenders:
Console:
name: Console
target: SYSTEM_OUT
PatternLayout:
Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
BuggyFile:
name: BuggyFile_Appender
fileName: buggyclass.log
PatternLayout:
Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
CompanyFile:
name: CompanyFile_Appender
fileName: company.log
PatternLayout:
Pattern: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
Loggers:
logger:
-
name: com.company.BuggyClass
level: debug
additivity: false
AppenderRef:
ref: BuggyFile_Appender
-
name: com.company.Main
level: info
additivity: false
AppenderRef:
ref: CompanyFile_Appender
Root:
level: error
AppenderRef:
ref: Console
Now the BuggyClass logger is still associated with its own appender. But com.company has one too. So, BuggyClass will log debug messages while the rest of the application logs info messages. The rest of the application will print error messages to the console. So, by creating loggers, placing them in the hierarchy, and associating appenders with them, you route log messages to different destinations.
All appenders have a Layout. In this example, we’re using PatternLayout, which is an instance of a Log4j layout class. Log4j has built-in layouts for logging messages in CSV, JSON, and various formats. PatternLayouts use formatting that works like C’s sprintf. The pattern defines how the log messages are assembled before they are logged. Each operator in the log pattern corresponds to a field in a log event. For example, we’re using the default log message. Here’s the pattern layout for it.
"%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
– %dHH:mm:ss.SSS is the date, formatted as hour:minute:seconds.milliseconds.
– %t is the name of the thread logging the message.
– %level is the log level. The -5 instructs PatternLayout to pad up to five spaces. This is so INFO and WARN align with the other log levels.
– %logger is the logger name. {36}truncates the class name to 36 levels in the hierarchy.
– %msg is the log message.
– %n is a newline. PatternLayouts do not automatically start a new line for every message.
There are many operators for PatternLayout. For an exhaustive list, see the Log4j 2 configuration documentation.
Log4j offers a wide variety of logging appenders for managing logs.
The Linux syslog system is a log management system. With it, you can manage log files and your Linux system or forward your logs to another server or an aggregator like SolarWinds® Loggly®. This configuration is more complex than other appenders. For details on this appender, see the documentation.
Syslog:
name: syslog
host: 172.17.0.1
port: 514
protocol: UDP
facility: local6
connectTimeoutMillis: 10000
reconnectionDelayMillis: 5000
mdcId: mdc
mdcExcludes: "X-B3-SpanId,X-B3-TraceId,X-Span-Export,spanExportable,spanId,traceId"
messageId: mywebapp
appName: mywebapp
charset: UTF-8
ignoreExceptions: true
newLine: true
format: RFC5424
LoggerFields:
KeyValuePair: !!pairs
- key: priority
value: "%p"
- key: category
value: "%c{0}"
- key: pid
value: "%pid"
- key: traceSpanId
value: "%X{traceId}/%X{spanId}"
- key: exception
value: "%ex"
The Log4j RollingFileAppender will rotate your log files when they reach a certain length, size, or age. This means when your log files reach the size limit or time limit, the current log file is saved, and a new log file is created. This configuration rolls the files at 100 kb and keeps no more than 30 old files in the logging directory. For details on this appender, see the documentation.
RollingFile:
- name: RollingFile_Appender
fileName: ${log-path}/rollingfile.log
filePattern: "logs/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: 100 KB
DefaultRollOverStrategy:
max: 30
With Log4j, you can manage your logging down to the class level, with customized formatting, prioritization, and routing for both your application and specific parts of your code. Then you can take your log management to the next step with SolarWinds Loggly. Sign up for a free trial today!