Одна из новых функций Logback — SiftingAppender ( JavaDoc ). Короче говоря, это прокси-приложение, которое создает одного дочернего приложения для каждого уникального значения данного свойства среды выполнения. Обычно это свойство взято из MDC . Вот пример, основанный на официальной документации, указанной выше:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?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, я склонен использовать фильтр сервлетов с помощью SecurityContextHolder :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
import javax.servlet._import org.slf4j.MDCimport org.springframework.security.core.context.SecurityContextHolderimport 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} в имени файла приводит к тому, что просеивающий аппендер создает одного дочернего аппендера для каждого отдельного значения этого свойства (то есть для разных имен пользователей). Запуск вашего веб-приложения с этой конфигурацией быстро создаст несколько файлов журнала, таких как user-alice.log , user-bob.log и user-unknown.log user-alice.log , если свойство MDC не установлено. Другой вариант использования — это использование имени потока, а не свойства MDC. К сожалению, это не встроено, но может быть легко подключено с помощью пользовательского Discriminator а не MDCBasedDiscriminator по умолчанию:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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 использовать наш собственный дискриминатор:
|
01
02
03
04
05
06
07
08
09
10
11
|
<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 , поток ввода-вывода и т. Д., Это может быть жизненно важной альтернативой.