When it comes to logging in enterprise applications, logback makes an excellent choice – it’s simple and fast, has powerful configuration options, and comes with a small memory footprint. I have introduced logback in my introductory post, Logback Introduction: An Enterprise Logging Framework. YAML is just one option you can use for Spring Boot configuration. In a series of posts on logback, I’ve also discussed how to configure Logback using XML and Groovy and how to use Logback in Spring Boot applications. The posts are available as:

In my earlier post on Using Logback with Spring Boot, I used a properties file to configure logback. In this post, I’ll discuss how to configure Logback using Spring Boot’s YAML configuration file. If you’re a seasoned user of the Spring Framework, you’ll find YAML a relatively new configuration option available to you when using Spring Boot.

Creating a Logger

We’ll use a simple Spring Boot web application and configure logback with YAML in that application. Please refer my previous post, where I wrote about creating a web application using Spring Boot. This post expands upon concepts from the previous post, but is focused on the use of YAML configuration with Spring Boot.

The application from the previous post contains a controller,
IndexController to which we’ll add logging code, like this.

IndexController.java

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

packageguru.springframework.controllers;

importorg.slf4j.Logger;

importorg.slf4j.LoggerFactory;

importorg.springframework.stereotype.Controller;

importorg.springframework.web.bind.annotation.RequestMapping;

@Controller

publicclassIndexController{

privatefinalLogger logger=LoggerFactory.getLogger(this.getClass());

@RequestMapping("/")

Stringindex(){

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");

return"index";

}

}

Since Logback is the default logger under Spring Boot, you do not need to include any additional dependencies for Logback or SLF4J.

Run the
SpringBootWebApplication main class. When the application starts, access it from your browser with the URL, http://localhost:8080

The logging output on the IntelliJ console is this.

In the output above, the logging messages from
IndexController are sent to the console by the logback root logger. Notice that the debug message of
IndexController is not getting logged. Logback by default will log debug level messages. However, the Spring Boot team provides us a default configuration for Logback in the Spring Boot default logback configuration file,
base.xml. In addition, Spring Boot provides provide two preconfigured appenders through the
console-appender.xml and
file-appender.xml files. The
base.xml file references both of them.

The code of the
base.xml file from the spring-boot github repo is this.

An error has occurred. Please try again later.

Here you can see that Spring Boot has overridden the default logging level of logback by setting the root logger to
INFO, which is the reason we did not see the debug messages in the example above. As we’ll see in the next section, changing log levels in Spring Boot is very simple.

YAML Configuration via Spring Boot’s application.yml File

In a Spring Boot application, you can externalize configuration to work with the same application code in different environments. The
application.yml file is one of the many ways to externalize configuration. Let’s use it to externalize logging configuration.

If you wish to use YAML for your Spring configuration, you simply need to create a YAML file. Spring Boot will look for a
application.yml file on the classpath. In the default structure of a Spring Boot web application, you can place the file under the
Resources directory. To parse YAML files, you need a YAML parser. Out of the box, Spring Boot uses SankeYAML, an YAML parser. There is nothing you need to do to enable YAML support in Spring Boot. By default under Spring Boot, YAML is ready to go.

Here is an example of an
application.yml file with basic configurations of logging levels.

YAML

1

2

3

4

5

6

spring:

logging:

level:

org.springframework.web: DEBUG

guru.springframework.controllers: DEBUG

org.hibernate: DEBUG

In the configuration code above, we set the log levels of the Spring framework, any application logger of the
guru.springframework.controllers package and its sub-packages, and hibernate to
DEBUG. Although, we are not using Hibernate in our example application, I have added the Hibernate logging configuration for demonstration purposes so you can see how to configure logging for various Java packages.

When you run the application, you’ll notice
DEBUG messages of the Spring frameworks’ startup on the console. When you access the application, notice the log messages of the
IndexController now include the debug message.

At this point, log messages are only being sent to the console. You can configure Spring Boot to additionally log messages to log files. You can also set the patterns of log messages both for console and file separately, like this.

YAML

1

2

3

4

5

6

7

8

9

10

spring:

logging:

file: logs/dev_app.log

pattern:

