¿Cuáles son algunos patrones y antipatrones de registro de aplicaciones? [cerrado]

66

Recientemente tuve que investigar un problema de campo para nuestra aplicación de gran empresa. Me horrorizaron los registros que tuve que revisar en un intento de encontrar el problema y al final del día los registros no ayudaron en nada a identificar / aislar el error.

Nota: Entiendo que no todos los errores se pueden descubrir a través de los registros. Esto no cambia el hecho de que los registros son horribles.

Hay algunos problemas obvios con nuestro registro que ya podemos intentar solucionar. No quiero enumerarlos aquí y no puedo simplemente mostrarle nuestros archivos de registro para que pueda dar consejos sobre qué hacer.

En cambio, para evaluar qué tan mal estamos haciendo en el frente de la tala, me gustaría saber:

  1. ¿Cuáles son algunas pautas , si las hay, cuando se trata de iniciar sesión para una aplicación, especialmente para aplicaciones grandes?
  2. ¿Hay algún patrón que deberíamos seguir o antipatrones que deberíamos tener en cuenta?
  3. ¿Es esto algo importante de arreglar o incluso puede arreglarse o todos los archivos de registro son simplemente enormes y necesita scripts adicionales para analizarlos?

Nota al margen: utilizamos log4j.

c_maker
fuente

Respuestas:

55

Algunos puntos que mi práctica resultó útil:

  • Mantenga todo el código de registro en su código de producción. Tener la capacidad de habilitar un registro más / menos detallado en la producción, preferiblemente por subsistema y sin reiniciar el programa.

  • Haga que los registros sean fáciles de analizar greppor ojo. Se adhieren a varios campos comunes al comienzo de cada línea. Identifique el tiempo, la gravedad y el subsistema en cada línea. Formule claramente el mensaje. Haga que cada mensaje de registro sea fácil de asignar a su línea de código fuente.

  • Si ocurre un error, intente recopilar y registrar tanta información como sea posible. Puede llevar mucho tiempo, pero está bien porque el procesamiento normal ha fallado de todos modos. No tener que esperar cuando ocurre la misma condición en la producción con un depurador adjunto no tiene precio.

Los registros son principalmente necesarios para el monitoreo y la resolución de problemas. Ponte en el lugar de un solucionador de problemas y piensa qué tipo de registros te gustaría tener cuando algo malo está sucediendo o ha sucedido en la oscuridad de la noche.

9000
fuente
10
Me gusta esta respuesta, pero agregaría que es importante registrar qué elección se hizo en los puntos de decisión. He visto muchos sistemas en los que se registraba mucha basura, pero las decisiones clave no se registraban. Entonces el 95% del registro es básicamente inútil. También para los sistemas de tipo solicitud / respuesta es más importante poder iniciar sesión por solicitud que por subsistema.
Kevin
44
+1. Me gusta tu idea de ponerte en el lugar de un solucionador de problemas. Parece que las declaraciones de registro deberían contener muchos más mensajes de calidad que lo que hemos estado haciendo ...
c_maker 05 de
1
Es importante tener en cuenta que el registro de errores debe registrarse en el registro de eventos apropiado, así como en los registros de la aplicación.
Steven Evers
2
@SnOrfus: hay varias formas de almacenar registros, pero la esencia es que los mensajes de registro deben estar disponibles hasta el último segundo que el sistema se bloqueó, como una caja negra de un avión. Si utiliza algún tipo de almacenamiento en búfer, proporcione una opción para omitir / vaciar cada mensaje.
rwong
1
@Rig: por otro lado, muchos registradores locales no implementaron ningún almacenamiento en búfer (y limpiaron cada mensaje), lo que condujo a un rendimiento muy pobre. Es por eso que debe hacerse opcional.
rwong
28

Trabajo con sistemas críticos de seguridad en tiempo real y el registro es a menudo la única forma de atrapar errores raros que aparecen una vez que hay luna azul cada martes 53 cuando hay luna llena, si me entiendes. Esto te hace obsesivo con el tema, así que me disculparé ahora si empiezo a hacer espuma en la boca. Lo siguiente se escribió para los registros de depuración de código nativo, pero la mayor parte también es aplicable al mundo administrado ...

