Logback File Appender Example
Operating under the assumption that you’ve stumbled onto this article because you’re a programmer and trying to solve a problem as painlessly as possible, I’m going to keep this direct and to the point. This post in no way delves completely into the mysteries of Logback, logging filters, or appenders – for that you’re going to have to read the manual. Presented here is a quick solution to the problem: how do I get Logback to send messages of one type to a particular destination while sending messages of a different type to another destination?
Here’s how, from start to finish:
1. Create a Simple Program that Logs Stuff
Logback, by default, doesn’t require any sort of configuration file to run. You can create a simple program that logs messages to System.out very easily:
App.java
package com.holiday.jcga1; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class App { public static void main( String[] args ) { Logger logger = LoggerFactory.getLogger("Example App"); logger.info("'sup? I'm your info logger"); logger.debug("hey HEY hey! I'm your debug logger"); } }
Which will result in something like this:
23:20:15.692 [main] INFO Example App - 'sup? I'm your info logger 23:20:15.696 [main] DEBUG Example App - hey HEY hey! I'm your debug logger
Now this is all well and good if your project isn’t logging much – but what happens when the messages get so voluminous that you lose track of important ones? Moreover, what if you need to archive log files for later analysis? With Logback, it’s pretty simple to create multiple repositories for your logging stream.
2. Create Multiple Repositories for Your Logging Stream
To make this happen, we’re going to be updating your logback.xml file. If you don’t have a logback.xml file, go ahead and create one and plunk it in whichever spot in your classpath tickles your pickle. If you’re using Maven to build the project, I highly suggest the src/main/resources
directory. Here is a sample logback.xml file that we’ll use for this example:
logback.xml
<configuration> <timestamp key="byDay" datePattern="yyyyMMdd'T'HHmmss"/> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file> log-${byDay}.txt </file> <append>true</append> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <root level="debug"> <appender-ref ref="FILE" /> <appender-ref ref="STDOUT" /> </root> </configuration>
Go ahead and build/run the program. You should see the same output to System.out as well as a new file added to your project that contains the same logging messages. This because by-in-large, this config file is doing exactly what the default configuration does – format logging messages into something pretty and pipe them to System.out. That’s the role of an appender – it directs logging messages to various places at which they can be printed/stored. Note that in addition to an appender definition for STDOUT, we also have one that pipes messages to a file.
3. Route Logging Messages Based On Level
The last thing we need to do is figure out how to route messages based on level. In this case, we may want to only see INFO messages at the console, but both INFO and DEBUG messages in the logfile. To make that happen, we need to add a filter parameter to the appender definition. Here’s what one looks like:
logback.xml
<configuration> <timestamp key="byDay" datePattern="yyyyMMdd'T'HHmmss"/> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file> log-${byDay}.txt </file> <append>true</append> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <root level="debug"> <appender-ref ref="FILE" /> <appender-ref ref="STDOUT" /> </root> </configuration>
Note the addition of the filter element in the STDOUT appender. It basically stands at the gates of System.out and bellows “You Shall Not Pass!” to any message that is the bearer of anything other than an INFO level message. You can add filters to any of your appenders in this way, or do fancier stuff by other means. As previously mentioned, logback has lots of useful features to handle these kinds of situations. While the contents of this article barely scratch the surface, I do hope it proved helpful in solving your immediate challenge of figuring out how to route logback messages of different logging levels to different places.
Download the Source Code
This was an example about Logback File Appender.
You can download the full source code of this example here : LogbackAppenderExample.zip
I tried this example on Mac High Sierra,
Log file is created but stays empty.
Does anyone have any idea why log file is not populated?
Thanks
where the log file is created? In which directory?
very helpful