2009-04-12 10 views
6

Siempre he tenido dudas a la hora de diseñar informes de ejecución adecuados.Información de informes durante la ejecución del código: mejor diseño

Digamos que tiene el siguiente caso (estúpido, para ser simple). Usaré Python.

def doStuff(): 
    doStep1() 
    doStep2() 
    doStep3() 

Ahora, supongamos que desea dar un informe de los distintos pasos, si algo va mal, etc. En realidad, no de depuración: simplemente el comportamiento informativo de la aplicación.

Una primera solución, fácil es poner impresiones

def doStuff(): 
    print "starting doing stuff" 
    print "I am starting to do step 1" 
    doStep1() 
    print "I did step 1" 
    print "I am starting to do step 2" 
    doStep2() 
    print "I did step 2" 
    print "I am starting to do step 3" 
    doStep3() 
    print "I did step 3" 

En general, esto es bastante malo. Supongamos que este código va a terminar en una biblioteca. No esperaría que mi biblioteca imprima cosas. Esperaría que hiciera el trabajo en silencio. Sin embargo, a veces me gustaría proporcionar información, no solo en situaciones de depuración, sino también para mantener al usuario informado de que algo realmente está en proceso de finalización. Imprimir también es malo porque no tienes control del manejo de tus mensajes. simplemente va a stdout, y no hay nada que puedas hacer al respecto, excepto la redirección.

Otra solución es tener un módulo para el registro.

def doStuff(): 
    Logging.log("starting doing stuff") 
    Logging.log("I am starting to do step 1") 
    doStep1() 
    Logging.log("I did step 1") 
    Logging.log("I am starting to do step 2") 
    doStep2() 
    Logging.log("I did step 2") 
    Logging.log("I am starting to do step 3") 
    doStep3() 
    Logging.log("I did step 3") 

Esto tiene la ventaja de que se sabe especie de un lugar único para su servicio de registro, y se puede retocar este servicio tanto como usted desee. Puede silenciarlo, redirigirlo a un archivo, a stdout, o incluso a una red. La desventaja es que obtiene un acoplamiento muy fuerte con el módulo de registro. Básicamente, cada parte de tu código depende de él, y tienes llamadas para iniciar sesión en cualquier lugar.

La tercera opción es tener un objeto de informe con una interfaz clara, y pasarlo alrededor

def doStuff(reporter=NullReporter()): 
    reporter.log("starting doing stuff") 
    reporter.log("I am starting to do step 1") 
    doStep1() 
    reporter.log("I did step 1") 
    reporter.log("I am starting to do step 2") 
    doStep2() 
    reporter.log("I did step 2") 
    reporter.log("I am starting to do step 3") 
    doStep3() 
    reporter.log("I did step 3") 

Eventualmente, también puede pasar el objeto reportero a doStepX() si tienen más que decir. Ventaja: reduce el acoplamiento con un módulo, pero introduce el acoplamiento con la creación de instancias del objeto NullReporter. Esto se puede resolver mediante el uso de ninguno por defecto y comprobar antes de llamar a registro, que es torpe, porque en Python que tiene que escribir un condicional cada vez (en C se puede definir una macro)

def doStuff(reporter=None): 
    if reporter is not None: 
     reporter.log("starting doing stuff") 
     # etc... 

Editar: Otro la opción es trabajar como Qt, y tener una estrategia de señal emit(). A medida que su código se ejecuta, emite información con códigos de estado adecuados, y cualquier persona interesada puede suscribirse a las señales y proporcionar información. Agradable y limpio, muy desacoplado, pero requiere un poco de codificación, ya que no creo que esto pueda hacerse rápidamente con la batería de pitón incluida.

Finalmente, puede plantear excepciones con un mensaje de error significativo, pero esto, por supuesto, se puede usar solo si sale de una condición de error. no funciona para informes ocasionales.

Editar: Me gustaría aclarar el hecho de que la situación es más general, y no se limita solo a una secuencia de pasos invocados. También podría considerar estructuras de control:

if disconnected: 
    print "Trying to connect" 
    connect() 
else: 
    print "obtaining list of files from remote host" 
    getRemoteList() 

El informe también podría ser en las rutinas reales, por lo que tendría una "impresión" en las rutinas connect() y getRemoteList() como un primer comunicado.Por lo tanto,

