Editar: como parece que no hay solución o estoy haciendo algo tan poco estándar que nadie lo sabe, revisaré mi pregunta para preguntar también: ¿Cuál es la mejor manera de lograr el registro cuando una aplicación de Python está haciendo un muchas llamadas al sistema?
Mi aplicación tiene dos modos. En modo interactivo, quiero que todos los resultados vayan a la pantalla, así como a un archivo de registro, incluido el resultado de cualquier llamada al sistema. En el modo demonio, toda la salida va al registro. El modo Daemon funciona muy bien usando os.dup2()
. No puedo encontrar una manera de "conectar" todas las salidas a un registro en modo interactivo, sin modificar todas y cada una de las llamadas al sistema.
En otras palabras, quiero la funcionalidad de la línea de comando 'tee' para cualquier salida generada por una aplicación python, incluida la salida de llamada del sistema .
Para aclarar:
Para redirigir toda la salida, hago algo como esto, y funciona muy bien:
# open our log file
so = se = open("%s.log" % self.name, 'w', 0)
# re-open stdout without buffering
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
# redirect stdout and stderr to the log file opened above
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
Lo bueno de esto es que no requiere llamadas de impresión especiales del resto del código. El código también ejecuta algunos comandos de shell, por lo que es bueno no tener que lidiar con cada uno de sus resultados individualmente.
Simplemente, quiero hacer lo mismo, excepto duplicar en lugar de redirigir.
A primera vista, pensé que simplemente invertir los dup2
's debería funcionar. ¿Por qué no lo hace? Aquí está mi prueba:
import os, sys
### my broken solution:
so = se = open("a.log", 'w', 0)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
os.dup2(sys.stdout.fileno(), so.fileno())
os.dup2(sys.stderr.fileno(), se.fileno())
###
print("foo bar")
os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)
El archivo "a.log" debe ser idéntico al que se muestra en la pantalla.
Respuestas:
Como se siente cómodo generando procesos externos a partir de su código, podría usarlo
tee
solo. No conozco ninguna llamada al sistema Unix que haga exactamente lo quetee
hace.También puede emular
tee
usando el paquete de multiprocesamiento (o usar el procesamiento si está usando Python 2.5 o anterior).Actualizar
Aquí hay una versión compatible con Python 3.3+:
fuente
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
ya no funciona desde python 3.3 (ver PEP 3116)tee.stdin.close()
al final de mi programa. También obtengo "ResourceWarning: el subproceso 1842 todavía se está ejecutando", y al agregarlosys.stdout.close(); sys.stderr.close()
al final del programa lo soluciona.Tuve este mismo problema antes y encontré este fragmento muy útil:
de: http://mail.python.org/pipermail/python-list/2007-May/438106.html
fuente
__del__
no se llama hasta el final de la ejecución. Ver stackoverflow.com/questions/6104535/…La
print
instrucción llamará alwrite()
método de cualquier objeto que asigne a sys.stdout.Giraba una pequeña clase para escribir en dos lugares a la vez ...
Ahora la
print
declaración hará eco en la pantalla y se agregará a su archivo de registro:Esto es obviamente rápido y sucio. Algunas notas:
<stdout>
si no va a iniciar sesión mientras dure el programa.Todo esto es lo suficientemente sencillo como para que me sienta cómodo dejándolos como ejercicios para el lector. La idea clave aquí es que
print
simplemente llama a un "objeto similar a un archivo" al que está asignadosys.stdout
.fuente
The print statement will call the write() method of any object you assign to sys.stdout
. ¿Y qué hay de otras funciones que envían datos a stdout sin usarprint
? Por ejemplo, si creo un proceso usandosubprocess.call
su salida, va a la consola pero no allog.dat
archivo ... ¿hay alguna manera de arreglar eso?Lo que realmente quieres es un
logging
módulo de la biblioteca estándar. Cree un registrador y adjunte dos controladores, uno estaría escribiendo en un archivo y el otro en stdout o stderr.Consulte Registro en múltiples destinos para más detalles.
fuente
logging
el módulo no redirigirá la salida de las llamadas del sistema comoos.write(1, b'stdout')
Aquí hay otra solución, que es más general que las otras: admite la división de salida (escrita en
sys.stdout
) a cualquier número de objetos similares a archivos. No hay requisito de que__stdout__
esté incluido.NOTA: Esta es una prueba de concepto. La implementación aquí no está completa, ya que solo envuelve los métodos de los objetos tipo archivo (p
write
. Ej. ), Omitiendo miembros / propiedades / setattr, etc. Sin embargo, probablemente sea lo suficientemente bueno para la mayoría de las personas como está actualmente.Lo que me gusta de ella, aparte de su generalidad, es que es limpia en el sentido de que no tiene ningún llamadas directas a
write
,flush
,os.dup2
, etc.fuente
_wrap
aquí en absoluto? ¿No podrías copiar el código allí__getattr__
y funciona igual?multifile([])
crea un archivo que genera un archivoUnboundLocalError
cada vez que llama a uno de sus métodos. (res
se devuelve sin ser asignado)Como se describe en otra parte, quizás la mejor solución es usar el módulo de registro directamente:
Sin embargo, hay algunas ocasiones (raras) en las que realmente desea redirigir stdout. Tuve esta situación cuando estaba extendiendo el comando runserver de django que usa print: no quería hackear la fuente de django pero necesitaba las declaraciones de print para ir a un archivo.
Esta es una forma de redirigir stdout y stderr fuera del shell utilizando el módulo de registro:
Solo debe usar esta implementación de LogFile si realmente no puede usar el módulo de registro directamente.
fuente
Escribí una
tee()
implementación en Python que debería funcionar para la mayoría de los casos, y también funciona en Windows.https://github.com/pycontribs/tendo
Además, puede usarlo en combinación con el
logging
módulo de Python si lo desea.fuente
(Ah, solo vuelva a leer su pregunta y vea que esto no se aplica).
Aquí hay un programa de muestra que utiliza el módulo de registro de Python . Este módulo de registro ha estado en todas las versiones desde 2.3. En este ejemplo, el registro es configurable por opciones de línea de comando.
En modo silencioso solo se registrará en un archivo, en modo normal se registrará tanto en un archivo como en la consola.
fuente
Para completar la respuesta de John T: https://stackoverflow.com/a/616686/395687
Agregué
__enter__
y__exit__
métodos para usarlo como administrador de contexto con lawith
palabra clave, que le da a este códigoEntonces se puede usar como
fuente
__del__
funcionalidad a__exit__
__del__
es una mala idea. Debe moverse a una función 'cerrar' que se llama__exit__
.Sé que esta pregunta ha sido respondida repetidamente, pero para esto tomé la respuesta principal de la respuesta de John T y la modifiqué para que contenga el rubor sugerido y seguí su versión revisada vinculada. También he agregado la entrada y la salida como se menciona en la respuesta de cladmi para usar con la declaración with. Además, la documentación menciona el uso de archivos de descarga,
os.fsync()
así que también he agregado eso. No sé si realmente necesitas eso, pero está ahí.Entonces puedes usarlo
o
fuente
mode="ab"
y en lawrite
funciónself.file.write(message.encode("utf-8"))
Otra solución que utiliza el módulo de registro:
fuente
Ninguna de las respuestas anteriores realmente parece responder al problema planteado. Sé que este es un hilo viejo, pero creo que este problema es mucho más simple de lo que todos lo están resolviendo:
Ahora esto repetirá todo en el controlador sys.stderr normal y su archivo. Crea otra clase
tee_out
parasys.stdout
.fuente
tee=tee_err();tee.write('');tee.write('');...
abrir + cierra un archivo para cada unowrite
. Consulte stackoverflow.com/q/4867468 y stackoverflow.com/q/164053 para ver argumentos en contra de esta práctica.Según una solicitud de @ user5359531 en los comentarios bajo la respuesta de @John T , aquí hay una copia de la publicación referenciada a la versión revisada de la discusión vinculada en esa respuesta:
fuente
Estoy escribiendo un script para ejecutar scripts de línea cmd. (Porque en algunos casos, simplemente no hay un sustituto viable para un comando de Linux, como el caso de rsync).
Lo que realmente quería era usar el mecanismo de registro de python predeterminado en todos los casos en los que fuera posible, pero aún capturar cualquier error cuando algo salía mal y no se había previsto.
Este código parece hacer el truco. Puede que no sea particularmente elegante o eficiente (aunque no usa string + = string, por lo que al menos no tiene ese cuello de botella potencial particular). Lo estoy publicando en caso de que le dé a alguien otras ideas útiles.
Obviamente, si no está tan sujeto a la fantasía como yo, reemplace LOG_IDENTIFIER con otra cadena que no le gustaría ver a alguien escribir en un registro.
fuente
Si desea registrar toda la salida en un archivo Y enviarla a un archivo de texto, puede hacer lo siguiente. Es un poco hacky pero funciona:
EDITAR: tenga en cuenta que esto no registra errores a menos que redirija sys.stderr a sys.stdout
EDIT2: Un segundo problema es que tienes que pasar 1 argumento a diferencia de la función incorporada.
EDITAR3: vea el código anterior para escribir stdin y stdout en la consola y el archivo con stderr solo yendo a archivo
fuente
Escribí un reemplazo completo para
sys.stderr
y simplemente dupliqué el cambiostderr
de nombre del códigostdout
para que también esté disponible para reemplazarsys.stdout
.Para ello se crea el mismo tipo de objeto como la corriente
stderr
ystdout
, y remitir a los métodos para el sistema originalstderr
ystdout
:Para usar esto, solo puede llamar
StdErrReplament::lock(logger)
yStdOutReplament::lock(logger)
pasar el registrador que desea usar para enviar el texto de salida. Por ejemplo:Al ejecutar este código, verá en la pantalla:
Y en el contenido del archivo:
Si desea ver también el contenido de las
log.debug
llamadas en la pantalla, deberá agregar un controlador de flujo a su registrador. En este caso sería así:Lo que se generaría así cuando se ejecute:
Si bien aún se guardaría esto en el archivo
my_log_file.txt
:Al deshabilitar esto
StdErrReplament:unlock()
, solo restaurará el comportamiento estándar de lastderr
secuencia, ya que el registrador adjunto nunca se puede desconectar porque alguien más puede tener una referencia a su versión anterior. Es por eso que es un singleton global que nunca puede morir. Por lo tanto, en caso de volver a cargar este móduloimp
o algo más, nunca volverá a capturar la corrientesys.stderr
como ya se inyectó en ella y la guardará internamente.fuente