Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I configure Logback to log different levels for a logger to different destinations?

How can I configure Logback to log different levels for a logger to different destinations?

For example, given the following Logback configuration, will Logback record INFO messages to STDOUT and ERROR messages to STDERR?

(Note that this example is a variation of example logback-examples/src/main/java/chapters/configuration/sample4.xml shown in Chapter 3: Logback Configuration).

<configuration>
  <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="STDERR"
   class="ch.qos.logback.core.ConsoleAppender">
   <encoder>
     <pattern>
        %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
      </pattern>
    </encoder>
    <target>System.err</target>
  </appender>
  <!-- What is the effective level of "chapters.configuration"? -->
  <logger name="chapters.configuration" level="INFO" additivity="false">
    <appender-ref ref="STDOUT" />
  </logger>
  <logger name="chapters.configuration" level="ERROR" additivity="false">
    <appender-ref ref="STDERR" />
  </logger>

  <!-- turn OFF all logging (children can override) -->
  <root level="OFF">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
like image 249
Derek Mahar Avatar asked Apr 13 '11 17:04

Derek Mahar


People also ask

How do I specify the path for Logback XML?

You may specify the location of the default configuration file with a system property named "logback. configurationFile". The value of this property can be a URL, a resource on the class path or a path to a file external to the application.

What is Logback configuration?

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.


10 Answers

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>
like image 51
Joe Avatar answered Nov 08 '22 16:11

Joe


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;
        }
    }

}
like image 38
Uriah Carpenter Avatar answered Nov 08 '22 15:11

Uriah Carpenter


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>
like image 28
Sébastien Helbert Avatar answered Nov 08 '22 15:11

Sébastien Helbert


The simplest solution is to use ThresholdFilter on the appenders:

    <appender name="..." class="...">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>

Full example:

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <encoder>
            <pattern>%d %-5level: %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <target>System.err</target>
        <encoder>
            <pattern>%d %-5level: %msg%n</pattern>
        </encoder>
    </appender>

    <root>
        <appender-ref ref="STDOUT" />
        <appender-ref ref="STDERR" />
    </root>
</configuration>

Update: As Mike pointed out in the comment, messages with ERROR level are printed here both to STDOUT and STDERR. Not sure what was the OP's intent, though. You can try Mike's answer if this is not what you wanted.

like image 43
X. Wo Satuk Avatar answered Nov 08 '22 17:11

X. Wo Satuk


okay, here is my favorite xml way of doing it. I do this for the eclipse version so I can

  • click on stuff to take me to the log statements and
  • see info and below in black and warn/severe in red

and for some reason SO is not showing this all properly but most seems to be there...

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

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">      
          <evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator"> 
            <expression>
               e.level.toInt() &lt;= INFO.toInt()
            </expression>
          </evaluator>
          <OnMismatch>DENY</OnMismatch>
          <OnMatch>NEUTRAL</OnMatch>
        </filter>

        <encoder>
            <pattern>%date{ISO8601} %X{sessionid}-%X{user} %caller{1} %-4level: %message%n</pattern>
        </encoder>
    </appender>

    <appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> 
            <level>warn</level>
        </filter>

        <encoder>
            <pattern>%date{ISO8601} %X{sessionid}-%X{user} %caller{1} %-4level: %message%n</pattern>
        </encoder>
        <target>System.err</target>
    </appender>

    <root>
        <level value="INFO" />
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="STDERR"/>
    </root>
</configuration>
like image 44
Dean Hiller Avatar answered Nov 08 '22 17:11

Dean Hiller


This is the configuration that I use, which works fine, it is based on XML + JaninoEventEvaluator (requires the Janino library to be added to Classpath)

<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%date | [%-5level] in [%file:%line] - %msg %n</pattern>
    </encoder>
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
        <evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
            <expression>
                level &lt;= INFO
            </expression>
        </evaluator>
        <OnMismatch>DENY</OnMismatch>
        <OnMatch>NEUTRAL</OnMatch>
    </filter>
