Pregunta

Como Jeff Atwood preguntó:"¿Cuál es su filosofía maderera?¿Debería todo el código estar plagado de .logthis() y .logthat() llamadas?¿O inyectas el registro a posteriori de alguna manera?"

¿Fue útil?

Solución

Mi filosofía de registro se resume fácilmente en cuatro partes:

Auditoría o registro de lógica empresarial

Registre aquellas cosas que deben registrarse.Esto proviene de los requisitos de la aplicación y puede incluir el registro de cada cambio realizado en cualquier base de datos (como en muchas aplicaciones financieras) o el registro de accesos a los datos (como puede ser necesario en la industria de la salud para cumplir con las regulaciones de la industria).

Como esto es parte de los requisitos del programa, muchos no lo incluyen en sus discusiones generales sobre el registro; sin embargo, hay superposiciones en estas áreas y, para algunas aplicaciones, es útil considerar todas las actividades de registro juntas.

Registro de programa

Mensajes que ayudarán a los desarrolladores a probar y depurar la aplicación, y a seguir más fácilmente el flujo de datos y la lógica del programa para comprender dónde pueden existir errores de implementación, integración y otros.

En general, este registro se activa y desactiva según sea necesario para las sesiones de depuración.

Registro de rendimiento

Agregue registros posteriores según sea necesario para encontrar y resolver cuellos de botella en el rendimiento y otros problemas del programa que no provocan que el programa falle, pero que conducirán a un mejor funcionamiento.Se superpone con el registro del programa en caso de pérdidas de memoria y algunos errores no críticos.

Registro de seguridad

Registrar las acciones del usuario y las interacciones con sistemas externos donde la seguridad es una preocupación.Útil para determinar cómo un atacante rompió un sistema después de un ataque, pero también puede vincularse a un sistema de detección de intrusiones para detectar ataques nuevos o en curso.

Otros consejos

Trabajo con sistemas críticos de seguridad en tiempo real y el registro es a menudo la única forma de detectar errores raros que solo aparecen cada 53 martes cuando hay luna llena, si me entiendes.Esto te vuelve obsesivo con el tema, así que ahora me disculparé si empiezo a echar espuma por la boca.

Diseño sistemas que son capaces de registrar prácticamente todo, pero no lo activo todo de forma predeterminada.La información de depuración se envía a un cuadro de diálogo de depuración oculto que le marca la hora y la envía a un cuadro de lista (limitado a unas 500 líneas antes de eliminarlo), y el cuadro de diálogo me permite detenerlo, guardarlo en un archivo de registro automáticamente o desviarlo a un depurador adjunto como DBWin32.Esa desviación me permite ver el resultado de depuración de múltiples aplicaciones, todas cuidadosamente serializadas, lo que a veces puede salvarme la vida.Los archivos de registro se eliminan automáticamente cada N días.I usado para usar niveles de registro numérico (cuanto más alto establezca el nivel, más capturará):

  • apagado
  • solo errores
  • básico
  • detallado
  • todo

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 caminar entre toneladas de desechos, y puede ser un tipo particular de transacción u operación. eso causa el error.Si eso requiere que enciendas todo, simplemente estás haciendo tu propio trabajo más difícil.Necesitas algo más detallado.

Ahora estoy en el proceso de cambiar al registro basado en un sistema de banderas.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.Normalmente 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, activado y guardado en un archivo de forma predeterminada.Es demasiado tarde para descubrir que debería haber iniciado sesión DESPUÉS de que se haya producido el error, si ese error sólo ocurre una vez cada seis meses en promedio y no tiene forma de reproducirlo.

El software normalmente se envía con ERROR, BASIC, STATE_CHANGE y EXCEPTION activados, pero esto se puede cambiar en el campo a través del cuadro de 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 el ID del cliente, la ubicación del sistema o lo que sea.Puede obtener una mezcla de archivos de registro provenientes del campo, y necesita algún registro de lo que vino, de dónde y qué versión del sistema estaban ejecutando, que en realidad está en los datos mismos, y no puede confiar en el cliente. /ingeniero de campo para decirle qué versión tienen; es posible que simplemente le digan qué versión CREEN que tienen.Peor aún, pueden informar la versión exe que está en el disco, pero la versión anterior aún se está ejecutando porque olvidaron reiniciar después de reemplazarla.Haga que su código se lo diga por sí mismo.