La pregunta es:

  • Cuál cree usted que es el mejor diseño para un cierto código (especialmente en el caso de una biblioteca) que sea al mismo tiempo en silencio cuando el ruido puede ser perjudicial para el cliente , pero prolijo cuando es útil?
  • ¿Cómo manejar una combinación equilibrada entre código lógico y código de informe?
  • La mezcla entre códigos y la comprobación de errores se ha resuelto con excepciones. ¿Qué se puede hacer para dividir el "ruido" de informar desde la lógica del código?

Editar: más pensamientos de la mente

creo que es no sólo una cuestión de desacoplar el código de registro del código de la lógica. Creo que también se trata de disociar la producción de información del consumo de información. Ya existen técnicas similares, en particular para manejar eventos de IU, pero realmente no veo los mismos patrones aplicados al problema de registro.


Editar: acepté la respuesta de Marcelo porque señala a los elementos de hecho que un compromiso es la mejor solución en este caso, y no hay bala de plata. Sin embargo, todos los demás también fueron respuestas interesantes, y estuve muy contento de haberlos votado a todos. ¡Gracias por la ayuda!

+0

He añadido una recompensa para ver si se puede seguir hablando sobre esta cuestión. Además, esta es la primera apertura de recompensas, así que también tenía curiosidad por hacerlo. –

Respuesta

1

Creo que hay un punto en el que debe trazar una línea y llegar a un compromiso. No veo ninguna forma de desacoplar completamente el registro del sistema porque tiene que enviar esos mensajes a alguna parte y de una manera que alguien entienda.

Iría con el módulo de registro predeterminado, porque ...es el módulo predeterminado. Está bien documentado y viene con la biblioteca predeterminada, por lo que no hay problemas de dependencia aquí. Además, te salvas de reinventar la rueda.

Dicho esto, si realmente te gusta hacer algo nuevo, podrías crear un objeto reportero global. Puede crear instancias y crear instancias al comienzo de su proceso (registro, no registro, redireccionamiento de flujos, etc. incluso en una etapa por proceso/función/paso) y llamar desde cualquier lugar, sin necesidad de pasarlo (tal vez en un entorno multi-hilo, pero eso sería mínimo).

También puede ponerlo dentro de otro hilo y atrapar eventos de registro a la Qt.

4

Creo que la mejor solución para una biblioteca es una en la línea de agregar, p.

Log.Write(...) 

donde el comportamiento de Log se recogió en el entorno ambiente (por ejemplo app.config o una variable de entorno).

(también creo que esto es un problema que ha sido abordado y resuelto muchas veces, y si bien hay algunos 'puntos dulces' en el espacio de diseño, la respuesta anterior es el mejor de la OMI para la situación que usted describe.)

No veo ninguna buena manera de "desacoplar" la parte "normal" de su código de la parte de "registro". La tala tiende a ser relativamente no intrusiva; No creo que el Log.Write ocasional (...) sea una distracción del código del mundo real.

+0

Bueno, aunque no intrusivo, creo que se trata de desacoplar no el código en sí, sino la noción de dar información a entidades externas sobre el progreso. –

2

Otra opción sería escribir el código sin iniciar sesión y luego aplicar alguna transformación para insertar las instrucciones de registro apropiadas antes de ejecutar el código. Las técnicas reales para hacer esto dependerán en gran medida del idioma, pero serían bastante similares al proceso de escribir un depurador.

Es probable que no vale la pena la complejidad añadida, aunque ...

+0

una especie de "tejedor" en AOP. pero o les resulta difícil implementar algo similar. Se enfrenta a muchos obstáculos prácticos. –

+0

Una manera fácil de implementar eso sería cambiar temporalmente los comentarios en línea en llamadas a funciones. –

1

No debe haber herramientas para permitir que los mensajes de registro repetitivo al estilo de "entrar en el método A con los parámetros (1,2,3)", "volver de método B con valor X, tomó 10 ms "para generarse automáticamente (y selectivamente) (controlado en tiempo de ejecución o despliegue). Escribir eso a mano es demasiado aburrido/repetitivo/propenso a errores.

No estoy seguro de si hay, sin embargo.

Si va a escribir mensajes de registro manuales, asegúrese de incluir información contextual útil (identificación de usuario, URL que se está buscando, consulta de búsqueda, etc.), de modo que si algo sale mal, obtenga más información que solo el nombre del método.

+0

Creo que lo que se necesita es una especie de sistema similar a una excepción, donde la excepción no se usa para controlar la ejecución, solo para informar que se ha producido un evento. –

2