console: "%d %-5level %logger : %msg%n"

file: "%d %-5level [%thread] %logger : %msg%n"

level:

org.springframework.web: DEBUG

guru.springframework.controllers: DEBUG

org.hibernate: DEBUG

With the updated YAML configuration, here is an example of the logging output.

Spring Active Profile Properties in YAML

Spring Profiles are commonly used to configure Spring for different deployment environments. For example, while developing in your local machine, it is common to set the log level to
DEBUG. This will give you detailed log messages for your development use. While on production, its typical set the log level to
WARN or above. This is to avoid filling your logs with excessive debug information and incurring the overhead of excessive logging.

You can segregate a YAML configuration into separate profiles with a
spring.profiles key for each profile. Then, add the required logging configuration code to each profile and ensure that the profile lists are separated by the
--- lines. In the same file, you can use the
spring.profiles.active key to set the active profile. However, this is not mandatory. You can also set the active profile to use programmatically or passing it as a system property or JVM argument while running the application.

The complete
application.yml file with logging configuration based on Spring profiles is this.

YAML

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

spring:

profiles.active: dev

---

spring:

profiles: dev

logging:

file: logs/dev_app.log

pattern:

console: "%d %-5level %logger : %msg%n"

file: "%d %-5level [%thread] %logger : %msg%n"

level:

org.springframework.web: DEBUG

guru.springframework.controllers: DEBUG

org.hibernate: DEBUG

---

spring:

profiles: production

logging:

file: logs/production_app.log

pattern:

file: "%d %-5level [%thread] %logger{0}: %msg%n"

level:

org.springframework.web: WARN

guru.springframework.controllers: WARN

org.hibernate: WARN

In the configuration code above, we defined two profiles:
dev and
production with different logging configurations. We also set the active profile to
dev.

When you run and access the application, the logging configuration of the
dev profile will be used and the logging outputs will be similar to this.

Now let’s make
production the active profile by passing the
-Dspring.profiles.active=production JVM argument.

In IntelliJ, select Run-> Edit Configurations, and set the JVM argument in the Run/Debug Configurations dialog box that appears, like this.

The logging output on accessing the application with
production as the active profile is this.

Separating Profiles in YAML Configuration Files

A Spring Boot configuration file is not limited to logging configurations only. Typically, several different types of configurations go into the different profiles of an enterprise application. Configurations can be of bean registrations, database connection settings, SMTP settings, etc. spread across development, testing, staging, production, and other profiles.

It’s both tedious and error prone to maintain a single file with multiple profiles with each profile containing different types of configuration settings. Remember, much more time is spent reading code and configuration files than is spent writing it. At some point in the future, yourself, or someone else, will be reading or updating the configuration files. And for monolithic configuration files with low readability, the chances of errors creeping in is high. Spring addresses such challenges by allowing separate configuration files – one for each profile. With separate configuration files, you improve the long term maintainability of your application.

Each of such configuration file must follow the
application-.yml naming convention. For example, for the
dev and
production profiles, you need the
application-dev.yml and
application-production.yml files in the classpath. You should also add a
application-default.yml file containing default configurations. When no active profile is set, Spring Boot falls back to the default configurations in
application-default.yml.

It’s important to note that if you have a
application.yml file (with no suffix) on your path, that it will always be included by Spring, regardless of what profiles are or not active.

The project structure of the Spring Boot web application with different profile-specific configuration files is this.

Following are the code for each of the configuration files.

application-default.yml

YAML

1

2

3

4

5

6

7

logging:

pattern:

console: "%msg%n"

level:

org.springframework.web: INFO

guru.springframework.controllers: INFO

org.hibernate: INFO

application-dev.yml

YAML

1

2

3

4

5

6

7

8

9

logging:

file: logs/dev_profile_app.log

pattern:

console: "%d %-5level %logger : %msg%n"

file: "%d %-5level [%thread] %logger : %msg%n"

level:

org.springframework.web: DEBUG

guru.springframework.controllers: DEBUG

org.hibernate: DEBUG

application-production.yml

YAML

1

2

3

4

5

6

7

8

logging:

file: logs/production_profile_app.log

pattern:

