Logback

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, and slf4j as dependencies
  • logback-classic in turn brings up the dependency of logback-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 is PatternLayoutEncoder 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 thread
    • level indicates the logging level upto 5 characters in length
    • logger indicates the logger name(mostly logger class) upto 36 characters in length
    • msg is the actual message supplied to the logger
    • n is the system line separator
    • The above config produces the output:
      18:28:10.970 [main] INFO LoggerRoot - Counter:1
  • 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 or System.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 parameter bySecond 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 name rollingFile
  • 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 specify prudent as true to allow multiple JVMs writing logs to the same file. Enabling prudent 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.

Download
You can download the full source code of this example here: Logback OutputStream Appender

Rajagopal ParthaSarathi

Rajagopal works in software industry solving enterprise-scale problems for customers across geographies specializing in distributed platforms. He holds a masters in computer science with focus on cloud computing from Illinois Institute of Technology. His current interests include data science and distributed computing.
Subscribe
Notify of
guest

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

0 Comments
Inline Feedbacks
View all comments
Back to top button