Encontré this mientras buscaba Programación Orientada a Aspectos para python. Estoy de acuerdo con otros carteles en que tales preocupaciones no deberían mezclarse con la lógica central.

En esencia, los puntos en los que desea colocar el registro no siempre pueden ser arbitrarios, pueden ser conceptos como "todos los puntos antes del error" podrían identificarse por un punto. Se pueden capturar otros puntos totalmente arbitrarios usando técnicas simples de registro.

+0

AO para python? Estaba intrigado por las ideas de AO que leí sobre el año pasado, pero pensé que tendría que sumergirme en C#, Java u otra cosa ... y vaya, ¿eso es de 2003? Guay. – DarenW

+0

No sé mucho sobre AOP, sin embargo, usar AOP te ayuda a interceptar puntos de acceso comunes, pero no resuelve el problema de poner puntos arbitrarios en tu código en los que deseas proporcionar un mensaje significativo para una condición de error que pronto pasar, o un estado informativo. ¿Estoy en lo cierto? –

+0

Sí, tienes razón. Pero deduje que los puntos en los que desea ponerlos no siempre son arbitrarios, es posible que los conceptos como "todos los puntos antes del error" puedan identificarse mediante un corte de punto. Se pueden capturar otros puntos totalmente arbitrarios usando técnicas simples de registro. – Surya

1

Utilizaría el módulo estándar logging que ha sido parte de la biblioteca estándar desde Python 2.3.

De esta forma, existe una buena posibilidad de que las personas que miren su código ya sepan cómo funciona el módulo logging. Y si tienen que aprender, al menos está bien documentado, y su conocimiento es transferible a otras bibliotecas que también usan logging.

¿Hay alguna función que desee pero no puede encontrar en el módulo estándar logging?

+0

Por lo que veo, el módulo de registro es muy poderoso. Los manejadores se pueden usar para definir cualquier comportamiento. Por lo tanto, en principio, al "mal uso" del módulo de registro, puede despachar eventos. Este módulo se ajusta a la segunda categoría que describí, y está vinculado a la biblioteca estándar. –

1

Creo que la solución más simple es la mejor aquí. Esto depende del idioma, pero solo use un identificador muy corto, accesible a nivel mundial - en PHP utilizo una función personalizada trace($msg) - y luego simplemente implemente y vuelva a implementar ese código como considere adecuado para el proyecto o fase en particular.

La versión automática en compilación de este es el depurador estándar. Si quiere ver etiquetas con significado, necesita escribir esas etiquetas usted mismo, desafortunadamente :)

O podría tratar de transformar temporalmente los comentarios en línea en llamadas a funciones, pero no estoy seguro de que eso funcione.

1

En respuesta a su edición sobre producción/consumo de información: esa es una preocupación válida para el caso general, pero el registro no es el caso general. En particular, no debe confiar en la salida de registro de una parte de su programa para afectar la ejecución de otra parte. Eso sin duda uniría estrechamente al consumidor con la implementación del productor.

El registro debe considerarse como accesorio a su ejecución principal. Sus sistemas no deberían conocer ni preocuparse por la presencia o el contenido de dichos archivos de registro (con la posible excepción de las herramientas de supervisión). Siendo ese el caso, la noción de desacoplamiento del "consumo" de los registros de su producción es inconsecuente. Como no está utilizando los registros para hacer algo significativo, el acoplamiento no es un problema.

+0

De hecho, el registro es una especie de caso especial de despacho y manejo de eventos. No confiaría en los mensajes de registro, pero podría confiar en los eventos de registro para controlar mi flujo. Ejemplo: una biblioteca de cliente FTP podría informar que el archivo se ha descargado como un evento de registro y como un evento de control (por ejemplo, devolución de llamada) –

2

A menudo uso DTrace para esto. En OS X, python y ruby ​​ya están configurados con ganchos DTrace. En otras plataformas, probablemente tendrías que hacer esto tú mismo. Pero poder adjuntar rastros de depuración a un proceso en ejecución es, bueno, increíble.

Sin embargo, para el código de la biblioteca (supongamos que está escribiendo una biblioteca de cliente http), la mejor opción es pasar un registrador opcional como parámetro, como usted mencionó. DTrace es bueno para agregar el registro a algo que salió mal en la producción (y en otras partes), pero si otras personas posiblemente necesiten acceder a los registros para depurar su código que luego llama al suyo, entonces un registrador opcional como parámetro es absolutamente el camino ir.

Cuestiones relacionadas