Logback OutputStreamAppender Example
This article discusses the OutputStreamAppender
of logback
, a logging framework for the Java application.
1. Introduction to Logback and Appender
Logback
is designed to be the successor for Log4j
. It has been developed by the same development community. These are some of the advantages logback has over log4j
- Faster implementation – ~10x faster on some critical areas
- Automatic Reloading of configuration files
- Ability to configure in groovy
- Gracefully recover from I/O failures
- Conditional processing of configuration files
- Native Support for SLF4J
SLF4J is expanded as Simple Logging Facade for Java. It provides a logging facade to Java applications enabling the option to switch out logging frameworks. Currently, it supports Log4J
, Logback
and java.util.logging
.
Logback
uses Appenders
to write to the logging destination. Appender has configurable properties which can be used to fine-tune it and also supply the logging destination.
2. Technologies Used
- IntelliJ Idea (Any Java IDE would work)
- Java 1.8.101 (1.8.x will do fine)
- Maven
We will take a look at the maven configuration for our project.
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>com.jcg</groupId> <artifactId>logbackoutputstreamappender</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.6.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
- We have declared our project as
logbackoutputstreamappender
- We declare
logback-classic
, andslf4j
as dependencies logback-classic
in turn brings up the dependency oflogback-core
which is handled by maven’s internal dependency resolution- We define
Maven
compiler to assemble the jar output and configure it with Java version of 1.8
3. OutputStream Appender
OutputStreamAppender
appends events to a logging destination of type java.io.OutputStream
. Well known output streams are Console and file. OutputStreamAppender
cannot be directly used in any logback.xml
file, only its subclasses can be used. OutputStreamAppender
has two configurable properties which affect the behavior of the appender:
encoder
– Determines the layout/structure of the log written to the destination. Default isPatternLayoutEncoder
which accepts a sample pattern like%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
.- The above pattern indicates the date for
d
followed by the pattern which is to get the current time till milliseconds. thread
indicates the currently executing threadlevel
indicates the logging level upto 5 characters in lengthlogger
indicates the logger name(mostly logger class) upto 36 characters in lengthmsg
is the actual message supplied to the loggern
is the system line separator- The above config produces the output:
18:28:10.970 [main] INFO LoggerRoot - Counter:1
- The above pattern indicates the date for
immediateFlush
– default is set to true. If set to true, logs are immediately flushed and sent to logging destination. This ensures that logs are always delivered in a failsafe manner. Setting it to false, increases the performance upto 4x but can lose logs in case of application crashes.
In the following sections, We will take a look at two subclasses of OutputStreamAppender
which are predominantly used. But before that, We will look a simple logger class which utilizes logback to append logs.
LoggerRoot.java
package com.jcg.logbackappender; import org.slf4j.LoggerFactory; import org.slf4j.Logger; import java.util.stream.IntStream; public class LoggerRoot { private static final Logger logger = LoggerFactory.getLogger(LoggerRoot.class.getSimpleName()); public static void main(String... args){ IntStream.rangeClosed(1,10).forEach(counter->{ logger.info("Counter:" + counter); }); } }
- In Line 9, We create a simple logger for our class with the name of our class
LoggerRoot
- We run a simple counter from 1 to 10 and print the counter
3.1. Console Appender
We will directly dive into a configuration of Console Appender and discuss the results below.
logback.xml
<configuration> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <target>System.out</target> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="info"> <appender-ref ref="console"/> </root> </configuration>
- We specify an appender name and provide the class as
ConsoleAppender
- As the name indicates, it supports two targets –
System.Out
orSystem.err
and we have specified output console - We have supplied the pattern discussed in the previous sections and encoder is set to
PatternLayoutEncoder
by default - In Lines 10-12, We wire up the appender to our root logger with logging level set to
INFO
This produces the below sample output.
18:28:10.970 [main] INFO LoggerRoot - Counter:1 18:28:10.975 [main] INFO LoggerRoot - Counter:2 18:28:10.976 [main] INFO LoggerRoot - Counter:3 18:28:10.976 [main] INFO LoggerRoot - Counter:4 18:28:10.976 [main] INFO LoggerRoot - Counter:5 18:28:10.976 [main] INFO LoggerRoot - Counter:6 18:28:10.976 [main] INFO LoggerRoot - Counter:7 18:28:10.976 [main] INFO LoggerRoot - Counter:8 18:28:10.976 [main] INFO LoggerRoot - Counter:9 18:28:10.976 [main] INFO LoggerRoot - Counter:10
3.2. File Appender
In this section, We will take a look at the File Appender. Instead of logging to console, our logging destination is the file.
logback.xml
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/> <appender name="file" class="ch.qos.logback.core.FileAppender"> <file>file-${bySecond}.log</file> <immediateFlush>true</immediateFlush> <append>true</append> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="info"> <appender-ref ref="console"/> <appender-ref ref="file"/> </root>
- We create a timestamp variable bySecond to store the time till second. This can provide information such as the log file creation time in the filename itself
- We utilize an instance of
FileAppender
and supply the parameterbySecond
to the filename - Append property in line 6 indicates the outputstream to append to an existing file if set to true. In our case, our file is dynamically generated and logs would not be appended. But if our file is static i.e without the time prefix, subsequent runs would append the logs to the previous file instead of recreating the file
- We have added the file Appender as another appender to our root logger in line 12
Running the application creates the file similar to file-20181003T081612.log
. If we change immediateFlush
to false, for our small log size, We would see no logs being appended to log file. This is because our application finished execution even before the buffer was full but it still creates the log file.
3.2.1. Rolling File Appender
RollingFileAppender
is a subClass of FileAppender
with some specific tweaks. As the name indicates, it is used in rolling log files i.e log file rotation. It uses rollingPolicy
to determine the criteria for log file rotation.
logback.xml
<appender name="rollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>mylog-%d{yyyy-MM-dd HH:mm:ss}.log</fileNamePattern> <maxHistory>30</maxHistory> <totalSizeCap>3GB</totalSizeCap> </rollingPolicy> <prudent>true</prudent> <immediateFlush>true</immediateFlush> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="info"> <appender-ref ref="console"/> <appender-ref ref="file"/> <appender-ref ref="rollingFile"/> </root>
- We utilize an instance of
RollingFileAppender
and create appender with namerollingFile
- In lines 2-6, We define our rolling policy
- We use
TimeBasedRollingPolicy
to indicate logs rollover criteria based on time. - We include timestamp till seconds in our filename which will hint the rollover to happen every second. Ideally, day based log files are recommended, for which our filename has to be specified as
mylog-%d{yyyy-MM-dd}.log
- We specify
maxHistory
to ensure we keep a maximum of 30 log files. - We specify
totalSizeCap
which will limit the single log file size to 3GB. After which, a new log file will be created ignoring the time.
- We use
- We specify
prudent
as true to allow multiple JVMs writing logs to the same file. Enablingprudent
auto enables append as JVMs need to append to the same file. - We have added the
rollingFile
as another appender to our root logger in line 16.
Running the application produces the log file mylog-2018-10-03 08:16:37.log
. Now in the same example, changing the file name pattern as mylog-%d{yyyy-MM-dd HH:mm:ss.SSS}.log
generates the log as mylog-2018-10-03 20:25:20.516.log
. This also leads to creating log files on a millisecond basis and we can observe that some of our logs will be lost during the file creation time. This is to indicate the granular control of time based rolling.
4. Download the Source Code
That was an Example of Logback OutputStreamAppender.
You can download the full source code of this example here: Logback OutputStream Appender