, ,

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

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.

Default Logging Output

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.

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.

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.

Logging Level Configuration in YAML with Spring Boot
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.

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

Logging to File and Console with updated YAML configuration

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.

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.
Logging Output of dev Profile

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.
Run Debug Configurations Dialog Box

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

Logging Output of production Profile

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.
Profile-specific YAML Configuration Files for Spring Boot

Following are the code for each of the configuration files.

application-default.yml

application-dev.yml

application-production.yml

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).

2
Share
, ,

Logback makes an excellent logging framework for enterprise applications – it’s fast, have simple but powerful configuration options, and comes with a small memory footprint. I introduced logback in my introductory post, Logback Introduction: An Enterprise Logging Framework. In a series of posts on Logback, I’ve also discussed how to configure logback using XML and Groovy. The posts are available as Logback Configuration: using XML and Logback Configuration: using Groovy.

In this post, I’ll discuss how to use Logback with Spring Boot. While there are a number of logging options for Java, the Spring Boot chose to use Logback for the default logger. Like many things in Spring Boot, Logback by default gets configured with sensible defaults. Out of the box, Spring Boot makes Logback easy to use.

Creating Loggers

In a previous post, I wrote about creating a web application using Spring Boot. We’ll configure logback for this application. The application contains a controller, IndexController to which we’ll add logging code. The code of IndexController is this.

Let’s add a SpringLoggingHelper class with logging code to the application. Although this class doesn’t do anything except emitting logging statements, it will help us understand configuring logging across different packages. Here is the code of SpringLoggingHelper :

In both the classes above, we wrote logging code against the SLF4J API. SLF4J is a façade for commonly used logging frameworks, such as Java Util Logging, Log4J 2, and Logback. By writing against SLF4J, our code remains decoupled from Logback, thus providing us the flexibility to plug-in a different logging framework, if required later.

If you are wondering about SLF4J and Logback dependencies, you don’t need to specify any. Spring Boot contains them too. Assuming you’re using Maven or Gradle to manage you Spring Boot project, the necessary dependencies are part of the dependencies under Spring Boot.

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.

Logging Output with Default Configuration in Spring Boot

We haven’t written any configuration for Logback. The output of both the IndexController and SpringLoggingHelper classes are from the logback root logger. Notice that the debug messages are 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.

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

Here you can see the 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.

Free Spring Framework Tutorial
Check out my FREE Introduction to Spring Course

Configuration via Spring Boot’s application.properties File

In a Spring Boot application, you can externalize configuration to work with the same application code in different environments. The application.properties file is likely the most popular of several different ways to externalize Spring Boot configuration properties. In the default structure of a Spring Boot web application, you can locate the application.properties file under the Resources folder. In the application.properties file, you can define log levels of Spring Boot, application loggers, Hibernate, Thymeleaf, and more. You can also define a log file to write log messages to in addition to the console.

Here is an example of an application.properties file with logging configurations.

Note: There is also a logging.path property to specify a path for a logging file. If you use it, Spring Boot creates a spring.log file in the specified path. However, you cannot specify both the logging.file and logging.path properties together. If done, Spring Boot will ignore both.

When you run the main class now and access the application, log messages from IndexController and SpringLoggingHelper are logged to the console and the logs/spring-boot-logging.log file.
Spring Boot Logging Output with application.properties

In the output, notice that debug and higher level messages of IndexController got logged to the console and file. This was because in the application.properties file, we specified DEBUG as the log level for the guru.springframework.controllers package that IndexController is part of. Since we did not explicitly configure the SpringLoggingHelper class, the default configuration of base.xml file was used. Therefore, only INFO and higher level messages of SpringLoggingHelper got logged.

You can see how simple this is to use when you need to get more detailed log messages for a specific class or package.

Logback Configuration through an External File

Logback configuration through application.properties file will be sufficient for many Spring Boot applications. However, large enterprise applications are likely to have far more complex logging requirements. As I mentioned earlier, Logback supports advanced logging configurations through XML and Groovy configuration files.

In a Spring Boot application, you can specify a Logback XML configuration file as logback.xml or logback-spring.xml in the project classpath. The Spring Boot team however recommends using the -spring variant for your logging configuration,   logback-spring.xml is preferred over  logback.xml. If you use the standard logback.xml configuration, Spring Boot may not be able to completely control log initialization.

Here is the code of the logback-spring.xml file.

In the configuration code above, we included the base.xml file in Line 3, notice that we didn’t configured any appenders. Rather we relied on the CONSOLE and FILE appenders which are provided by Spring Boot.

With the updated Spring Boot Logback configuration, our logging output now looks like this:

Logging Output of Spring Boot XML Configuration

Note: Spring Boot expects the logback-spring.xml configuration file to be on the classpath. However, you can store it in a different location and point to it using the logging.config property in application.properties.

Spring Boot Profiles in Logging

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 logging overhead while running in production. While logging is very efficient, there is still a cost.

Spring Boot has addressed these requirements by extending Spring profiles for logback configuration with the <springProfile> element. Using this element in your logback-spring.xml file, you can optionally include or exclude sections of logging configuration based on the active Spring profile.

