2009-02-19 20 views
22

Quiero tener un registro de diagnóstico producido por varias tareas de administración de datos. Estas tareas pueden estar en múltiples hilos. Cada tarea necesita escribir un elemento (posiblemente con subelementos) en el registro; entrar y salir rápidamente. Si se tratara de una situación de una sola tarea, utilizaría XMLStreamWriter ya que parece ser la mejor opción para la simplicidad/funcionalidad sin tener que mantener un documento XML expandido en la memoria.¿Mejores prácticas para el registro de Java desde varios subprocesos?

Pero no es una situación de una sola tarea, y no estoy seguro de cómo asegurarme de que es "seguro para hilos", donde "enhebrar" en esta aplicación significa que cada elemento de registro debe escribirse correctamente en el registro en serie (uno después del otro y no intercalado de ninguna manera).

¿Alguna sugerencia? Tengo una vaga intuición de que el camino a seguir es usar una cola de elementos de registro (con cada uno de ellos capaz de producirse rápidamente: mi aplicación está ocupada haciendo un trabajo real que es sensible al rendimiento), y tengo un hilo separado que maneja el registro elementos y los envía a un archivo para que el registro no interrumpa a los productores.

El registro no tiene que ser necesariamente XML, pero sí quiero que sea estructurado y legible por máquina.

editar: puse "threadsafe" entre comillas. Log4j parece ser la opción obvia (nueva para mí pero antigua para la comunidad), ¿por qué reinventar la rueda?

+0

Tengo el mismo problema, pero no es necesario que muestre el registro en tiempo real (solo después de que se ejecuten todos los subprocesos). Mi solución es agregar una hora del sistema al inicio de cada mensaje y ordenarlos en el tiempo. – Fuhrmanator

+0

La hora del sistema no es confiable después de todo, algunas operaciones están tan cerca que tienen la misma marca de tiempo en milisegundos. La ordenación puede dar como resultado un cambio de orden. Entonces, otra cosa que intenté fue usar Colllections.synchronizedList() para envolver una ArrayList de cadenas de mensajes. Agregar a la cadena una marca de tiempo muestra que incluso así no es perfecto. Algunos mensajes en mi caso estaban fuera de servicio (en uno o dos milisegundos), pero esto probablemente se deba a la demora de cola para la llamada de add() a la lista sincronizada. – Fuhrmanator

Respuesta

21

Utilice un marco de registro, como Log4j.

+0