file: "%d %-5level [%thread] %logger{0}: %msg%n"

level:

org.springframework.web: WARN

guru.springframework.controllers: WARN

org.hibernate: WARN

Test the application by first starting it without any profile, then with the
dev profile, and finally the
production profile. Ensure that the expected configurations are being used for the different environments.

Conclusion

YAML configuration file in Spring Boot provides a very convenient syntax for storing logging configurations in a hierarchical format. YAML configuration, similar to Properties configuration cannot handle some advanced features, such as different types of appender configurations, and also encoders and layout configurations.

Functionally, YAML is nearly the same as using a traditional properties file. Personally, I find YAML fun to write in. It feels more expressive than the old school properties files and it has a nice clean syntax. Often you don’t need many of the more advanced logging features of logback. So you’re fine using the simplicity of the YAML file configuration. For advanced logging configurations using XML and Groovy, explore my earlier posts on them available here, and here.

I have encountered one issue with using YAML files for Spring Boot configuration. When setting up a JUnit test outside of Spring Boot, it was problematic to read the YAML properties file with just Spring. Remember, YAML support is specific to Spring Boot. I suspect at some point it will get included into the core functionality of Spring (If it has not already been).

Logback supports configuration through XML and Groovy. I explained XML configuration in my previous post, Logback Configuration: using XML. We’ll use similar configuration options for Logback, but this time in Groovy.

Because of its simplicity and flexibility, Groovy is an excellent tool for configuring Logback. Groovy is intuitive and has an easy-to-learn syntax. Even if you aren’t familiar with it, you should still easily understand, read, and write groovy configurations for Logback.

Creating a Logger

We will start by creating an application logger. As I mentioned in my earlier post here, for a Spring Boot application, we don’t need any additional Logback dependencies in our Maven POM. Groovy support is already there. We just need to start using it. Let’s start with creating a class and test for our example.

The Groovy Configuration File

When Logback starts, it searches for a
logback.groovy file in the classpath to configure itself. If you store the file in a different location outside the classpath, you will need to use the
logback.configurationFile system property to point to the location, like this.

1

-DLogback.configurationFile=/path/to/logback.groovy

In a
logback.groovy file, you can enable auto-scan using the
scan() method. With auto-scan enabled, Logback scans for changes in the configuration file. For any changes, Logback automatically reconfigure itself with them. By default, Logback scans for changes once every minute. You can specify a different scanning period by passing a scan period, with a value specified in units of milliseconds, seconds, minutes or hours as parameter to
scan(). For example,
scan("30 seconds") tells Logback to scan
Logback.groovy after every 30 seconds.

In
logback.groovy, you can define one or more properties, like this.

1

2

def LOG_PATH="logs"

def LOG_ARCHIVE="${LOG_PATH}/archive"

Configuration code after a property declaration can refer the property with the
${property_name} syntax, as shown in the second line of the code above. If you’re familiar with Spring, you’ll find this syntax similar to SpEL.

Console and File Appenders

You declare one or more appenders with the
appender() method. This method takes the name of the appender being configured as its first mandatory argument. The second mandatory argument is the class of the appender to instantiate. An optional third element is a closure, an anonymous block containing further configuration instructions.

The code to create a Logback console and a file appender, is this.

1

2

3

4

5

6

7

8

9

10

11

12

appender("Console-Appender",ConsoleAppender){

encoder(PatternLayoutEncoder){

pattern="%msg%n"

}

}

appender("File-Appender",FileAppender){

file="${LOG_PATH}/logfile.log"

encoder(PatternLayoutEncoder){

pattern="%msg%n"

outputPatternAsHeader=true

}

}

In the code above:

Line 1: We called the
appender() method passing
Console-Appender as the appender name and
ConsoleAppender as the appender implementation class.

Line 2: We injected an encoder of type
PatternLayoutEncoder into the appender.

Line 3: We set the
pattern property of the encoder to the
%msg%n conversion pattern. A conversion pattern is composed of literal text and format control expressions called conversion specifiers. You can learn more about pattern layout and conversion specifiers here.

