2009-12-18 5 views
10

Ich arbeite in einem Projekt, das Log4J verwendet. Eine der Anforderungen besteht darin, für jeden Thread eine separate Protokolldatei zu erstellen. Das war ein seltsames Problem, etwas sortiert, indem ein neuer FileAppender im laufenden Betrieb erstellt und an die Logger-Instanz angehängt wurde.Log4J - SiftingAppender-ähnliche Funktionalität

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); 

Alles ging gut, bis wir, dass eine andere Bibliothek realisiert wir verwenden - Spring Framework v3.0.0 (die Verwendung Commons Logging) - Ball nicht mit der Technik, die oben spielen - das Frühlingsprotokolldaten nur initialisiert durch Appen „gesehen“ wird aus der log4.configuration-Datei, aber nicht von der Runtime erstellt Appenders. Also, zurück zu Platz eins.

Nach einigen Untersuchungen fand ich heraus, dass die neue und verbesserte LogBack einen Appender hat - SiftingAppender - die genau das tut, was wir brauchen, das Thread-Level-Protokollierung auf unabhängige Dateien.

Im Moment ist der Wechsel zu LogBack keine Option. Wie kann ich also mit SiftingAppender-ähnlichen Funktionen arbeiten und den Frühling glücklich machen?

Hinweis: Feder wird nur für JdbcTemplate Funktionalität verwendet, kein IOC; um Log4J zu „Haken“ Spring Commons Logging habe ich diese Zeile in der Datei log4j.properties:

log4j.logger.org.springframework = DEBUG

wie angewiesen here.

+1

Wenn Sie nur Spring für die jdbc-Funktionalität verwenden, sollten Sie stattdessen etwas wie Apache commons-dbutils verwenden. –

Antwort

3

LogBack wird über die slf4j api zugegriffen. Es gibt eine Adapterbibliothek mit dem Namen jcl-over-sjf4j, die die Commons-Protokollierungsschnittstelle verfügbar macht, aber die gesamte Protokollierung an die API slf4j vornimmt, die direkt zur Implementierung - LogBack - führt. Wenn Sie Maven verwenden, sind hier die Abhängigkeiten:

<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> 

(und fügen Sie die commons-logging auf der Ausschlussliste finden here)

0

Ich mag alle der slf4j Fassaden/Re-Router enthalten/whateveryocallthem. Beachten Sie auch den "bereitgestellten" Hack, der Abhängigkeiten von Commons Logging hält; zuvor benutzte ich eine gefälschte leere Commons-Logging-Bibliothek namens version-99.0-does-not-exist.

Siehe auch 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> 
0

haben Sie bei log4j.NDC und MDC geschaut? Damit können Sie zumindest threadspezifische Daten für Ihre Protokollierung kennzeichnen. Nicht genau, was du fragst, aber könnte nützlich sein. Es gibt eine Diskussion here.

3

Ich kämpfte eine Weile, um SiftingAppender-ähnliche Funktionalität in log4j zu finden (wir konnten wegen einiger Abhängigkeiten nicht zum Logback wechseln), und endete mit einer programmatischen Lösung, die ziemlich gut funktioniert, einen MDC verwendet und Logger anfügt Laufzeit:

// 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"); 

Der Filter oben nur für eine bestimmte Prozess-ID angefügt prüft:

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; 
    } 
} 

Hope this hilft ein wenig.

0

In Log4j2 können wir jetzt RoutingAppender verwenden:

Der RoutingAppender wertet LogEvents und sie leitet dann zu einem untergeordneten Appender. Der Ziel-Appender kann ein Appender sein, der zuvor konfiguriert wurde und kann durch seinen Namen referenziert werden, oder der Appender kann dynamisch wie benötigt erzeugt werden.

Von ihrem FAQ:

Wie schreibe ich Dateien trennen dynamisch anmelden? Schauen Sie sich den RoutingAppender an. Sie können in der Konfiguration mehrere Routen definieren und Werte in die ThreadContext-Map eingeben, die bestimmen, in welcher Protokolldatei nachfolgende Ereignisse in diesem Thread protokolliert werden.