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

JavaLoggingLogback

Java Problem Overview


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>

Java Solutions


Solution 1 - Java

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>

Solution 2 - Java

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 3 - Java

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>

Solution 4 - Java

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>

Solution 5 - Java

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.

Solution 6 - Java

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>  

Solution 7 - Java

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!

Solution 8 - Java

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.

Solution 9 - Java

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>

Solution 10 - Java

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>

Solution 11 - Java

<configuration scan="true" scanPeriod="60 seconds">
 <appender name="A1" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${storm.log.dir}/${logfile.name}</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>${storm.log.dir}/${logfile.name}.%i</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>9</maxIndex>
    </rollingPolicy>

    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>100MB</maxFileSize>
    </triggeringPolicy>

    <encoder>
      <pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSZZ} %c{1} [%p] %m%n</pattern>
    </encoder>
 </appender>

 <appender name="ACCESS" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${storm.log.dir}/access.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>${storm.log.dir}/access.log.%i</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>9</maxIndex>
    </rollingPolicy>

    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>100MB</maxFileSize>
    </triggeringPolicy>

    <encoder>
      <pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSZZ} %c{1} [%p] %m%n</pattern>
    </encoder>
  </appender>

  <appender name="METRICS" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${storm.log.dir}/metrics.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>${storm.log.dir}/logs/metrics.log.%i</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>9</maxIndex>
    </rollingPolicy>

    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>2MB</maxFileSize>
    </triggeringPolicy>

    <encoder>
      <pattern>%d %-8r %m%n</pattern>
    </encoder>
  </appender>

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

  <logger name="backtype.storm.security.auth.authorizer" additivity="false">
    <level value="INFO" />
    <appender-ref ref="ACCESS" />
  </logger>

  <logger name="backtype.storm.metric.LoggingMetricsConsumer" additivity="false" >
    <level value="INFO"/>
    <appender-ref ref="METRICS"/>
  </logger>

</configuration>

So here is the logback file in which I am not printing backtype.storm.metric.LoggingMetricsConsumer info level if i say additivity = "true" then for for all classes in backtype.* this rule will be applied

Solution 12 - Java

Example of how to output colored messages of level "INFO" or higher to console and messages of level "WARN" or higher to file.

Your logback.xml file:

<?xml version="1.0" encoding="UTF-8"?>

<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
   
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>

            <!--output messages of exact level only-->
            <!--<onMatch>ACCEPT</onMatch>-->
            <!--<onMismatch>DENY</onMismatch>-->
        </filter>
        <encoder>
            <pattern>%d{yyyy-MMM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg %n
            </pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>myfile.log</file>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>WARN</level>
        </filter>
        <append>true</append>
        <encoder>
            <pattern>%d{yyyy-MMM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{15} - %msg%n</pattern>
        </encoder>
    </appender>

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDerek MaharView Question on Stackoverflow
Solution 1 - JavaJoeView Answer on Stackoverflow
Solution 2 - JavaUriah CarpenterView Answer on Stackoverflow
Solution 3 - JavaSébastien HelbertView Answer on Stackoverflow
Solution 4 - JavaDean HillerView Answer on Stackoverflow
Solution 5 - JavaX. Wo SatukView Answer on Stackoverflow
Solution 6 - Javasoulfly1983View Answer on Stackoverflow
Solution 7 - JavaAndras BokorView Answer on Stackoverflow
Solution 8 - Javamike rodentView Answer on Stackoverflow
Solution 9 - JavaEldar AgalarovView Answer on Stackoverflow
Solution 10 - JavaRupesh KumarView Answer on Stackoverflow
Solution 11 - JavaRahul SaxenaView Answer on Stackoverflow
Solution 12 - JavaKirill ChView Answer on Stackoverflow