</appender>
<appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
    <target>System.err</target>
    <encoder>
        <pattern>%date | [%-5level] in [%file:%line] - %msg %n</pattern>
    </encoder>
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>WARN</level>
    </filter>
</appender>

<root level="DEBUG">
    <appender-ref ref="STDOUT" />
    <appender-ref ref="STDERR" />
</root>
</configuration>  
like image 30
soulfly1983 Avatar answered Nov 08 '22 15:11

soulfly1983


I use logback.groovy to configure my logback but you can do it with xml config as well:

import static ch.qos.logback.classic.Level.*
import static ch.qos.logback.core.spi.FilterReply.DENY
import static ch.qos.logback.core.spi.FilterReply.NEUTRAL
import ch.qos.logback.classic.boolex.GEventEvaluator
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.core.filter.EvaluatorFilter

def patternExpression = "%date{ISO8601} [%5level] %msg%n"

appender("STDERR", ConsoleAppender) {
    filter(EvaluatorFilter) {
      evaluator(GEventEvaluator) {
        expression = 'e.level.toInt() >= WARN.toInt()'
      }
      onMatch = NEUTRAL
      onMismatch = DENY
    }
    encoder(PatternLayoutEncoder) {
      pattern = patternExpression
    }
    target = "System.err"
  }

appender("STDOUT", ConsoleAppender) {
    filter(EvaluatorFilter) {
      evaluator(GEventEvaluator) {
        expression = 'e.level.toInt() < WARN.toInt()'
      }
      onMismatch = DENY
      onMatch = NEUTRAL
    }
    encoder(PatternLayoutEncoder) {
      pattern = patternExpression
    }
    target = "System.out"
}

logger("org.hibernate.type", WARN)
logger("org.hibernate", WARN)
logger("org.springframework", WARN)

root(INFO,["STDERR","STDOUT"])

I think to use GEventEvaluator is simplier because there is no need to create filter classes.
I apologize for my English!

like image 30
Andras Bokor Avatar answered Nov 08 '22 16:11

Andras Bokor


I take no credit for this answer, as it's merely a combination of the best two answers above: that of X. Wo Satuk and that of Sébastien Helbert: ThresholdFilter is lovely but you can't configure it to have an upper level as well as a lower level*, but combining it with two LevelFilters set to "DENY" WARN and ERROR works a treat.

Very important: do not forget the <target>System.err</target> tag in the STDERR appender: my omission of it had me frustrated for a few minutes.

<configuration>
    <timestamp key="byDay" datePattern="yyyyMMdd'T'HHmmss" />
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </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>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M\(%line\)
                - %msg%n
            </pattern>
        </encoder>
    </appender>

    <appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>WARN</level>
        </filter>
        <target>System.err</target>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M\(%line\)
                - %msg%n
            </pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="STDERR" />
    </root>
</configuration>

* it does however have a method decide in the API but I haven't a clue how you'd use it in this context.

like image 33
mike rodent Avatar answered Nov 08 '22 17:11

mike rodent


Try this. You can just use built-in ThresholdFilter and LevelFilter. No need to create your own filters programmically. In this example WARN and ERROR levels are logged to System.err and rest to System.out:

<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
    <!-- deny ERROR level -->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>ERROR</level>
        <onMatch>DENY</onMatch>
    </filter>
    <!-- deny WARN level -->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>WARN</level>
        <onMatch>DENY</onMatch>
    </filter>
    <target>System.out</target>
    <immediateFlush>true</immediateFlush>
    <encoder>
        <charset>utf-8</charset>
        <pattern>${msg_pattern}</pattern>
    </encoder>
</appender>

