Registro de Python desde múltiples procesos.

Tengo un progtwig posiblemente de larga duración que actualmente tiene 4 procesos, pero podría configurarse para tener más. He investigado el registro de múltiples procesos utilizando el logging de python y estoy usando el enfoque SocketHandler que se analiza aquí . Nunca tuve ningún problema con un solo registrador (sin sockets), pero por lo que leí me dijeron que fallaría eventualmente e inesperadamente. Según tengo entendido, no se sabe qué sucederá cuando intentes escribir en el mismo archivo al mismo tiempo. Mi código hace esencialmente lo siguiente:

 import logging log = logging.getLogger(__name__) def monitor(...): # Spawn child processes with os.fork() # os.wait() and act accordingly def main(): log_server_pid = os.fork() if log_server_pid == 0: # Create a LogRecordSocketServer (daemon) ... sys.exit(0) # Add SocketHandler to root logger ... monitor() if __name__ == "__main__": main() 

Así que mis preguntas son: ¿Necesito crear un nuevo objeto de log después de cada os.fork() ? ¿Qué sucede con el objeto de log global existente?

Con hacer las cosas como estoy, ¿estoy tratando de solucionar el problema que estoy tratando de evitar (múltiples archivos / sockets abiertos)? ¿Esto fallará y por qué fallará (me gustaría poder saber si futuras implementaciones similares fallarán)?

Además, ¿de qué manera falla el método “normal” (un log= expresión) para registrar un archivo desde varios procesos? ¿Sube un IOError / OSError? ¿O simplemente no escribe completamente los datos en el archivo?

Si alguien pudiera proporcionar una respuesta o enlaces para ayudarme, sería genial. Gracias.

FYI : Estoy probando en Mac OS X Lion y el código probablemente terminará ejecutándose en una máquina virtual CentOS 6 en una máquina con Windows (si eso importa). Cualquiera que sea la solución que use, no necesita funcionar en Windows, pero debería funcionar en un sistema basado en Unix.

ACTUALIZACIÓN: esta pregunta ha comenzado a alejarse del comportamiento específico de registro y es más en el ámbito de lo que hace Linux con los descriptores de archivos durante las bifurcaciones. Saqué uno de mis libros de texto de la universidad y parece que si abres un archivo en modo agregado desde dos procesos (no antes de una bifurcación), ambos podrán escribir en el archivo correctamente siempre que tu escritura no exceda el búfer real del kernel (aunque puede ser necesario usar el búfer de línea, aún no estoy seguro de eso). Esto crea 2 entradas de tabla de archivos y una entrada de tabla de v-nodos. Abrir un archivo y luego forking no se supone que funcione, pero parece que siempre y cuando no exceda el búfer del kernel como antes (lo he hecho en un progtwig anterior).

Así que supongo que, si desea un registro de multiprocesamiento independiente de la plataforma, use sockets y cree un nuevo SocketHandler después de cada bifurcación para estar seguro como lo sugiere Vinay a continuación (eso debería funcionar en todas partes). Para mí, ya que tengo un fuerte control sobre el sistema operativo en el que se ejecuta mi software, creo que voy a usar un objeto de log global con un FileHandler (se abre en modo de aplicación de forma predeterminada y en línea con búfer en la mayoría de los sistemas operativos). La documentación para open dice “Un almacenamiento en búfer negativo significa usar el valor predeterminado del sistema, que generalmente es el almacenamiento intermedio de línea para los dispositivos tty y el almacenamiento intermedio de otros archivos. Si se omite, se utiliza el valor predeterminado del sistema”. o simplemente podría crear mi propia secuencia de registro para estar seguro del búfer de línea. Y para ser claros, estoy bien con:

 # Process A a_file.write("A\n") a_file.write("A\n") # Process B a_file.write("B\n") 

productor…

 A\n B\n A\n 

mientras no produzca …

 AB\n \n A\n 

Vinay (o alguien más), ¿qué tan equivocado estoy? Házmelo saber. Gracias por la mayor claridad / seguridad que puede proporcionar.

¿Necesito crear un nuevo objeto de registro después de cada os.fork ()? ¿Qué sucede con el objeto de registro global existente?

AFAIK el objeto de registro global sigue apuntando al mismo registrador en los procesos padre e hijo. Así que no deberías necesitar crear uno nuevo. Sin embargo, creo que debe crear y agregar el SocketHandler después de la fork() en monitor() , de modo que el servidor de socket tenga cuatro conexiones distintas, una para cada proceso secundario. Si no hace esto, entonces los procesos secundarios deshabilitados en monitor () heredarán el SocketHandler y su controlador de socket de su padre, pero no estoy seguro de que se comporte mal. Es probable que el comportamiento sea dependiente del sistema operativo y que tenga suerte con OSX.

Con hacer las cosas como estoy, ¿estoy tratando de solucionar el problema que estoy tratando de evitar (múltiples archivos / sockets abiertos)? ¿Esto fallará y por qué fallará (me gustaría poder saber si futuras implementaciones similares fallarán)?

No esperaría un error si creas la conexión de socket con el servidor de socket después de la última fork() como sugerí anteriormente, pero no estoy seguro de que el comportamiento esté bien definido en ningún otro caso. Se refiere a varios archivos abiertos, pero no veo ninguna referencia a abrir archivos en su fragmento de pseudocódigo, simplemente abriendo sockets.

Además, ¿de qué manera falla el método “normal” (un registro = expresión) para registrar un archivo desde varios procesos? ¿Sube un IOError / OSError? ¿O simplemente no escribe completamente los datos en el archivo?

Creo que el comportamiento no está bien definido, pero uno esperaría que los modos de falla se presenten como mensajes de registro intercalados de diferentes procesos en el archivo, por ejemplo

 Process A writes first part of its message Process B writes its message Process A writes second part of its message 

Actualización: si usa un FileHandler de la manera que describió en su comentario, las cosas no serán tan buenas, debido al escenario que describí anteriormente: el proceso A y B comienzan a apuntar al final del archivo (debido al apéndice modo), pero luego las cosas pueden desincronizarse porque (por ejemplo, en un multiprocesador, pero incluso potencialmente en un uniprocesador), un proceso puede (adelantarse a otro y) escribir en el identificador de archivo compartido antes de que otro proceso haya terminado de hacerlo.