Use archivos de registro de texto. Parece obvio, pero algunas personas intentan generar archivos de registro binarios: eso es tonto porque no necesito buscar una herramienta de lectura cuando estoy en el campo. Además, si se trata de texto y la depuración es detallada, existe una buena posibilidad de que el ingeniero de campo pueda leer el archivo y diagnosticar el problema sin tener que volver a consultarme. Todos ganan.

Diseño sistemas que son capaces de registrar casi todo, pero no enciendo todo por defecto. La información de depuración se envía a un cuadro de diálogo de depuración oculto que la marca de tiempo y la envía a un cuadro de lista (limitado a alrededor de 500 líneas antes de la eliminación), y el cuadro de diálogo me permite detenerlo, guardarlo en un archivo de registro automáticamente o desviarlo a un depurador adjunto. Ese desvío me permite ver la salida de depuración de múltiples aplicaciones, todas bien serializadas, lo que a veces puede ser un salvavidas. Yo solía utilizar niveles de registro numéricos (el más alto sea el nivel, más se captura):

off
errors only
basic
detailed
everything

pero esto es demasiado inflexible: a medida que avanza hacia un error, es mucho más eficiente poder concentrarse en iniciar sesión exactamente en lo que necesita sin tener que atravesar toneladas de detritos, y puede ser un tipo particular de transacción u operación eso causa el error. Si eso requiere que encienda todo, solo está haciendo su propio trabajo más difícil. Necesitas algo más fino.

Así que ahora estoy en el proceso de cambiar al registro basado en un sistema de bandera. Todo lo que se registra tiene una bandera que detalla qué tipo de operación es, y hay un conjunto de casillas de verificación que me permiten definir qué se registra. Por lo general, esa lista se ve así:

#define DEBUG_ERROR          1
#define DEBUG_BASIC          2
#define DEBUG_DETAIL         4
#define DEBUG_MSG_BASIC      8
#define DEBUG_MSG_POLL       16
#define DEBUG_MSG_STATUS     32
#define DEBUG_METRICS        64
#define DEBUG_EXCEPTION      128
#define DEBUG_STATE_CHANGE   256
#define DEBUG_DB_READ        512
#define DEBUG_DB_WRITE       1024
#define DEBUG_SQL_TEXT       2048
#define DEBUG_MSG_CONTENTS   4096

Este sistema de registro se envía con la versión de lanzamiento , activada y guardada en el archivo de forma predeterminada. Es demasiado tarde para descubrir que debería haber estado iniciando sesión DESPUÉS de que ocurrió el error, si ese error solo ocurre una vez cada seis meses en promedio y no tiene forma de reproducirlo. El registro que solo funciona con compilaciones de depuración es justo. llanura. tonto.

El software generalmente se entrega con ERROR, BASIC, STATE_CHANGE y EXCEPTION activados, pero esto se puede cambiar en el campo a través del diálogo de depuración (o una configuración de registro / ini / cfg, donde se guardan estas cosas).

Ah, y una cosa: mi sistema de depuración genera un archivo por día. Sus requisitos pueden ser diferentes. Pero asegúrese de que su código de depuración comience cada archivo con la fecha, la versión del código que está ejecutando y, si es posible, algún marcador para la identificación del cliente, la ubicación del sistema o lo que sea. Puede obtener una mezcla de archivos de registro desde el campo, y necesita un registro de lo que vino de dónde y qué versión del sistema estaban ejecutando que está realmente en los datos, y no puede confiar en el cliente / ingeniero de campo para decirte qué versión tienen, pueden simplemente decirte qué versión piensan que tienen. Peor aún, pueden informar la versión exe que está en el disco, pero la versión anterior todavía se está ejecutando porque olvidaron reiniciar después de reemplazarla. Haz que tu código te lo diga a ti mismo.

Por último, no desea que su código genere sus propios problemas, por lo tanto, coloque una función de temporizador para purgar los archivos de registro después de tantos días o semanas (solo verifique la diferencia entre la hora actual y la hora de creación del archivo). Esto está bien para una aplicación de servidor que se ejecuta todo el tiempo, en una aplicación del lado del cliente que puede solucionar purgando los datos antiguos cuando se inicia. Por lo general, purgamos después de aproximadamente 30 días, en un sistema sin visitas frecuentes de ingenieros, es posible que desee dejarlo más tiempo. Obviamente, esto también depende del tamaño de sus archivos de registro.

