logging - slideshare - ¿Deben abrirse/cerrarse las secuencias de archivos de registro en cada grabación o mantenerse abiertas durante la vida útil de una aplicación de escritorio?
ciclo cardiaco slideshare (13)
Abrir y cerrar. Puede salvarte de un archivo corrupto en caso de un bloqueo del sistema.
¿Deben las clases de registro abrir / cerrar una secuencia de archivos de registro en cada escritura en el archivo de registro o debe mantener la secuencia de archivos de registro abierta durante toda la vida útil de la aplicación hasta que se complete todo el registro?
Pregunto en el contexto de una aplicación de escritorio. He visto a personas hacerlo en ambos sentidos y me preguntaba qué enfoque arroja los mejores resultados para un registrador.
Como usuario de su aplicación, prefiero no mantener abiertos los archivos a menos que sea un requisito real de la aplicación. Solo una cosa más que puede salir mal en caso de un bloqueo del sistema, etc.
En general, como todos los demás dijeron, mantenga el archivo abierto para el rendimiento (abrir es una operación relativamente lenta). Sin embargo, debe pensar en lo que sucederá si mantiene el archivo abierto y las personas eliminan el archivo de registro o lo truncan. Y eso depende de las banderas usadas en tiempo abierto. (Me dirijo a Unix: probablemente se apliquen consideraciones similares a Windows, pero aceptaré la corrección por parte de aquellos más conocedores que yo).
Si alguien ve que el archivo de registro crece, digamos, 1 MiB y luego lo elimina, la aplicación no será más prudente, y Unix mantendrá los datos de registro seguros hasta que la aplicación cierre el registro. Además, los usuarios estarán confundidos porque probablemente crearon un nuevo archivo de registro con el mismo nombre que el anterior y están desconcertados acerca de por qué la aplicación ''detuvo el registro''. Por supuesto, no fue así; es solo iniciar sesión en el archivo anterior que nadie más puede obtener.
Si alguien se da cuenta de que el archivo de registro ha crecido a, por ejemplo, 1 MiB y luego lo trunca, la aplicación tampoco será más inteligente. Sin embargo, dependiendo de cómo se haya abierto el archivo de registro, es posible que obtenga resultados extraños. Si el archivo no se abrió con O_APPEND (POSIX-speak), el programa continuará escribiendo en su desplazamiento actual en el archivo de registro, y el primer 1 MiB del archivo aparecerá como una secuencia de cero bytes, que es apto confundir los programas que miran el archivo.
¿Cómo evitar estos problemas?
- Abra el archivo de registro con O_APPEND.
- Periódicamente use
fstat()
en el descriptor del archivo y verifique sist_nlink
es cero.
Si el número de enlaces va a cero, alguien eliminó su archivo de registro. Es hora de cerrarlo y reabrir uno nuevo. En comparación con stat()
o open()
, fstat()
debería ser rápido; básicamente se trata de copiar información directamente de las cosas que ya están en la memoria, no se necesita buscar nombres. Entonces, probablemente deberías hacer eso cada vez que estés a punto de escribir.
Sugerencias:
- Asegúrese de que haya un mecanismo para indicarle al programa que cambie los registros.
- Asegúrese de registrar la fecha y hora completa en los mensajes.
Sufro de una aplicación que da tiempo y no cita. Hoy temprano, tenía un archivo de mensaje que tenía algunas entradas desde el 17 de agosto (uno de los mensajes incluía accidentalmente la fecha en el mensaje después de la hora), y luego algunas entradas de hoy, pero solo puedo decirlo porque las creé. Si miraba el archivo de registro en un período de semanas, no podía decir en qué día se crearon (aunque sabría la hora en que se crearon). Ese tipo de cosas es molesto.
También puede ver qué sistemas como Apache: tienen mecanismos para manejar archivos de registro y hay herramientas para manejar la rotación de registros. Nota: si la aplicación mantiene abierto un solo archivo, no utiliza el modo de adición y no planifica la rotación de registros o los límites de tamaño, entonces no hay mucho que pueda hacer con los archivos de registro creciendo o teniendo ceros al principio. que reiniciar la aplicación periódicamente.
Debe asegurarse de que todas las escrituras en el registro se completen lo antes posible. Si usa descriptores de archivos, solo hay memoria intermedia del kernel en el camino; esto bien puede ser aceptable, pero considere las opciones O_SYNC
u O_DSYNC
para open()
. Si usa E / S de flujo de archivos, asegúrese de que cada escritura sea seguida por fflush()
. Si tiene una aplicación de subprocesos múltiples, asegúrese de que cada write()
contenga un mensaje completo; no intente escribir partes de un mensaje por separado. Con E / S de flujo de archivos, es posible que necesite usar flockfile()
y parientes para agrupar operaciones. Con la E / S del descriptor de archivo, puede usar dprintf()
para realizar E / S formateadas en un descriptor de archivo (aunque no está del todo claro que dprintf()
haga una sola llamada a write()
), o quizás writev()
para escribir segmentos de datos separados en una sola operación.
A propósito, los bloques de disco que ''contienen'' los ceros no están realmente asignados en el disco. Realmente puede arruinar las estrategias de copia de seguridad de las personas al crear archivos que son unos pocos GiB cada uno, pero todos excepto el último bloque de disco contienen solo ceros. Básicamente (se omite la comprobación de errores y la generación de nombres de archivo por concisión):
int fd = open("/some/file", O_WRITE|O_CREATE|O_TRUNC, 0444);
lseek(fd, 1024L * 1024L * 1024L, 0);
write(fd, "hi", 2);
close(fd);
Esto ocupa un bloque de disco en el disco, pero 1 copia de seguridad GiB (y cambia) en (sin comprimir) y 1 GB (y cambia) cuando se restaura. Antisocial, pero posible.
En general, es mejor mantenerlos abiertos.
Si le preocupa poder leerlos desde otro proceso, debe asegurarse de que el modo de compartir que usa para abrirlos / crearlos les permita a otros leerlos (pero no escribir en ellos, obviamente).
Si le preocupa perder datos en caso de un bloqueo, debe enjuagar / confirmar periódicamente sus buffers.
Es una compensación. Abrir y cerrar el archivo cada vez hace que sea más probable que el archivo se actualice en el disco en el programa. Por otro lado, hay una sobrecarga implicada en abrir el archivo, buscar hasta el final y agregarle datos.
En Windows, no podrá mover / renombrar / eliminar el archivo mientras está abierto, por lo que abrir / escribir / cerrar podría ser útil para un proceso de larga ejecución en el que ocasionalmente podría querer archivar el contenido del registro anterior sin interrumpir el escritor.
En la mayoría de los casos en los que hice este tipo de registro, mantuve el archivo abierto y usé fflush () para aumentar la probabilidad de que el archivo estuviera actualizado si el programa se bloqueaba.
La ventaja de cerrar el archivo cada vez es que el sistema operativo garantizará que el nuevo mensaje se escriba en el disco. Si deja el archivo abierto y su programa falla, es posible que no se escriba todo. También podría lograr lo mismo haciendo un fflush () o lo que sea equivalente en el idioma que está utilizando.
No veo ninguna razón para cerrarlo.
Por otro lado, el cierre y la reapertura requieren un poco más de tiempo.
Para aplicaciones grandes e intensivas, lo que suelo hacer es mantener el archivo de registro abierto mientras dure la aplicación y tener un hilo separado que vacía periódicamente el contenido del registro en la memoria del HDD. La operación de abrir y cerrar archivo requiere llamadas al sistema, lo cual es mucho trabajo si miras al nivel inferior.
Para el rendimiento, manténgase abierto . Por seguridad, enjuague a menudo .
Esto significará que la biblioteca en tiempo de ejecución no intentará almacenar en búfer las escrituras hasta que tenga muchos datos: ¡puede colgarse antes de que se escriba eso!
Puedo pensar en un par de razones por las que no quieres mantener el archivo abierto:
- Si un archivo de registro se comparte entre varias aplicaciones, usuarios o instancias de aplicaciones diferentes, podría tener problemas de bloqueo.
- Si no está borrando correctamente el almacenamiento intermedio de transmisión, podría perder las últimas entradas cuando esa aplicación falle y la necesite más.
Por otro lado, la apertura de archivos puede ser lenta, incluso en el modo de adición. Al final, se trata de lo que hace tu aplicación.
Si tiene lecturas / escrituras frecuentes, es más eficiente mantener el archivo abierto durante toda la vida con un solo abrir / cerrar .
Es posible que desee enjuagar periódicamente o después de cada escritura, aunque en caso de que su aplicación falle, es posible que no tenga todos los datos escritos en su archivo. Utilice fflush en sistemas basados en Unix y FlushFileBuffers en Windows.
Si también se ejecuta en Windows, puede usar la API CreateFile con FILE_FLAG_NO_BUFFERING para ir directamente al archivo en cada escritura.
También es mejor mantener el archivo abierto durante toda la vida, ya que cada vez que abre / cierra puede tener una falla si el archivo está en uso. Por ejemplo, es posible que tenga una aplicación de copia de seguridad que se ejecute y abra / cierre su archivo a medida que lo respalda. Y esto podría hacer que su programa no pueda acceder a su propio archivo . Lo ideal es mantener siempre abierto el archivo y especificar indicadores de uso compartido en Windows (FILE_SHARE_READ). En los sistemas basados en Unix, compartir será predeterminado.
Tiende a dejarlos abiertos, pero ábrelos con los permisos de uso compartido de archivos establecidos para permitir que otros lectores y asegúrese de limpiar el resultado del registro con cada mensaje.
Odio los programas que ni siquiera te permiten mirar el archivo de registro mientras se están ejecutando, o donde el archivo de registro no se vacía y queda rezagado con respecto a lo que está sucediendo.
Abría y cerraba cada escritura (o lote de escrituras). Si esto causa un problema de rendimiento en una aplicación de escritorio, es posible que esté escribiendo en el archivo de registro con demasiada frecuencia (aunque estoy seguro de que puede haber razones legítimas para muchas escrituras).