<appender name="stderr" class="ch.qos.logback.core.ConsoleAppender">
    <!-- deny all events with a level below WARN, that is INFO, DEBUG and TRACE -->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>WARN</level>
    </filter>
    <target>System.err</target>
    <immediateFlush>true</immediateFlush>
    <encoder>
        <charset>utf-8</charset>
        <pattern>${msg_pattern}</pattern>
    </encoder>
</appender>   

<root level="WARN">
    <appender-ref ref="stderr"/>
</root>

<root level="TRACE">
    <appender-ref ref="stdout"/>
</root>

like image 39
Eldar Agalarov Avatar answered Nov 08 '22 17:11

Eldar Agalarov


No programming needed. configuration make your life easy.

Below is the configuration which logs different level of logs to different files

<property name="DEV_HOME" value="./logs" />

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>INFO</level>
    </filter>
    <layout class="ch.qos.logback.classic.PatternLayout">
        <Pattern>
            %d{yyyy-MM-dd HH:mm:ss} %-5level - %msg%n
        </Pattern>
    </layout>
</appender>

<appender name="FILE-ERROR"
    class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${DEV_HOME}/app-error.log</file>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <Pattern>
            %d{yyyy-MM-dd HH:mm:ss} %-5level - %msg%n
        </Pattern>
    </encoder>

    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!-- rollover daily -->
        <fileNamePattern>${DEV_HOME}/archived/app-error.%d{yyyy-MM-dd}.%i.log
        </fileNamePattern>
        <timeBasedFileNamingAndTriggeringPolicy
            class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>10MB</maxFileSize>
        </timeBasedFileNamingAndTriggeringPolicy>
    </rollingPolicy>

    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>ERROR</level>
        <!--output messages of exact level only -->
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
</appender>
<appender name="FILE-INFO"
    class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${DEV_HOME}/app-info.log</file>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <Pattern>
            %d{yyyy-MM-dd HH:mm:ss} %-5level - %msg%n
        </Pattern>
    </encoder>

    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!-- rollover daily -->
        <fileNamePattern>${DEV_HOME}/archived/app-info.%d{yyyy-MM-dd}.%i.log
        </fileNamePattern>
        <timeBasedFileNamingAndTriggeringPolicy
            class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>10MB</maxFileSize>
        </timeBasedFileNamingAndTriggeringPolicy>
    </rollingPolicy>


    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>INFO</level>
        <!--output messages of exact level only -->
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
</appender>


<appender name="FILE-DEBUG"
    class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${DEV_HOME}/app-debug.log</file>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <Pattern>
            %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
        </Pattern>
    </encoder>

    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!-- rollover daily -->
        <fileNamePattern>${DEV_HOME}/archived/app-debug.%d{yyyy-MM-dd}.%i.log
        </fileNamePattern>
        <timeBasedFileNamingAndTriggeringPolicy
            class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>10MB</maxFileSize>
        </timeBasedFileNamingAndTriggeringPolicy>
    </rollingPolicy>

    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>DEBUG</level>
        <!--output messages of exact level only -->
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
</appender>

<appender name="FILE-ALL"
    class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${DEV_HOME}/app.log</file>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <Pattern>
            %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
        </Pattern>
    </encoder>

    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!-- rollover daily -->
        <fileNamePattern>${DEV_HOME}/archived/app.%d{yyyy-MM-dd}.%i.log
        </fileNamePattern>
        <timeBasedFileNamingAndTriggeringPolicy
            class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>10MB</maxFileSize>
        </timeBasedFileNamingAndTriggeringPolicy>
    </rollingPolicy>
</appender>

<logger name="com.abc.xyz" level="DEBUG" additivity="true">
    <appender-ref ref="FILE-DEBUG" />
    <appender-ref ref="FILE-INFO" />
    <appender-ref ref="FILE-ERROR" />
    <appender-ref ref="FILE-ALL" />
</logger>

<root level="INFO">
    <appender-ref ref="STDOUT" />
</root>

like image 44
Rupesh Kumar Avatar answered Nov 08 '22 16:11

Rupesh Kumar