Line 6 – Line 12: We created a file appender named
File-Appender of the
FileAppender type, and set the
file property to a log file. We injected an encoder into the appender and set the
pattern and
outputPatternAsHeader properties of the encoder. The
outputPatternAsHeader property, when set to
true, inserts the pattern used for the log output at the top of log files.

We will now configure an application-specific logger along with the root logger to use the console and file appenders.

You use the
logger() method to configure a logger. This method accepts the following parameters.

A string specifying the name of the logger.

One of the
OFF,
ERROR,
WARN,
INFO,
DEBUG,
TRACE, or
ALL fields of the Level class to represent the level of the designated logger.

An optional
List containing one or more appenders to be attached to the logger.

An optional
Boolean value indicating additivity. The default value is
true. We will come to additivity a bit later.

In the code above, we configured all loggers of the
guru.springframework.blog.Logbackgroovy package and its sub-packages to log
INFO and higher level messages to the configured
File-Appender. We also configured the root logger to log
INFO and higher level messages to the configured
Console-Appender.

The output on running the test class,
LogbackConfigGroovyTest is this.

Rolling File Appender

The rolling file appender supports writing to a file and rolls the file over according to one of your pre-defined policies. To learn more about the rolling file appender and its policies, refer to the Logback manual.

The code to configure a rolling file appender is this.

1

2

3

4

5

6

7

8

9

10

11

appender("RollingFile-Appender",RollingFileAppender){

file="${LOG_PATH}/rollingfile.log"

rollingPolicy(TimeBasedRollingPolicy){

fileNamePattern="${LOG_ARCHIVE}/rollingfile.log%d{yyyy-MM-dd}.log"

maxHistory=30

totalSizeCap="1KB"

}

encoder(PatternLayoutEncoder){

pattern="%msg%n"

}

}

in the code above:

Line 1: We called the
appender() method passing
RollingFile-Appender as the appender name and
RollingFileAppender as the appender class.

Line 2: We set the
file property of the appender to specify the rolling file.

Line 3: We injected a time-based rolling policy of type
TimeBasedRollingPolicy into the appender. A time-based rolling policy performs a rollover once the date/time pattern is no longer applies to the active log file.

Line 4 – Line 6: We set the
fileNamePattern,
maxHistory, and
totalSizeCap properties of the policy. The
fileNamePattern property defines a file name pattern for archived log files. The rollover period is inferred from the value of
fileNamePattern, which in the code example is set for daily rolling. The
maxHistory property sets the maximum number of archive files to keep, before deleting older files asynchronously. The
totalSizeCap element sets the total size of all archive files. Oldest archives are deleted asynchronously when the total size cap is exceeded.

To use the rolling file appender, add the appender name in the list passed to the
logger() method, like this.

At this point, if you run the test class, a rolling log file named
rollingfile.log is created under
logs. To simulate a rollover, you can set the system clock one day ahead and run the test class again. A new
rollingfile.log is created under
logs and the previous file is archived in the
logs/archive folder.

Async Appender

An async appender runs in a separate thread to decouple the logging overhead from the thread executing your code. To make an appender async, first call the
appender() method passing a name for the async appender and an
AsyncAppender object. Then, inject the appender to invoke asynchronously, like this.

1

2

3

appender("Async-Appender",AsyncAppender){

appenderRef("RollingFile-Appender")

}

The code above makes the
RollingFile-Appender appender asynchronous.

Once you define an async appender, you can use it in a logger like any other appender, as shown.

In the configuration code above, observe Line 34. I included the console appender and passed a
false parameter to
logger(). I did this to disable additivity. With additivity disabled, Logback will use
Console-Appender of the application logger instead of the one configured for the root logger. Review the Logback manual for more information on additivity. If you have noticed in the Groovy code, we intentionally skipped a number of import statements. To reduce unnecessary boilerplate code, Groovy includes several common types and packages by default.

Summary

If you work with both XML and Groovy to configure Logback, you will find the Groovy syntax less verbose, and so more readable. Also, the Groovy syntax being a super-set of Java syntax is more intuitive for Java developers. On the other hand, XML with the years of industry backing is more popular and have a larger user base. You’ll find better IDE support when using XML, since it can be validated against the structure of a XML schema. Groovy doesn’t have this fallback. So while you may be writing snytxually correct Groovy code, it may fail to configure Logback in the way in you intended.