Note: Support for <springProfile> in logback configuration is available from SpringBoot 1.3.0.M2 milestone onwards.

Here is an XML example to configure Logback using active Spring profiles.

In the configuration code above, for the dev and staging profiles, we configured the guru.springframework.controllers logger to log DEBUG and higher level messages to the console. For the production profile, we configured the same logger to log WARN and higher level messages to a file.

To pass a profile to the application, run the application with the -Dspring.profiles.active= JVM argument.

For local development, in IntelliJ, select Run-> Edit Configurations, and set the JVM argument in the Run/Debug Configurations dialog box, like this.

setting active profiles for logging in IntelliJ

Now, when we run the application with the dev profile, we will see the following log output.

Logging Output with Spring Active Profiles

In the output above, observe the logging output of IndexController. DEBUG and higher log messages got logged to console based on the configuration of the dev profile. You can restart the application with the production profile to ensure that WARN and higher log messages gets logged to the file.

Conditional Processing of Configuration File

Logback supports conditional processing of configuration files with the help of the Janino library. You can use <if>, <then> and <else> elements in a configuration file to target several environments. To perform conditional processing, add the Janino dependency to your Maven POM, like this.

The complete logback-spring.xml file with conditional processing logic is this.

In the code above, we specified a condition in the <if> element to check whether the current active profile contains dev. If the condition evaluates to true, the configuration code within the <then> element executes. In the <then> element, we configured guru.springframework.helpers to log DEBUG and higher messages to console. We used the <else> element to configure the logger to log WARN and higher messages to the log file. The <else> element executes for any profiles other than dev.

When you run the application with the production profile and access it, both loggers will log WARN and higher messages to the log file, similar to this.
Logging Output with production Profile

For the dev profile, both loggers will log DEBUG and higher messages to the console, similar to this.
Logging Output for dev Profile

Logback Auto-Scan Issue with Spring Boot

In a logback-spring.xml file, you can enable auto-scan of the configuration by setting the scan="true" attribute. With auto-scan enabled, Logback scans for changes in the configuration file. For any changes, Logback automatically reconfigure itself with them. You can specify a scanning period by passing a time period to the scanPeriod attribute, with a value specified in units of milliseconds, seconds, minutes or hours.
For example, this code tells Logback to scan logback-spring.xml after every 10 seconds.

One limitation of Spring Boot Logback is that with springProfile and springProperty, setting auto-scan results in error.

The error occurs because of incompatibility issues. Spring Boot uses the JoranConfigurator subclass to support springProfile and springProperty. Unfortunately, Logback’s ReconfigureOnChangeTask doesn’t provide a hook to plug it in.

free spring framework tutorial
Checkout my FREE Introduction to Spring Course!

Conclusion

The popularity of Logback is trending in the open source community. A number of popular open source projects use Logback for their logging needs. Apache Camel, Gradle, and SonarQube are just a few examples.

Logback is clearly has the capabilities to handle the needs of logging in a complex enterprise application. So, it’s no wonder the Spring Boot team selected Logback for the default logging implementation. As you’ve seen in this post, the Spring Boot team has provided a nice integration with Logback. Out of the box, Logback is ready to use with Spring Boot. In this post, you’ve seen how easy it is to configure Logback in Spring Boot as your logging requirements evolve.

12
Share
, ,

Logback is designed to be faster and have a smaller memory footprint than the other logging frameworks around. If you are new to Logback, you should checkout my introductory post on Logback: Logback Introduction: An Enterprise Logging Framework.

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.

LogbackConfigGroovy.java

Our test class uses JUnit to unit test the LogbackConfigGroovy class.

LogbackConfigGroovyTest.java

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.

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.

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.

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.

The code to configure the loggers is this.

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.
Output of Console and File Appender

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.

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.
Output of Rolling File Appender

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.

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.

The complete code of the logback.groovy file is this.

Logback.groovy

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.

0
Share
,

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.

LogbackConfigXml.java

Our test class uses JUnit to unit test the preceding LogbackConfigXml class.

LogbackConfigXmlTest.java

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.

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.

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.

The configuration code above declares two properties, LOG_PATH and LOG_ARCHIVE whose values represent the paths to store log files and archived log files respectively.

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.

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:

In the Logback configuration code above:

  • 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.

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.

Console and File Appender Output

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

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.

The code to configure a rolling file appender is this.

In the code above:

  • 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.

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.
Rolling File Appender Output from Logback

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.

Logback Additivity

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

The console output on running the test class is this.
Console Appender Additivity Output

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.

With additivity set to false, Logback will not use Console-Appender of root to log messages.

Although additivity is a convenient feature and is not intended to trip new users, it can be somewhat confusing. I suggest reviewing the Logback manual on the subject.

The complete code of the Logback.xml file is this.

Logback.xml

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.

0
Share

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.

Interaction of a Java application with the Logback logging framework

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.
Logger Hierarchy

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.

Log Levels

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.

Logback Dependencies via Sprint 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.

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

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

When you run the test class, the log messages of LogbackDemo are sent to the console.
Log Output in 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.

0
Share