Ese es mi cerebro tirado...

Creo que siempre, siempre, siempre agregue el registro cuando haya una excepción, incluido el mensaje y el seguimiento de la pila completa.Más allá de eso, creo que es bastante subjetivo si usas los registros con frecuencia o no...

A menudo trato de agregar registros solo en lugares críticos donde lo que estoy registrando rara vez debería llegar; de lo contrario, surge el problema, como mencionó, de registros que crecen demasiado...Es por eso que registrar los casos de error es lo ideal para registrarlos siempre (y es genial poder ver cuándo se están produciendo estos casos de error para poder inspeccionar el problema más a fondo).

Otras cosas buenas para registrar son si tiene aserciones y sus aserciones fallan, regístrelas...por ejemplo, esta consulta debe tener menos de 10 resultados; si es mayor, puede haber un problema, así que regístrela.Por supuesto, si una declaración de registro termina llenando los registros, probablemente sea una sugerencia para ponerla en algún tipo de nivel de "depuración" o para ajustar o eliminar la declaración de registro.Si los troncos crecen demasiado, a menudo terminarás ignorándolos.

Tomo lo que considero un enfoque tradicional;algo de registro, rodeado de definiciones condicionales.Para compilaciones de producción, desactivo las definiciones.

Elijo iniciar sesión deliberadamente sobre la marcha, ya que esto significa que los datos del registro son significativos:

  • Dependiendo del marco de registro, puede agregar información de nivel/gravedad/categoría para que los datos del registro se puedan filtrar.
  • Puede asegurarse de que esté presente el nivel correcto de información, ni demasiada ni muy poca.
  • Al escribir el código, usted sabe cuáles son las cosas más importantes y, por lo tanto, puede asegurarse de que se registren.

El uso de algún tipo de herramienta de inyección de código, creación de perfiles o rastreo para generar registros probablemente generaría registros detallados y menos útiles en los que sería más difícil profundizar.Sin embargo, pueden resultar útiles como ayuda para la depuración.