The Logback team provides an online conversion tool to translate a Logback.xml file into the equivalent Logback.groovy configuration file. Although, you cannot expect 100% accuracy, It’s a good tool to use for reference.

Personally, I feel you should not lock yourself into XML or Groovy. XML will provide you highly structured configuration. Groovy gives you freedom to do things programmatically, which you cannot do in a structured XML document. Most of the time, XML configuration will be just fine. But when you have a complex use case for your logging requirements, Groovy is a great tool you can use to configure Logback.

The whole purpose of logging gets defeated when the underlying logging framework becomes a bottleneck. Logging frameworks need to be fast, have a small memory footprint, and easily configurable. Logback is a logging framework with those qualities. If you are new to Logback, I suggest going through my introductory post on Logback: Logback Introduction: An Enterprise Logging Framework.

Logback supports configuration through XML and Groovy. In this post, I’ll discuss how to configure Logback using an XML file.

Creating a Logger

We will start by creating an application logger and later configure it through XML. As mentioned here, if we are using Spring Boot, we don’t require any additional dependency declaration on Logback in our Maven POM. We can straightaway start writing logging code.

The Logback Configuration File

For Logback configuration through XML, Logback expects a
Logback.xml or
Logback-test.xml file in the classpath. In a Spring Boot application, you can put the
Logback.xml file in the
resources folder. If your
Logback.xml file is outside the classpath, you need to point to its location using the
Logback.configurationFile system property, like this.

1

-DLogback.configurationFile=/path/to/Logback.xml

In a
Logback.xml file, all the configuration options are enclosed within the
<configuration> root element. In the root element, you can set the
debug=true attribute to inspect Logback’s internal status. You can also configure auto scanning of the configuration file by setting the
scan=true attribute. When you do so, Logback scans for changes in its configuration file. If Logback finds any changes, Logback automatically reconfigure itself with the changes. When auto scanning is enabled, Logback scans for changes once every minute. You can specify a different scanning period by setting the
scanPeriod attribute, with a value specified in units of milliseconds, seconds, minutes or hours, like this.

1

2

3

<configuration debug="true"scan="true"scanPeriod="30 seconds">

...

</configuration>

The
<configuration> root element can contain one or more properties in local scope, each specified using a
<property> element. Such a property exists from the point of its definition in the configuration file until the interpretation/execution of the file completes. Configuration options in other parts of the file can access the property using the
${property_name} syntax. Declare properties in the local scope for values that might change in different environments. For example, path to log files, database connections, SMTP server settings, and so on.

At this point, one Logback element worth mentioning is
<timestamp>. This element defines a property according to the current date and time – particularly useful when you log to a file. Using this property, you can create a new log file uniquely named by timestamp at each new application launch. The code to declare a timestamp property is this.

1

<timestamp key="timestamp-by-second"datePattern="yyyyMMdd'T'HHmmss"/>

In the code above, the
datePattern attribute denotes the date pattern used to convert the current time following the conventions defined in SimpleDateFormat.

Next, we’ll look how to use each of the declared properties from different appenders.

Console and File Appenders

You declare one or more appenders with the
<appender> element containing the mandatory
name and
class attributes. The
name attribute specifies the appender name that loggers can refer whereas the
class attribute specifies the fully qualified name of the appender class. The appender element can contain
<layout> or
<encoder> elements to define how logging events are transformed. Here is the configuration code to define console and file appenders:

Line 2 – Line 6: We defined a console appender with the name
Console-Appender to use a pattern layout. Note that we haven’t explicitly set the layout, but instead relied on the default Logback value that uses pattern layout.

Line 4: We defined a conversion pattern with the
<pattern> element. A conversion pattern is composed of literal text and format control expressions called conversion specifiers. In the code, the
%msg conversion specifier outputs the application-supplied message associated with the logging event. The
%n conversion specifier outputs the platform dependent line separator characters. You can learn more about pattern layout and conversion specifiers here.

Line 7 – Line 13: We defined a file appender with the name
File-Appender. This appender writes to a file defined by the
<file> element. Observe how we referred the properties we defined earlier to generate a new log file each time the application starts.

