Logback的一项新颖功能是SiftingAppender
( JavaDoc )。 简而言之,它是一个代理附加器,它为给定运行时属性的每个唯一值创建一个子附加器。 通常,此属性来自MDC 。 这是基于上面链接的官方文档的示例:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
<discriminator>
<key>userid</key>
<defaultValue>unknown</defaultValue>
</discriminator>
<sift>
<appender name="FILE-${userid}" class="ch.qos.logback.core.FileAppender">
<file>user-${userid}.log</file>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{HH:mm:ss:SSS} | %-5level | %thread | %logger{20} | %msg%n%rEx</pattern>
</layout>
</appender>
</sift>
</appender>
<root level="ALL">
<appender-ref ref="SIFT" />
</root>
</configuration>
注意, <file>
属性是使用${userid}
属性进行参数化的。 这个财产来自哪里? 它必须放在MDC中。 例如,在使用Spring Security的Web应用程序中,我倾向于在SecurityContextHolder
的帮助下使用servlet过滤器:
import javax.servlet._
import org.slf4j.MDC
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.UserDetails
class UserIdFilter extends Filter
{
def init(filterConfig: FilterConfig) {}
def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
val userid = Option(
SecurityContextHolder.getContext.getAuthentication
).collect{case u: UserDetails => u.getUsername}
MDC.put("userid", userid.orNull)
try {
chain.doFilter(request, response)
} finally {
MDC.remove("userid")
}
}
def destroy() {}
}
只要确保在Spring Security过滤器之后应用此过滤器即可。 但这不是重点。 文件名中${userid}
占位符的存在会导致筛选追加程序为该属性的每个不同值(因此:不同的用户名)创建一个子追加程序。 如果未设置MDC属性,以这种配置运行Web应用程序将快速创建多个日志文件,例如user-alice.log
, user-bob.log
和user-unknown.log
user-bob.log
。 另一个用例是使用线程名称而不是MDC属性。 不幸的是,它不是内置的,但是可以使用自定义Discriminator
而不是默认的MDCBasedDiscriminator
轻松插入:
public class ThreadNameBasedDiscriminator implements Discriminator<ILoggingEvent> {
private static final String KEY = "threadName";
private boolean started;
@Override
public String getDiscriminatingValue(ILoggingEvent iLoggingEvent) {
return Thread.currentThread().getName();
}
@Override
public String getKey() {
return KEY;
}
public void start() {
started = true;
}
public void stop() {
started = false;
}
public boolean isStarted() {
return started;
}
}
现在,我们必须指示logback.xml
使用我们的自定义标识符:
<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
<discriminator class="com.blogspot.nurkiewicz.ThreadNameBasedDiscriminator"/>
<sift>
<appender class="ch.qos.logback.core.FileAppender">
<file>app-${threadName}.log</file>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{HH:mm:ss:SSS} | %-5level | %logger{20} | %msg%n%rEx</pattern>
</layout>
</appender>
</sift>
</appender>
请注意,我们不再将%thread
放入PatternLayout
–不必要,因为线程名是日志文件名的一部分:
-
app-main.log
-
app-http-nio-8080-exec-1.log
-
app-taskScheduler-1
-
app-ForkJoinPool-1-worker-1.log
- ……依此类推
对于服务器应用程序,这可能不是最方便的设置,但是在台式机上,如EDT ,IO线程等专注线程数量有限,这可能是一个重要的选择。