2009-12-18 9 views
10

Trabajo en un proyecto que usa Log4J. Uno de los requisitos es crear un archivo de registro separado para cada hilo; esto en sí mismo era un problema extraño, algo ordenado al crear un nuevo FileAppender sobre la marcha y adjuntarlo a la instancia de Logger.Log4J - Funcionalidad similar a SiftingAppender

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

Todo fue bien hasta que nos dimos cuenta de que otra biblioteca que utilizamos - Spring Framework v3.0.0 (que usar Commons Logging) - no jugar a la pelota con la técnica anterior - el registro de datos de Primavera es “vista” solamente por appenders inicializado desde el archivo log4.configuration pero no por los Appenders creados en tiempo de ejecución. Entonces, volviendo al punto uno.

Después de algunas investigaciones, descubrí que la nueva y mejorada LogBack tiene un appender - SiftingAppender - que hace exactamente lo que necesitamos, es decir, el registro de nivel de subprocesos en archivos independientes.

Por el momento, pasar a LogBack no es una opción, entonces, al estar atascado con Log4J, ¿cómo puedo lograr la funcionalidad de SiftingAppender y también mantener a Spring feliz?

Nota: Spring solo se utiliza para la funcionalidad JdbcTemplate, no IOC; con el fin de “gancho” Comunes de la primavera de registro que puede Log4J he añadido esta línea en el archivo log4j.properties:

log4j.logger.org.springframework = DEBUG

según las instrucciones here.

+1

Si solo está utilizando Spring para la funcionalidad de jdbc, entonces quizás quiera considerar usar algo como apache commons-dbutils. –

Respuesta

3

LogBack se accede a través del slf4j api. Hay una biblioteca de adaptadores llamada jcl-over-sjf4j que expone la interfaz de registro de los recursos comunes pero hace todo el registro a la API slf4j, que va directamente a la implementación: LogBack. Si está utilizando Maven, aquí están las dependencias:

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

(y añadir el commons-logging a la lista de exclusión, ver here)

0

me gusta incluir todas las fachadas SLF4J/re-routers/whateveryoucallthem. También tenga en cuenta el truco "provisto", que evita que las dependencias extraigan el registro de commons; anteriormente estaba usando una biblioteca falsa de registro de recursos comunes vacía llamada version-99.0-does-not-exists.

Véase también 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

has mirado log4j.NDC y MDC? Esto al menos le permite etiquetar datos específicos de la secuencia para su registro. No es exactamente lo que estás preguntando, pero podría ser útil. Hay una discusión here.

3

Luché por un tiempo para encontrar la funcionalidad de SiftingAppender en log4j (no pudimos cambiar a logback debido a algunas dependencias), y terminamos con una solución programática que funciona bastante bien, usando un MDC y agregando loggers en tiempo de ejecución:

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

El filtro anexa anteriormente simplemente comprueba para un ID de proceso específico:

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 esto ayuda un poco.

0

En Log4j2, ahora podemos utilizar RoutingAppender:

El RoutingAppender evalúa LogEvents y luego las envía a un Appender de subordinado. El Appender de destino puede ser un apéndice previamente configurado y puede ser referenciado por su nombre o el Appender se puede crear dinámicamente según sea necesario.

de sus FAQ:

¿Cómo escribo de forma dinámica para separar los archivos de registro? Mire en el enrutador de enrutamiento. Puede definir múltiples rutas en la configuración y poner valores en el mapa ThreadContext que determinan a qué archivo de registro se suman los eventos subsiguientes en este hilo.

Cuestiones relacionadas