Line 10 – Line 11: We defined an encoder with a pattern. We also used
outputPatternAsHeader to insert the pattern used for the log output at the top of log files.

Note: Encoders were introduced in Logback version 0.9.19. Due to the benefits that encoders provide, as explained here, it is recommended to use encoders instead of layouts. As a matter of fact, Logback has removed support for layouts in FileAppender and its sub-classes from version 0.9.19 onwards.

We will now configure an application-specific logger along with the root logger to use the console and appenders, like this.

1

2

3

4

5

6

7

8

...

<logger name="guru.springframework.blog.logbackxml"level="info">

<appender-ref ref="File-Appender"/>

</logger>

<root>

<appender-ref ref="Console-Appender"/>

</root>

...

In the code above, we defined two loggers. The first logger defined by
<logger> configures all loggers under the
guru.springframework.blog.logbackxml package to use the file appender. The second one defined by
<root> is the root logger configured to use the console appender.

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

Run the test class again. Observe how Logback uses the timestamp property to generate a separate log file based on the specified date pattern.

Rolling File Appender

The rolling file appender supports writing to a file and rolls the file over according to one of your pre-defined policies. The most popular policy is the time-based rolling policy. You can define a time-based policy to perform a rollover once the date/time pattern is no longer applies to the active log file. To learn more about the rolling file appender and its policies, refer to the Logback user manual.

Line 3: The
<file> element defines the name of the log file to write to.

Line 4: The
<rollingPolicy> element defines a time-based policy.

Line 5: The
<fileNamePattern> element defines a file name pattern for archived log files. The rollover period is inferred from the value of
<fileNamePattern>, which in the code example is set for daily rolling.

Line 6: The
<maxHistory> element sets the maximum number of archive files to keep, before deleting older files asynchronously.

Line 7: The
<totalSizeCap&gt element sets the total size of all archive files. Oldest archives are deleted asynchronously when the total size cap is exceeded.

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

1

2

3

4

5

6

...

<logger name="guru.springframework.blog.logbackxml"level="info">

<appender-ref ref="File-Appender"/>

<appender-ref ref="RollingFile-Appender"/>

</logger>

...

At this point, when you run the test class, a rolling log file, named
rollingfile.log is created under logs. To simulate a rollover, set the system clock one day ahead, and run the test class again. A new
rollingfile.log is created under logs and the previous file is archived in the
logs/archive folder.

In addition to the time-based rolling policy, you can define a size-based triggering policy. It’s important to understand the difference between rolling and triggering policies. A rolling policy defines WHAT happens when rollover occurs, whereas a triggering policy defines WHEN a rollover should occur. The following code sets a triggering policy to trigger a rollover when the size of a log file exceeds 1 MB.

Logback Async Appender

For increased logging performance, we want lower logging latency and higher throughput. Latency is the time required to perform some action or to produce some result. On the other hand, throughput is the number of some actions executed or results produced per unit of time.

To consistently achieve lower latency and higher throughput, Logback supports asynchronous logging through an async appender. Logback executes an async appender in a separate thread to decouple the logging overhead from the thread executing your code.

Using the async appender is incredibly easy. Refer the appender that should be asynchronously invoked within an
<appender> element. Then, set the
class attribute to the fully qualified name of
AsyncAppender, like this.

Once you define an async appender, you can use it in a logger like any other appender, like this.

1

2

3

4

5

6

...

<logger name="guru.springframework.blog.logbackxml"level="info">

<appender-ref ref="File-Appender"/>

<appender-ref ref="Async-Appender"/>

</logger>

...

Logback Additivity

To understand Logback additivity, let’s add the configured console appender to the application logger. The logger configuration code is this.

1

2

3

4

5

6

7

8

9

10

...

<logger name="guru.springframework.blog.logbackxml"level="info">

<appender-ref ref="Console-Appender"/>

<appender-ref ref="File-Appender"/>

<appender-ref ref="RollingFile-Appender"/>

</logger>

<root>

<appender-ref ref="Console-Appender"/>

</root>

...

The console output on running the test class is this.

