How can I configure Logback to log different levels for a logger to different destinations?
I believe this would be the simplest solution:
<configuration> <contextName>selenium-plugin</contextName> <!-- Logging configuration --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <Target>System.out</Target> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <encoder> <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%level] %msg%n</pattern> </encoder> </appender> <appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender"> <Target>System.err</Target> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <encoder> <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%level] [%thread] %logger{10} [%file:%line] %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="STDOUT"/> <appender-ref ref="STDERR" /> </root></configuration>
Update: For an all configuration based approach using Groovy see Dean Hiller's answer.
--
You can do some interesting things with Logback filters. The below configuration will only print warn and error messages to stderr, and everything else to stdout.
logback.xml
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <target>System.out</target> <filter class="com.foo.StdOutFilter" /> ...</appender><appender name="stderr" class="ch.qos.logback.core.ConsoleAppender"> <target>System.err</target> <filter class="com.foo.ErrOutFilter" /> ...</appender><logger name="mylogger" level="debug"> <appender-ref ref="stdout" /> <appender-ref ref="stderr" /></logger>
com.foo.StdOutFilter
public class StdOutFilter extends ch.qos.logback.core.filter.AbstractMatcherFilter{ @Override public FilterReply decide(Object event) { if (!isStarted()) { return FilterReply.NEUTRAL; } LoggingEvent loggingEvent = (LoggingEvent) event; List<Level> eventsToKeep = Arrays.asList(Level.TRACE, Level.DEBUG, Level.INFO); if (eventsToKeep.contains(loggingEvent.getLevel())) { return FilterReply.NEUTRAL; } else { return FilterReply.DENY; } }}
com.foo.ErrOutFilter
public class ErrOutFilter extends ch.qos.logback.core.filter.AbstractMatcherFilter{ @Override public FilterReply decide(Object event) { if (!isStarted()) { return FilterReply.NEUTRAL; } LoggingEvent loggingEvent = (LoggingEvent) event; List<Level> eventsToKeep = Arrays.asList(Level.WARN, Level.ERROR); if (eventsToKeep.contains(loggingEvent.getLevel())) { return FilterReply.NEUTRAL; } else { return FilterReply.DENY; } }}
Solution based on configuration only, with a ThresoldFilter and LevelFilters to keep things really simple to understand :
<configuration> <appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender"> <target>System.err</target> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>WARN</level> </filter> <encoder> <pattern>%date %level [%thread] %logger %msg%n</pattern> </encoder> </appender> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <target>System.out</target> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>DEBUG</level> <onMatch>ACCEPT</onMatch> </filter> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> </filter> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>TRACE</level> <onMatch>ACCEPT</onMatch> </filter> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>WARN</level> <onMatch>DENY</onMatch> </filter> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>DENY</onMatch> </filter> <encoder> <pattern>%date %level [%thread] %logger %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="STDOUT" /> <appender-ref ref="STDERR" /> </root></configuration>