Empiezo afirmando muchas condiciones en mi código (en C#, usando System.Diagnostics.Assert), pero agrego el registro solo cuando encuentro, mientras depuro o pongo el sistema bajo estrés, que realmente necesito tener una manera de seguir lo que sucede dentro de mi código sin tener un depurador conectado permanentemente.

De lo contrario, prefiero usar la capacidad de Visual Studio para colocar rastros en el código como puntos de interrupción especiales (es decir,inserta un punto de interrupción y hace clic derecho en él, luego selecciona "Cuando se golpea ..." y le dice qué mostrar en ese caso).No es necesario volver a compilar y es fácil habilitar/deshabilitar los seguimientos sobre la marcha.

Si está escribiendo un programa que será utilizado por muchas personas, es mejor tener algún tipo de mecanismo para elegir qué se registrará y qué no.Un argumento a favor de las funciones .logthis() es que pueden ser un excelente reemplazo para los comentarios en línea en algunos casos (si se hacen correctamente).

Además, le ayuda a delimitar EXACTAMENTE dónde se produce un error.

Regístrelos todos y deje que Grep los resuelva.

Estoy de acuerdo con Adam, pero también consideraría registrar cosas de interés o cosas que puedas demostrar como logros como una especie de prueba de que están sucediendo.

Defino una variedad de niveles y paso una configuración con la configuración/invocación.

Si realmente necesita iniciar sesión en su sistema, entonces sus pruebas son una mierda o al menos están incompletas y no son muy exhaustivas.En la medida de lo posible, todo lo que hay en su sistema debe ser una caja negra.Observe cómo las clases principales como String no necesitan registro; la razón principal es que están muy bien probadas y funcionan según lo detallado.No hay sorpresas.

Utilizo el registro como una forma de limitar los problemas que no se reproducen en nuestras pruebas unitarias y mucho menos repetir los mismos pasos proporcionados por el usuario:esos raros fallos que sólo aparecen en algún hardware muy remoto (y a veces, aunque muy raramente, incluso causados ​​por un fallo de controlador o biblioteca de terceros fuera de nuestro control).

Estoy de acuerdo con el comentario de que todo esto debería quedar incluido en nuestro procedimiento de prueba, pero es difícil encontrar más de un millón de códigos base LOC que exijan un código de muy bajo nivel y de rendimiento crítico para cumplir con esos requisitos.No trabajo en software de misión crítica, pero trabajo en la industria gráfica, donde a menudo tenemos que hacer de todo, desde implementar asignadores de memoria hasta utilizar código GPU en SIMD.

Incluso con código muy modular, débilmente acoplado o incluso completamente desacoplado, las interacciones del sistema pueden conducir a entradas y salidas muy complejas con comportamiento que varía entre plataformas, donde ocasionalmente tenemos ese caso extremo que elude nuestras pruebas.Las cajas negras modulares pueden ser muy simples, pero las interacciones entre ellas pueden volverse muy complejas y conducir a casos extremos ocasionales e inesperados.

Como ejemplo de un caso en el que el registro me salvó el trasero, una vez tuve a este usuario extraño con un prototipo de máquina Intel que fallaba.Enumeramos los requisitos mínimos para las máquinas que deberían admitir SSE 4, pero esta máquina en particular cumplió con esos requisitos mínimos y aún no admitía extensiones Streaming SIMD posteriores a SSE 3 a pesar de ser una máquina de 16 núcleos.Descubrir eso rápidamente fue posible mirando su registro que mostraba precisamente el número de línea donde se usaron las instrucciones SSE 4.Ninguno de nosotros en nuestro equipo pudo reproducir el problema y mucho menos otro usuario que participó en la verificación del informe.Idealmente, deberíamos haber escrito código para versiones SIMD anteriores o al menos haber hecho algunas ramificaciones y comprobaciones para asegurarnos de que el hardware cumpliera con los requisitos mínimos, pero queríamos hacer una suposición firme comunicada a través de los requisitos mínimos de hardware para lograr simplicidad y economía.Aquí, quizás, se pueda argumentar que fueron nuestros requisitos mínimos del sistema los que tuvieron el "fallo".

Dada la forma en que uso el registro aquí, tendemos a obtener registros bastante grandes.Sin embargo, el objetivo no es la legibilidad: lo que normalmente es importante es la última línea de un registro enviado con un informe cuando el usuario experimenta un bloqueo de algún tipo que ninguno de nosotros en el equipo (y mucho menos algunos otros usuarios en el mundo) puede reproducirse.

Sin embargo, un truco que empleo regularmente para evitar el spam excesivo de registros es que a menudo es razonable suponer que un fragmento de código que se ejecuta una vez con éxito también lo hará posteriormente (no es una garantía estricta, pero a menudo es una suposición razonable).Así que a menudo empleo un log_once tipo de función para funciones granulares para evitar la sobrecarga de pagar el costo de registro cada vez que se llama.

No esparzo salidas de registros por todos lados (lo haría si tuviera tiempo).Normalmente los reservo más para las zonas que parecen más peligrosas:código que invoca sombreadores GLSL, p.(Los proveedores de GPU varían enormemente en términos de capacidad e incluso en cómo compilan el código), código que utiliza elementos intrínsecos SIMD, código de muy bajo nivel, código que inevitablemente tiene que depender del comportamiento específico del sistema operativo, código de bajo nivel que hace suposiciones sobre el representación de POD (ej.:código que supone 8 bits por byte): el tipo de casos en los que también agregaríamos muchas afirmaciones y comprobaciones de cordura, así como escribiríamos la mayor cantidad de pruebas unitarias.Por lo general, esto es suficiente, y el registro me ha salvado el trasero muchas veces en las que, de otro modo, habría tenido un problema irreproducible y habría tenido que intentar solucionar el problema a ciegas, lo que requirió muchas iteraciones para intentar encontrar una solución al único usuario en el mundo que podría reproducir el problema.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top