Log4J - SiftingAppender-подобная функциональность - PullRequest
10 голосов
/ 18 декабря 2009

Я работаю в проекте, который использует 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 данные журналов «видны» только Appenders, инициализированными из файла log4.configuration, но не созданными во время выполнения Appenders. Итак, вернемся к исходной точке.

После некоторого исследования я обнаружил, что у нового и улучшенного LogBack есть аппендер - SiftingAppender - который делает именно то, что нам нужно, то есть ведение журнала на уровне потоков в независимых файлах.

В настоящее время переход на LogBack не является вариантом, поэтому, застряв в Log4J, как я могу достичь функциональности, подобной SiftingAppender, и сохранить Spring также счастливым?

Примечание: Spring используется только для JdbcTemplate функциональности, без IOC; для того, чтобы «зацепить» регистрацию Commons Spring в Log4J, я добавил эту строку в файл log4j.properties:

log4j.logger.org.springframework = DEBUG

как указано здесь .

Ответы [ 5 ]

3 голосов
/ 18 июня 2013

Я некоторое время пытался найти функциональность, аналогичную 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 голосов
/ 20 декабря 2009

Доступ к LogBack осуществляется через slf4j api . Существует библиотека адаптеров под названием jcl-over-sjf4j , которая предоставляет интерфейс ведения журнала общего достояния, но осуществляет всю регистрацию в 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>

(и добавление регистрации общего доступа в список исключений см. здесь )

1 голос
/ 04 сентября 2016

В Log4j2 теперь мы можем использовать RoutingAppender :

RoutingAppender оценивает LogEvents и затем направляет их подчиненному Appender. Целевым Appender может быть ранее настроенный appender, на который может ссылаться его имя, или Appender может создаваться динамически по мере необходимости.

Из их FAQ :

Как динамически записывать в отдельные файлы журнала? Посмотрите на RoutingAppender. Вы можете определить несколько маршрутов в конфигурации и поместить значения в карту ThreadContext, которые определяют, в какой файл журнала регистрируются последующие события в этом потоке.

0 голосов
/ 24 декабря 2009

вы смотрели на log4j.NDC и MDC? Это, по крайней мере, позволяет вам пометить специфичные для потока данные в журнале. Не совсем то, что вы просите, но может быть полезно. Здесь обсуждается .

0 голосов
/ 22 декабря 2009

Мне нравится включать все фасады / ре-маршрутизаторы / все, что вам нужно, в slf4j. Также обратите внимание на «предоставленный» хак, который не дает зависимостям вытягивать в общем журнале; ранее я использовал фальшивую пустую библиотеку регистрации общих пользователей, называемую version-99.0-has-not-there.

Также см. 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>
...