Bob Moore
fuente
1
+1 En general, excelente respuesta, pero especialmente para poner el id de la aplicación y la información de la versión en el archivo de registro, desafortunadamente esto se pierde con mucha frecuencia.
Binario Worrier
27

Mi recurso público favorito para las pautas de registro son las mejores prácticas de Apache JCL .

Las mejores prácticas para JCL se presentan en dos categorías: general y empresarial. Los principios generales son bastante claros. Las prácticas empresariales están un poco más involucradas y no siempre es tan claro por qué son importantes.

Los principios de mejores prácticas empresariales se aplican a los componentes y herramientas de middleware que se espera que se ejecuten en un entorno de nivel "empresarial". Estos problemas se relacionan con el registro como internacionalización y la detección de fallas. Enterprise requiere más esfuerzo y planificación, pero se recomienda encarecidamente (si no es necesario) en los sistemas de nivel de producción. Las diferentes empresas / entornos corporativos tienen diferentes requisitos, por lo que ser flexible siempre ayuda ...

A pesar de apuntar a JCL, estos parecen ser lo suficientemente genéricos como para ser adoptados para el registro en general.

  • Mis "pautas" personales para iniciar sesión es que, a nivel de depuración, trato de hacer que mis registros se lean como una historia, con una lógica comprensible y detalles suficientes (pero no sobrecargados).

El anti-patrón más famoso es probablemente "tragar excepciones" - solo búscalo en la web.

En cuanto a los archivos de registro enormes, en mi práctica este fue principalmente el caso normal. Y sí, los scripts complementarios como los llama y / o herramientas como Chainsaw también me parecen normales.

  • Sin embargo, lo anterior no significa que siempre deba colocar a ciegas todos los registros en un archivo enorme. A veces puede ser útil escribir / copiar algunos de los registros para separar archivos. Por ejemplo, en mi reciente proyecto de control de calidad, los chicos pidieron archivos dedicados para métricas y datos de tiempo y breves informes sobre las operaciones del sistema. Dijeron que se beneficiarán de eso y dev lo hizo (el beneficio del archivo de informes breves resultó realmente significativo).

PD. Con respecto a los antipatrones, otros que vienen a la mente son "inundaciones" y mensajes sin sentido.

  • Lo llamo inundación cuando veo múltiples mensajes similares provenientes de un bucle con muchas iteraciones. Para mí, las inundaciones son lo suficientemente molestas como para tratar de deshacerme de ellas cuando las detecto en el código fuente. Por lo general, mejorarlo requiere algo de arte, porque, bueno, las cosas que suceden dentro del ciclo pueden ser interesantes. Cuando no tengo tiempo para mejorarlo más, intento al menos cambiar el nivel de registro de dichos mensajes al más bajo para que sea más fácil filtrarlo.

  • Los mensajes sin sentido parecen ser basura bastante popular. Estos se ven inofensivos cuando se leen en el código fuente; supongo que uno tiene que pasar por el dolor de analizar la salida de depuración con el aspecto de ...

    step #1
    step #2
    step #3
    

    ... para apreciar profundamente su fealdad inherente. Mi heurística favorita para detectar este tipo de problemas a nivel de código fuente (propuesta por un colega en uno de mis proyectos anteriores) es calcular la cantidad de ocurrencias de símbolos de espacio en literales de cadena utilizados en el registro. En mi experiencia, cero espacios básicamente garantiza que la declaración de registro no tenga sentido, un espacio también es un buen indicador del posible problema.

