Log4J - Функция SiftingAppender
Я работаю в проекте, который использует Log4J. Одним из требований является создание отдельного файла журнала для каждого потока; это само по себе было странной проблемой, несколько упорядоченной путем создания нового FileAppender "на лету" и прикрепления его к экземпляру Logger.
Logger logger = Logger.getLogger(<thread dependent string>);
FileAppender appender = new FileAppender();
appender.setFile(fileName);
appender.setLayout(new PatternLayout(lp.getPattern()));
appender.setName(<thread dependent string>);
appender.setThreshold(Level.DEBUG);
appender.activateOptions();
logger.addAppender(appender);
Все прошло хорошо, пока мы не поняли, что используем другую библиотеку - Spring Framework v3.0.0 (которые используют Commons Logging) - не играет в мяч с помощью метода выше - данные регистрации Spring "видны" только приложением, инициализированным из файла log4.configuration, но не с помощью среда выполнения создала Appenders.
Итак, вернемся к квадрату.
После некоторого расследования я узнал, что новый и улучшенный LogBack имеет appender - SiftingAppender - что делает именно то, что нам нужно, т.е. ведение журнала уровня потока на независимых файлах.
В настоящий момент переход на LogBack не является вариантом, поэтому, будучи застрявшим с Log4J, как я могу достичь функциональности, подобной SiftingAppender, и поддерживать Spring счастливым?
Примечание: Spring используется только для JdbcTemplate, no IOC; для того, чтобы "зацепить" Spring s Commons Logging to Log4J Я добавил эту строку в файл log4j.properties:
log4j.logger.org.springframework = ОТЛАДКА
в соответствии с инструкциями здесь.
Ответы
Ответ 1
Доступ к LogBack осуществляется через slf4j api. Существует библиотека адаптеров, называемая jcl-over-sjf4j, которая предоставляет интерфейс ведения журнала commons, но делает все протоколирование в API slf4j, который идет напрямую к реализации - LogBack. Если вы используете maven, вот зависимости:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>0.9.18</version>
</dependency>
(и добавьте общий доступ к списку исключений, см. здесь)
Ответ 2
Некоторое время я изо всех сил пытался найти функциональность, подобную SiftingAppender, в log4j (мы не могли переключиться на logback из-за некоторых зависимостей), и в итоге получилось программное решение, которое работает очень хорошо, используя MDC и добавляющие регистраторы во время выполнения
// this can be any thread-specific string
String processID = request.getProcessID();
Logger logger = Logger.getRootLogger();
// append a new file logger if no logger exists for this tag
if(logger.getAppender(processID) == null){
try{
String pattern = "%d{yy/MM/dd HH:mm:ss} %p %c{2}: %m%n";
String logfile = "log/"+processID+".log";
FileAppender fileAppender = new FileAppender(
new PatternLayout(pattern), logfile, true);
fileAppender.setName(processID);
// add a filter so we can ignore any logs from other threads
fileAppender.addFilter(new ProcessIDFilter(processID));
logger.addAppender(fileAppender);
}catch(Exception e){
throw new RuntimeException(e);
}
}
// tag all child threads with this process-id so we can separate out log output
MDC.put("process-id", processID);
//whatever you want to do in the thread
LOG.info("This message will only end up in "+processID+".log!");
MDC.remove("process-id");
Фильтр, добавленный выше, просто проверяет определенный идентификатор процесса:
public class RunIdFilter extends Filter {
private final String runId;
public RunIdFilter(String runId) {
this.runId = runId;
}
@Override
public int decide(LoggingEvent event) {
Object mdc = event.getMDC("run-id");
if (runId.equals(mdc)) {
return Filter.ACCEPT;
}
return Filter.DENY;
}
}
Надеюсь, это немного поможет.
Ответ 3
Мне нравится включать все фасады /re -routers slf4j/whaterverycallthem. Также обратите внимание на "предоставленный" взлом, который удерживает зависимости от вытаскивания в журнал ведения сообщества; ранее я использовал фальшивую пустую библиотеку ведения журнала, называемую версией 99.0-do-not-exist.
Также см. http://blog.springsource.com/2009/12/04/logging-dependencies-in-spring/
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<!-- use provided scope on real JCL instead -->
<!-- <version>99.0-does-not-exist</version> -->
<version>1.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging-api</artifactId>
<!-- use provided scope on real JCL instead -->
<!-- <version>99.0-does-not-exist</version> -->
<version>1.1</version>
<scope>provided</scope>
</dependency>
<!-- the slf4j commons-logging replacement -->
<!-- if any package is using jakarta commons logging this will -->
<!-- re-route it through slf4j. -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${version.slf4j}</version>
</dependency>
<!-- the slf4j log4j replacement. -->
<!-- if any package is using log4j this will re-route -->
<!-- it through slf4j. -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>${version.slf4j}</version>
</dependency>
<!-- the slf4j java.util.logging replacement. -->
<!-- if any package is using java.util.logging this will re-route -->
<!-- it through slf4j. -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>${version.slf4j}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${version.slf4j}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${version.logback}</version>
</dependency>
</dependencies>
<properties>
<version.logback>0.9.15</version.logback>
<version.slf4j>1.5.8</version.slf4j>
</properties>
Ответ 4
Вы посмотрели на log4j.NDC и MDC? Это, по крайней мере, позволяет помечать данные, зависящие от потока, для ведения журнала. Не совсем то, что вы просите, но может быть полезно. Здесь обсуждается здесь.
Ответ 5
В Log4j2 теперь мы можем использовать RoutingAppender:
RoutingAppender оценивает LogEvents и затем перенаправляет их в подчиненный Appender. Целевой Appender может быть ранее настроенным приложением и может ссылаться на его имя, или Appender может быть динамически создан по мере необходимости.
Из FAQ:
Как динамически записывать отдельные файлы журналов?Посмотрите на RoutingAppender. Вы можете определить несколько маршрутов в конфигурации и поместить значения на карте ThreadContext, которые определяют, какие файлы последующих событий в этом потоке регистрируются.