gracias! leyendo los documentos (http://logging.apache.org/log4j/1.2/manual.html) ahora ... –

+2

asegúrese de usar la expresión '% t' en log4j ConversionPattern para registrar el nombre del hilo – chburd

+3

No lo hago piense que log4j hace que el registro sea seguro. Por threadsafe me refiero al orden en que se imprime el registro debe ser el mismo para cualquier orden del planificador. Creo que todavía tiene que usar la sincronización y el bloqueo para que el registro sea seguro para los hilos. ¿Alguien quiere confirmar/denegar? – jbu

3

Puede usar mecanismos de sincronización (como un monitor o un semáforo) para asegurarse de que se procesa una solicitud de registro antes de aceptar la siguiente. Todo esto podría estar oculto del código que llama a las rutinas de registro.

+0

Me encantaría ver a los videntes comentar sus razones. Por supuesto, el uso de un marco de trabajo es quizás la mejor opción, pero si quieres hacerlo por tu cuenta, tienes que usar la sincronización ... – jpfollenius

+0

No te resté, pero puedo adivinar el razonamiento de cualquiera que tenga . Alguien que "no está seguro de cómo asegurarse de que esto no sea enhebrable" no debe decir "usar un monitor o un semáforo". Simplemente debería usar un marco de registro. –

+0

Además, la mejor manera de hacer esto en Java si * lo * hiciera usted mismo sería probablemente tener varios subprocesos haciendo una oferta o colocarse en un BlockingQueue, y un hilo de registrador que realiza un recorrido en un ciclo y los escribe salir al archivo de registro tan rápido como sea posible. No hay necesidad de meterse con 'sincronizado'. –

4

Utilice un marco de registro, como Log4.

y si no está contento con la salida puede escribir su propio apéndice, filtro, lo que sea que lo ajuste simplemente escriba. Así que podrías hacer incluso un caché para reorganizar las entradas, aunque no estoy diciendo que sea una buena idea.

3

log4j es y ha sido el estándar para el registro de Java durante muchos años. Pero si no le apetece una dependencia externa, el paquete java.util.logging proporciona una solución aceptable.

+1

Sure JUL es una alternativa, pero no estoy seguro de llamarla perfectamente buena. Lo mejor es evitarlo en mi opinión, y sí lo aprendí de la manera difícil, de ser forzado a usarlo. –

+0

¿por qué dices eso? – LoveMeow

4

Utilice un marco de registro que implemente alguna forma de the NDC pattern, como Log4J.

+1

+1 para NDC, y no olvide el MDC también, que personalmente encuentro más útil que NDC. En una aplicación web, me gusta agregar el ID de sesión al MDC para que cada línea de registro pueda tener la ID de sesión del usuario. –

0

Desarrollar esto usted mismo de una manera segura para la ejecución de subprocesos no es trivial, por lo que realmente debería usar un marco de trabajo de registro existente que sea seguro para subprocesos. El más utilizado es Log4J, que es seguro para subprocesos (ver FAQ).

20

Creo que estás en el camino equivocado. Usted dice "threadsafe" pero en realidad quiere decir "serializado". Threadsafe significa que un hilo no interferirá con los datos de otro hilo. La mayoría de las veces, los problemas de enhebrado se resuelven de antemano y no debe preocuparse solo por el registro. Por ejemplo, si su escritura:

myVariableSum = 0 + myVariable; 
//here comes other thread - Not very likely! 
logger.info("Log some INFO; myVariable has value" + myVariable.toString()); 

Usted tiene que asegurarse de que myVariable no ha sido cambiado por algún otro flujo del cálculo momento (primera línea) se ha realizado, pero antes de iniciar método fue llamado. Si esto ocurre, registrará el valor sucio que no se utilizó para realizar la operación, pero el valor fue asignado por otro hilo.Esto generalmente es cuidado; por ejemplo, la variable local (nivel de método) no se puede cambiar por otro hilo. De todos modos, si tiene que preocuparse por esto al iniciar sesión, más del 99% de que su programa ya tiene problemas graves de enhebrado.
Todos los principales frameworks de registro son en sí mismos "threadsafe", lo que significa que pueden implementarse en entornos multiproceso y no mostrarán problemas similares a los descritos internamente.
Obtener rastros para que aparezcan en el registro para que sucedan se suele llamar "serialización" de llamadas. La serialización de las escrituras de registro será un importante cuello de botella de rendimiento en cualquier aplicación multiproceso. Si usa el marco de trabajo de registro, como log4j, los rastros de todos los hilos aparecerán en un solo lugar más o menos para que sucedan. Sin embargo, una columna generalmente es un nombre de subproceso, por lo que puede filtrar fácilmente sus datos de registro por hilo; cada hilo registrará sus datos en orden cronológico. Echa un vistazo a este enlace: http://logging.apache.org/log4j/1.2/faq.html#1.7
Por último, si serializar grabaciones de registro es lo que realmente necesitas, entonces podrías usar algún tipo de estructura, como java.util.concurrent.BlockingQueue para enrutar tus mensajes.

+1

Como dices, quise decir ser serializado (era claro en el concepto pero no en el vocabulario). Cualquier problema de subprocesamiento será independiente del registro; si deseo asegurarme de que estoy registrando los valores correctos, haré las copias apropiadas de los datos volátiles en el momento correcto. –

+1

Buen trabajo en su explicación de la seguridad del hilo frente a la serialización. – Julie

+0

Muy bien explicado +1 – ADJ

5

Tiendo a usar SLF4J encima de Log4J. La funcionalidad parameterized logging es especialmente atractiva si va a tener muchas declaraciones de inicio de sesión que pueden desconectarse en un entorno de producción.

También puede ejecutarse en la parte superior de java.util.logging o usar su propia salida simple.

9

Utilice logback-classic. Es una implementación más nueva y mejor de log4j.

0

Si tuviera que hacerlo, podría hacer su propia versión ... utilizando FIFO o colas de único escritor/lector único.

1

Tuve un problema similar y demandas de implementación solo para registros especiales. Mi solución fue:

  1. yo hicimos un blockinglinkedqueue con el tamaño de *2 de tráfico de la aplicación/min.

  2. Todos los hilos colocan el objeto en la cola y finaliza el trabajo.

  3. Separado Log-Writer hilo que toma el objeto de la cola y lo escribe en el archivo log4j usando un appender separado. Este appender no se usó para los registros del sistema.

Esto asegura que los registros se escriben en serie y siempre están en orden.

Esto no afectará el rendimiento de la aplicación ya que la escritura de registro es un proceso completamente separado y no creará un cuello de botella.

También puede usar aysncappender de log4j.

Cuestiones relacionadas