mosquito
fuente
44
Para evitar inundaciones, suelo recopilar las heurísticas del bucle y enviarlo después del bucle. Lo que significa que cualquier cosa interesante que ocurra en el bucle debe almacenarse en una variable (como somethingSpecialHappenedCount) y luego enviarse al registrador.
Spoike
@Spoike buen punto! almacenar en una variable es de hecho uno de mis trucos favoritos personales para combatir las inundaciones
mosquito
1
Realizo la salida de todos los contadores diferentes al registrador como una tabla ASCII en el registro después de que finaliza el ciclo para que puedan compararse fácilmente. La idea de la tabla se inspiró en la que genera Spring's StopWatch.prettyPrint () . Aparte de eso, hacer que el texto del registro sea legible y relevante sigue siendo un "arte" como se mencionó anteriormente en la respuesta.
Spoike
@Spoike: (y @gnat) Esto es interesante. Entonces, ¿básicamente agrega código real a la lógica de negocios solo con el propósito de iniciar sesión? Nunca he oído hablar de esto o hecho esto antes y no estoy seguro de cómo justificarlo ante mis compañeros de trabajo. Me temo que si comenzamos a hacer esto, algunos de nuestros desarrolladores saturarán el código fuente de tal manera que la lógica de negocios se vuelva complicada y difícil de leer. Simplemente registrar una declaración ya está haciendo que la fuente se vea más fea.
c_maker
2
@c_maker su punto sobre mezclar el registro con la lógica empresarial parece una pregunta dedicada. Personalmente todavía no tengo una opinión sólida sobre estos asuntos. En teoría, podemos imaginar algunas mejoras de separación usando AOP e iirc, incluso hay aplicaciones prácticas para este enfoque. En la práctica, sin embargo, me quedo con el enfoque "mixto" y hasta ahora no tuve mayores problemas con él. El desorden del código fuente es un peligro real pero, de nuevo, hasta ahora pude hacer que coexistiera con el código de registro bastante "pacíficamente". Por supuesto, esto requiere cierto esfuerzo.
mosquito
11

¡Registre la excepción solo una vez!

Uno de los puntos de dolor comunes que he notado es iniciar sesión y volver a lanzar una excepción. Como resultado, los archivos de registro contienen las mismas excepciones varias veces en varios niveles de pila.

Nayaki
fuente
5

Aquí hay un antipatrón: hacer dos docenas de campos "genéricos variables" en una tabla de base de datos para rastrear cualquier cosa concebible y luego tener 88 (y contar) diferentes valores de enumeración para diferentes tipos de registros.

Wayne Molina
fuente
+1 - He visto esto. Las "tablas de error" que tienen columnas como string1, string2, string3, string4, string5, donde concatinar todas las columnas dará como resultado un código de error al que no se hace referencia en ninguna documentación. El resultado es un registro que es a la vez confuso e inútil; también conocido como "3rd-party-enterprise-app-with-custom-development-debugging-hell".
Morgan Herlocker
En mi caso, es "sistema de registro enrollado a mano sin tener idea de lo que realmente implica el registro"
Wayne Molina
4

Mi experiencia con los registros es cuanto más grande mejor, pero ser lo suficientemente consistente como para que se pueda filtrar por máquina y poder configurar un nivel de gravedad para cada componente de su aplicación individualmente.

Además, es muy difícil predecir qué registro necesitará para encontrar un error futuro. La mayoría de los lugares obvios para registrar errores se solucionan antes de que el producto salga por la puerta. No es raro que el resultado de un informe de error sea que acaba de agregar el registro para ayudar a diagnosticarlo si vuelve a ocurrir.

Karl Bielefeldt
fuente
2

Un par de notas del lado de operaciones de la casa aquí:

1) Asegúrese de que los registros sean configurables localmente, preferiblemente con una herramienta no más pesada que un editor de texto. La mayoría de las veces no queremos obtener el registro de nivel TRACE, pero nos encanta poder activarlo.

2) Si es posible, asegúrese de que los registros se puedan leer con una herramienta que no sea más pesada que un editor de texto. Nada es peor que tener que buscar herramientas a una hora extraña cuando el sistema de producción falla.

Wyatt Barnett
fuente
1

Desde mi propia experiencia trabajando con aplicaciones web:

(y considerando que el almacenamiento es muy barato hoy en día)

  • Registre tanta información disponible (en ese mismo momento) como pueda.
  • Siempre incluyo DateTime.Now en mis cadenas de registro.
  • Siempre (si es posible) registro el tiempo de duración de alguna "acción" específica.
  • Sea coherente con sus cadenas de registro. Como siempre uso este tipo de patrón:

    • "[Información X] [Información Y] [Información Z] [etc.]"
sabiland
fuente
1

Además del stacktrace, registre el estado actual de la aplicación y la entrada.

El software es determinista, estos dos son generalmente lo único que necesita para reproducir el error. El almacenamiento del estado completo puede ser problemático en algunos casos, por lo que las formas de reproducir el estado actual, por ejemplo, mediante entradas anteriores, también son buenas.

Por supuesto, más datos siempre es mejor, pero como mínimo estos dos son un buen comienzo para los bloqueos más fáciles.

ladida
fuente
3
"El software es determinista" => no siempre desafortunadamente. Piense en errores de concurrencia, por ejemplo.
asilias