In the figure above, notice the duplicate output. It’s due to additivity. The appender named
Console-Appender is attached to two loggers:
root and
guru.springframework.blog.Logbackxml. Since root is the ancestor of all loggers, logging request made by
guru.springframework.blog.Logbackxml gets output twice. Once by the appender attached to
guru.springframework.blog.Logbackxml itself and once by the appender attached to root. You can override this default Logback behavior by setting the
additivity flag of a logger to
false, like this.

Summary

One feature I’d like to see with Logback is the ability to use different appenders at different levels from the same logger. On searching the web, I came to the LOGBACK-625 enhancement issue requesting this feature. One workaround is using a filter inside the appender, as described here. Although not elegant, you can use the approach until the Logback team addresses this enhancement request.

Monitoring, diagnosing, and troubleshooting are key activities in any enterprise application lifecycle, and logging is the core part of these activities. Through logging you get to see what the application code is actually doing during these activities at runtime. Using System.out to print messages to the console is simply not sufficient for enterprise applications. Enterprise applications have logging requirements with varied degree of complexities. You will need generating logs with different levels of importance, such as ERROR, WARN, INFO, and DEBUG. You’ll also have requirements to send logs to different destinations, such as console, file, database, SMTP server, or JMS queue destination. These requirements are not possible with simple System.out statements. Logging frameworks such as Logback are designed to meet the needs of logging in the enterprise.

Logback Architecture

Ceki Gülcü the founder of the Log4J along with Sébastien Pennec, another Log4J contributor, designed logback. With decades of experience with logging frameworks, they designed Logback to be fast and generic enough to work under different environments. Logback is comprised of three modules:

logback-core: Forms the foundation of logback-classic and logback-access. To perform logging, you need the more specialized logback-classic or logback-access.

logback-classic: Relies on logback-core for logging services.

logback-access: Provides HTTP-access log functionalities to servlets containers, such as Tomcat and Jetty.

In this post we will explore
log-back-classic , which in going forward I’ll refer to as logback. Logback natively implements the Simple Logging Facade for Java (SLF4J) API. In a nutshell, SLF4J is a façade for various logging frameworks. As a developer, you’ll write logging code against the SLF4J API. At deployment time, you have the flexibility to plug-in a desired logging framework, made possible through an intermediate SLF4J bridge layer. As logback natively implements SLF4J, the additional SLF4J API layer doesn’t incur any performance overhead, a slight advantage that Logback has over other frameworks.

This figure illustrates the interaction of an application with Logback.

The key Logback components are loggers, appenders and encoders/layouts. The components work together to provide developer full control on how messages are logged, formatted, and where they are reported.

Logger

Loggers are the components that do the heavy work in logging. They capture the logging data and output it to a destination using appenders. The loggers used in an application are typically organized into a hierarchy and a root logger resides at the top of the hierarchy. It is the LoggerContext that is responsible for creating loggers and arranging them in a hierarchy.

Loggers maintains a hierarchical naming rule. As an example, a logger named guru is the parent of the logger, named guru.springframework and the ancestor of the logger, named guru.springframework.blog.

Apart from logger inheritance, an important logback concept is level inheritance, also referred as effective level. You can assign levels to loggers. Logback supports the TRACE, DEBUG, INFO, WARN and ERROR levels, as shown in this figure.

As you can see in the figure above, TRACE is the lowest level and the level moves up through, DEBUG, INFO, WARN, till ERROR, the highest level. This means that if you set the logger level to WARN, then only the WARN and ERROR level log messages will be displayed and the rest will be ignored.

In addition to the above levels, there are two special levels:

ALL: Turns on all levels.

OFF: Turns off all levels.

If a logger is not assigned a level, then level inheritance comes into play. The logger will inherit the level from its nearest ancestor with an assigned level. If none of the application loggers in the hierarchy have assigned level, the level of the root logger will be inherited. The default level of the root logger is DEBUG.

Note: While developing in your local machine, it is common to set the log level to DEBUG. This will give you detailed log messages for your development use. When deployed to a production environment, it’s typical to set the log level to ERROR. This is to avoid filling your logs with excessive debug information. Also, while logging is very efficient, there is still a cost to system resources.

