Одна из новых функций 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.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}
в имени файла приводит к тому, что просеивающий аппендер создает одного дочернего аппендера для каждого отдельного значения этого свойства (то есть для разных имен пользователей). Запуск вашего веб-приложения с этой конфигурацией быстро создаст несколько файлов журнала, таких как 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 , поток ввода-вывода и т. Д., Это может быть жизненно важной альтернативой.