2011-12-02 43 views
6

Tengo una clase JAVA que inicia varios subprocesos que tienen identificaciones únicas. Cada subproceso debe iniciar sesión en un archivo de registro único, con el nombre de ID.log.¿Cómo registrar múltiples hilos en diferentes archivos de registro?

Porque sólo obtener el ID único en tiempo de ejecución, tengo que configurar Log4J programáticamente:

// Get the jobID 
myJobID = aJobID; 
// Initialize the logger 
myLogger = Logger.getLogger(myJobID); 
FileAppender myFileAppender; 
try 
{ 
    myFileAppender = new FileAppender(new SimpleLayout(), myJobID + ".log", false); 
    BasicConfigurator.resetConfiguration(); 
    BasicConfigurator.configure(myFileAppender); 
} catch (IOException e1) { 
// TODO Auto-generated catch block 
    e1.printStackTrace(); 
} 

Ahora bien, esto funciona bien si comienzo a los trabajos de forma secuencial - pero cuando comienzo a 2 hilos (de la misma clase) simultáneamente, ambos registros se crean pero los registros se mezclan: el segundo hilo se registra en el primero y el segundo registro.

¿Cómo puedo asegurarme de que cada instancia sea única? Ya traté de dar un nombre único a cada instancia de registrador, pero no cambió nada.

+0

¿Hay alguna buena razón para querer hacer esta distinción en el código java? La práctica general es mantener el inicio de sesión en un archivo y luego ejecutar un procesamiento posterior para extraer/agregar? Consulte también http://logging.apache.org/log4j/1.2/faq.html#a3.1 – havexz

+1