Appenders

Once you capture logging information through a logger, you need to send it to an output destination. The output destination is called an appender, and it’s attached to the logger. Log4J 2 provides appenders for console, files, remote socket servers, SMTP servers, many popular databases (such as MySQL, PostgreSQL, and Oracle), JMS, remote UNIX Syslog daemons, and more.

Layouts/Encoders

An appender uses a layout to format a log event. A layout, which is an implementation of the Layout interface of log4j-core, transforms a log event to a string. A layout can’t control when log events get written out, and therefore can’t group events into batches. To address the limitations of layouts, logback introduced encoders in version 0.9.19. Encoders, which are implementation of the Encoder interface, transforms an incoming log event into a byte array and writes out the resulting array onto the appropriate output stream. Encoders have total control over the format of the bytes written out. In addition, encoders can control whether (and when) those bytes get written out. I’ll discuss more about layouts and encoders in upcoming posts on logback configuration.

Using Logback

We will start with a simple application that uses Logback for logging. To start with, we need the logback dependencies in our project. Out of the box, both Spring Boot core and web projects include the logback classic dependencies. This figure shows the logback dependencies included in Spring Boot.

As shown in the figure above, the latest SpringBoot 1.3.3REALESE version as of writing this post uses Logback classic 1.1.5.

If you want to use different Logback and SLF4J versions or if you are not using SpringBoot, define their dependencies in your Maven POM, like this.

1

2

3

4

5

6

7

8

9

10

11

12

...

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId>

<version>1.7.21</version>

</dependency>

<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-classic</artifactId>

<version>1.1.7</version>

</dependency>

...

In an application, you can retrieve a logger by calling the getLogger() method of the SLF4J LoggerFactory class. There are two overloaded getLogger() methods. One returns a Logger instance named according to the string value passed as parameter. The other returns a Logger instance named corresponding to the class passed as parameter. The recommended strategy is to use the latter one. This is because in a large application with thousands of log statements, you will find it easy to identify the origin of a log message as the log output bears the name of the generating logger. Once you retrieve a Logger, you can call the log methods on it, like this.

LogbackDemo.java

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

packageguru.springframework.blog.logbackoverview;

importorg.slf4j.Logger;

importorg.slf4j.LoggerFactory;

publicclassLogbackDemo{

privatefinalLogger logger=LoggerFactory.getLogger(this.getClass());

publicvoidperformTask(){

logger.info("This is an {} message.","info");

logger.warn("This is a warn message.");

logger.error("This is an error message.");

logger.debug("This is a debug message.");

}

}

In Line 8 of the
LogbackDemo class above, we retrieved a
Logger object with a call to the static
Loggerfactory.getLogger() method. Passing
LogbackDemo.class to
getLogger() instead of
this.getClass() will produce the same result. But I suggest passing
this.getClass() to decouple the statement from a particular class, thereby making it reusable across other logging classes. From Line 10- Line 13 we called the log methods on the retrieved logger. Notice Line 10 that uses parameterized message in the
info() method. You can use such parameterized log messages in the other logging methods too.

To test the preceding logger, we will use JUnit. The test class is this.

LogbackDemoTest.java

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

packageguru.springframework.blog.logbackoverview;

importorg.junit.Test;

importstaticorg.junit.Assert.*;

publicclassLogbackDemoTest{

@Test

publicvoidtestPerformTask()throwsException{

LogbackDemo logBackDemo=newLogbackDemo();

logBackDemo.performTask();

}

}

When you run the test class, the log messages of LogbackDemo are sent to the console.

Summary

In the example of this post, you may have noticed that I did not specify any appender/encoder or layout for the logger. Rather, I relied upon defaults inherited from the logback root logger. By default, the root logger is associated with the console appender and have the DEBUG level, and our logger inherited both. Therefore, debug and higher log messages were sent to the IntelliJ console. However, in enterprise applications its likely you’ll work with external configuration files to use more advanced features of Logback. These configuration files can be XML or Groovy to specify Logback configuration options. In upcoming posts, I’ll discuss using both XML and Groovy external configuration files to help you explore what a powerful logging tool Logback is.