Duplicado de [¿Cómo configuro las propiedades de log4j para que cada subproceso genere su propio archivo de registro?] (Http://stackoverflow.com/q/1172113/127035) – sudocode

Respuesta

11

Logback ha llamado appender especial SiftingAppender que proporciona una muy buena solución para el tipo de problemas que describes . Un SiftingAppender se puede usar para separar (o tamizar) el registro de acuerdo con cualquier atributo de tiempo de ejecución, incluida la identificación del hilo.

+0

¿Hay alguna implementación de java para hacer lo mismo que este https://www.mkyong.com/logging/logback-different-log-file-for-each-thread/ es decir, tener tamizado Apender para hacer lo mismo que descrito en ese blog. – prime

+0

El enlace que muestra está completamente basado en SiftingAppender de logback. – Ceki

+0

Hmm. Lo tengo. ¿podemos tener múltiples tamizadoAppenders? Básicamente, necesito dos registradores para escribir en dos ubicaciones diferentes. Ambos se usarán en hilos separados, por lo que creo que usaría tamizado. Pero cuando tengo dos no funcionará de todos modos. También estoy teniendo 2 valores de MDC. – prime

0

¿Qué tal si agregamos una variable de contador de instancias estáticas a su clase? Entonces necesitaría un método sincronizado que aumente el contador para cada objeto creado y cree el nombre del archivo de registro a partir de ese valor. Algo como esto: enfoque

class yourClass { 

    private static int cnt = 0; 

    public yourClass(){ 
    ... 
    initLogger(); 
    } 

    private synchronized initLogger(){ 
    yourClass.cnt++; 
    myJobid = yourClass.cnt; 

    //include your logging code here 
    } 
} 
+0

Mi ID de trabajo es un UUID simple. El problema no es el nombre del archivo de registro, sino los apéndices que se mezclan. – Tim

+0

Encontré esto: http://www.manniwood.com/log4j_stuff/index.html pero implica que tengo que saber cuántos trabajos (hilos) se iniciarán. – Tim

5

@havexz 's es bastante bueno: writing everything to the same log file and using nested diagnostic contexts.

Si su preocupación es sobre varias JVM al grabar en el mismo FileAppender, a continuación, me gustaría sugerir dos cosas:

En modo prudente, FileAppender escribirá con seguridad en el archivo especificado, , incluso en presencia de otras instancias de FileAppender ejecutándose en diferentes JVM, potencialmente en ejecución en diferentes hosts.

0

Por lo que puedo decir ThreadLocal API fue diseñado para hacer lo que usted describe.

Código, como a continuación se establecería madereros por hilos cada uno usando propia (por subproceso) FileAppender:

/** 
* usage: threadLocalLogger.get().info("hello thread local logger") 
*/ 
static ThreadLocal<Logger> threadLocalLogger = newThreadLocalLogger("myJobId"); 

private static ThreadLocal<Logger> newThreadLocalLogger(final String myJobID) { 
    return new ThreadLocal<Logger>() { 
     @Override 
     protected Logger initialValue() { 
      return logger(myJobID, Thread.currentThread().getId()); 
     } 
    }; 
} 

private static Logger logger(String myJobID, long threadId) { 
    // Initialize the logger 
    String loggerId = myJobID + "-" + threadId; 
    Logger myLogger = Logger.getLogger(loggerId); 
    FileAppender myFileAppender; 
    try 
    { 
     myFileAppender = new FileAppender(new SimpleLayout(), 
       loggerId + ".log", false); 
     BasicConfigurator.resetConfiguration(); 
     BasicConfigurator.configure(myFileAppender); 
    } catch (IOException e1) { 
    // TODO Auto-generated catch block 
     e1.printStackTrace(); 
    } 
    return myLogger; 
} 
8

Para log4j v2 puede usar RoutingAppender para enrutar mensajes dinámicamente. Puede poner valor para la clave 'threadId' en el mapa ThreadContext y luego usar esta identificación como parte del nombre del archivo. Hay un ejemplo que he aplicado fácilmente para el mismo propósito que el tuyo. Consulte http://logging.apache.org/log4j/2.x/faq.html#separate_log_files

Tenga en cuenta al poner valores en el mapa ThradContext: "Un subproceso secundario hereda automáticamente una copia del contexto de diagnóstico asignado de su elemento primario". Por lo tanto, si ha puesto un valor para la clave 'threadId' en el hilo principal y finalmente ha creado varios hilos a partir de él, todos los hilos hijo heredarán el valor del valor 'threadId'. No fui capaz de anular este valor simplemente usando put() una vez más; necesita usar ThreadContext.clear() o eliminar explícitamente() el valor del mapa de contexto de subprocesos.

Aquí es mi log4j.xml de trabajo:

<?xml version="1.0" encoding="UTF-8"?> 
<configuration status="WARN"> 
    <properties> 
     <property name="logMsgPattern">%d{HH:mm:ss} %-5level - %msg%n</property> 
     <property name="logDir">test logs</property><!-- ${sys:testLogDir} --> 
    </properties> 
    <appenders> 
     <Console name="Console" target="SYSTEM_OUT">   
      <PatternLayout pattern="${logMsgPattern}"/> 
     </Console> 

     <Routing name="Routing"> 
        <Routes pattern="$${ctx:threadId}">    
         <Route> 
          <RollingFile name="RollingFile-${ctx:threadId}" fileName="${logDir}/last-${ctx:threadId}.log" filePattern="${logDir}/%d{yyyy-MM-dd}/archived_%d{HH-mm}-${ctx:threadId}.log"> 
            <PatternLayout pattern="${logMsgPattern}"/> 
            <Policies> 
           <OnStartupTriggeringPolicy /> 
          </Policies> 
        </RollingFile> 
         </Route> 
        </Routes> 
      </Routing> 
    </appenders> 

    <loggers>    
     <root level="debug"> 
      <appender-ref ref="Console" level="debug" /> 
      <appender-ref ref="Routing" level="debug"/> 
     </root>      
    </loggers> 
</configuration> 
+0

¿dónde defines esta variable threadId? –

2

Aquí es el fragmento del código de ruta de un archivo log4j.xml de trabajo.

<Appenders> 

     <Console name="ConsoleAppender" target="SYSTEM_OUT"> 
      <PatternLayout> 
       <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %-50logger{4}: %msg%n</pattern> 
      </PatternLayout> 
     </Console> 
    <Routing name="RoutingAppender"> 
       <Routes pattern="${ctx:logFileName}"> 


        <!-- This route is chosen if ThreadContext has a value for logFileName. 
         The value dynamically determines the name of the log file. --> 

        <Route> 
         <RollingFile name="Rolling-${ctx:logFileName}" 
          fileName="${sys:log.path}/${ctx:logFileName}.javalog" 
          filePattern="./logs/${date:yyyy-MM}/${ctx:logFileName}_%d{yyyy-MM-dd}-%i.log.gz"> 
          <PatternLayout> 
           <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %-50logger{4}: %msg%n</pattern> 
          </PatternLayout> 
          <Policies> 
           <TimeBasedTriggeringPolicy interval="6" 
            modulate="true" /> 
           <SizeBasedTriggeringPolicy size="10 MB" /> 
          </Policies> 
         </RollingFile> 
        </Route> 

        <!-- This route is chosen if ThreadContext has no value for key logFileName. --> 
        <Route key="${ctx:logFileName}" ref="ConsoleAppender" /> 

       </Routes> 
      </Routing> 
</Appenders> 
    <loggers> 
     <root level="debug"> 
      <appender-ref ref="RoutingAppender" level="debug" /> 
     </root> 
    </loggers> 

La clave 'LogFileName' se pueden añadir al mapa contexto hilo en el método run() de la clase Ejecutable como sigue,

public class SomeClass implements Runnable{ 

private int threadID; 

public SomeClass(int threadID){ 
    this.threadID=threadID; 
    } 
@Override 
public void run() { 
    String logFileName = "thread_log_"+ threadID; 
    ThreadContext.put("logFileName", logFileName); 
    //Some code 
    ThreadContext.remove("threadId"); 
    } 
} 

Además, los paquetes de log4j correctos deben ser importados , Como se muestra abajo.

import org.apache.logging.log4j.LogManager; 
import org.apache.logging.log4j.Logger; 
import org.apache.logging.log4j.ThreadContext; 

Tenga en cuenta que las siguientes importaciones no funcionarán. LogManager y Logger también deben provenir de org.apache.logging.log4j.

import org.apache.log4j.LogManager; 
import org.apache.log4j.Logger; 
import org.apache.logging.log4j.ThreadContext; 
Cuestiones relacionadas