Si quieres hacer las cosas bien, primero debes afilar tus herramientas.
Muchos programadores pueden olvidar lo importante que es registrar el comportamiento de las aplicaciones. Al encontrar errores concurrentes causados por alta presión en entornos de múltiples subprocesos, puede comprender la importancia de registrar registros.
Algunas personas estaban muy felices de agregar esta oración al código:
log.info ("Registro feliz y despreocupado");
Es posible que ni siquiera se dé cuenta de la importancia de los registros de aplicaciones en el mantenimiento, el ajuste y la identificación de fallas. Creo que SLF4J es la mejor API de registro, principalmente porque admite una excelente manera de inyectar esquema:
log.debug ("Fundir {} registra el filtro de coincidencia: '{}'", registros, filtrar);
Para log4j, solo puedes hacer esto:
log.debug ("encontrado" + registros + "RecordsMatching Filter: '" + filtro + "'");
Este escrito no solo es más vibrante y deficiente legibilidad, sino que también tiene un empalme de cadena que afecta la eficiencia (cuando este nivel no requiere salida).
SLF4J presenta la función de inyección {}, y dado que el empalme de cadena se evita cada vez, no se llamará al método de tostración, y no hay necesidad de agregar ISDEBUGENABLED. SLF4J es una aplicación del modo de apariencia, es solo una fachada. Para una implementación específica, recomiendo el marco logback. Ya lo he anunciado una vez antes, en lugar del Log4J ya completo. Tiene muchas características interesantes. A diferencia de LOG4J, todavía está en desarrollo activo y mejora.
Otra herramienta para recomendar es perf4j:
Perf4j es a System.CurrentTimemillis () como log4j es para system.out.println ()
Al igual que LOG4J es una mejor alternativa a System.out.println, Perf4j es más como un reemplazo para System.CurrentTimemillis (). He introducido perf4J en un proyecto y observé cómo funciona bajo cargas altas. Los administradores y los usuarios comerciales están atónitos por los hermosos gráficos proporcionados por este gadget. Podemos ver los problemas de rendimiento en cualquier momento. Perf4j debería ser un artículo especial para hablar. Ahora, primero puede mirar su guía de desarrolladores. También hay un Ceki Gülcü (creador de Log4J, SLF4J y Logback Project) que nos proporciona una manera fácil de eliminar las dependencias de la registro de los bienes comunes.
No olvides el nivel de registro
Cada vez que desee agregar una línea de registros, pensará, ¿qué nivel de registro debe usarse aquí? Alrededor del 90% de los programadores no prestan mucha atención a este problema. Utilizan un nivel para grabar registros, generalmente información o depuración. ¿Por qué?
El marco de registro tiene dos ventajas principales en comparación con el sistema. Ambos le permiten filtrar registros selectivamente, de forma permanente o simplemente cuando resuelve errores.
Error: se produjo un error grave y debe tratarse de inmediato. Este nivel de error es intolerable para cualquier sistema. Por ejemplo: la excepción del puntero nulo, la base de datos no está disponible, y los casos de uso de rutas críticas no pueden continuar ejecutándose.
Advertencia: El proceso posterior continuará, pero debe tomarse en serio. En realidad, espero que haya dos niveles aquí: uno es el problema obvio con la solución (como "los datos actuales no están disponibles, utilizando datos en caché"), y el otro es el problema potencial y las sugerencias (como "el programa se ejecuta en modo de desarrollo" o "la contraseña de la consola de administración no es lo suficientemente segura". Las aplicaciones pueden tolerar esta información, pero se deben verificar y fijar.
Debug: lo que preocupa los desarrolladores. Más tarde hablaré sobre qué cosas deben grabarse en este nivel.
Trace: información más detallada, solo utilizada en la etapa de desarrollo. Es posible que aún deba prestar atención a esta información dentro de un corto período de tiempo después de que se lance el producto, pero estos registros de registro son solo temporales y eventualmente deben desactivarse. Es difícil distinguir la diferencia entre depuración y rastro, pero si agrega una línea de registros y la elimina después del desarrollo y las pruebas, el registro debe estar en el nivel de rastreo.
La lista anterior es solo una sugerencia, puede iniciar sesión de acuerdo con sus propias reglas, pero es mejor tener ciertas reglas. Mi experiencia personal es: no filtrar registros a nivel de código, pero use el nivel de registro correcto para filtrar rápidamente la información deseada, lo que puede ahorrarle mucho tiempo.
Lo último que debe decir es que esta infame Declaración condicional IS*habilitó. A algunas personas les gusta agregar esto antes de cada registro:
if (log.isdeBugeNabled ()) log.debug ("lugar para su comercial");Personalmente, creo que debes evitar agregar esta cosa desordenada al código. El rendimiento no parece estar muy mejorado (especialmente después de usar SLF4J), que es más como una optimización prematura. Además, ¿no te parece un poco redundante? Raramente, esta declaración de juicio explícita es explícitamente necesaria a menos que demostremos que construir mensajes de registro en sí es demasiado costoso. De lo contrario, puede recordar todo lo que debería y dejar que el marco del registro se preocupe por esto.
¿Sabes lo que estás grabando?
Cada vez que escribe una línea de registros, tómese un momento para ver lo que está imprimiendo en el archivo de registro. Lea su registro y descubra dónde está la excepción. Primero, al menos evite las excepciones de puntero nulo:
log.debug ("Solicitud de procesamiento con id: {}", request.getID ());
¿Ha confirmado que la solicitud no es nula?
Las colecciones de grabación también son un gran pozo. Si usa Hibernate para obtener una colección de objetos de dominio de la base de datos, lo escribe accidentalmente así: log.debug ("Usuarios de retorno: {}", usuarios);
SLF4J solo llamará al método ToString cuando esta declaración se imprima, por supuesto, esto es genial. Sin embargo, si la memoria se desborda, los problemas de selección N+1, el subproceso de hambre de hambre, la excepción de inicialización retrasada, el espacio de almacenamiento de registros se agota ... todo esto puede suceder. La mejor manera es registrar solo la identificación del objeto (o solo el tamaño de la colección). Sin embargo, la recopilación de IDS requiere llamar al método getID para cada objeto, lo que realmente no es una tarea fácil en Java. Groovy tiene un gran operador de expansión (usuarios*.id). En Java podemos usar la Biblioteca Commons Beanutils para simular:
log.debug ("devolviendo ID de usuario: {}", recopilar (usuarios, "id"));
Así es probablemente como se implementa el método de recopilación:
Public Static Collection Collection (Colección Collection, String PropertyName) {return CollectionUtils.collect (colección, nuevo BeantoPropertyValuErformer (Propertyname));}Finalmente, el método de ToString no puede implementarse correctamente o usarse.
Primero, para registrar, hay muchas formas de crear una tostración para cada clase. Es mejor usar ToStringBuilder para generar (pero no la versión de su implementación de reflexión).
En segundo lugar, preste atención a las matrices y conjuntos atípicos. La implementación de tostring de matrices y algunas colecciones alternativas pueden no llamar al método de tostración de cada elemento uno por uno. Se puede utilizar el método de matrices#DeepToString proporcionado por JDK. Consulte los registros que imprimió para ver si hay alguna información sobre la excepción de formato.
Evite los efectos secundarios
La impresión de registro generalmente no tiene mucho impacto en el rendimiento del programa. Recientemente, un amigo mío organizó una excepción hibernada de lacioinitializationException en un sistema que se ejecuta en algunas plataformas especiales. Como puede haber adivinado a partir de esto, algunas impresiones de registro que dan como resultado que se carguen las colecciones de inicialización retrasada cuando la sesión está conectada. En este caso, si se incrementa el nivel de registro, la colección ya no se inicializará. Si no conoce esta información de contexto, ¿cuánto tiempo le llevará descubrir este error?
Otro efecto secundario es que afecta la velocidad de ejecución del programa. Una respuesta rápida a esta pregunta: si los registros se imprimen demasiado o el empalme de tostración y cadena no se usa correctamente, la impresión de registros tendrá un impacto negativo en el rendimiento. ¿Qué tan grande puede ser? Bueno, he visto un programa reiniciar cada 15 minutos porque demasiados registros hacen que los hilos mueran de hambre. ¡Este es el efecto secundario! Desde mi experiencia, imprimir cien megabytes en una hora es casi el límite superior.
Por supuesto, si el proceso de negocio se aborta debido a las excepciones de impresión de registro, este efecto secundario será excelente. A menudo veo personas que escriben esto para evitar esto:
Pruebe {log.trace ("id =" + request.getUser (). getId () + "accesos" + gerente.getPage (). getUrl (). toString ())} catch (nullPointerException e) {}Este es un verdadero código, pero para que el mundo sea más purificado, no lo escriba así.
Describir claramente
Cada registro de registro contiene datos y descripción. Echa un vistazo a este ejemplo:
log.debug ("Mensaje procesado"); log.debug (message.getjmsmessageId ()); log.debug ("Mensaje con id '{}' procesado", message.getjmsmessageId ()); Al solucionar errores de resolución de problemas en un sistema desconocido, ¿qué tipo de registro prefiere ver? Confía en mí, estos ejemplos son comunes. También hay un modo negativo:
if (Mensaje instancia de textMessage)
// ...
demás
log.warn ("tipo de mensaje desconocido");
¿Es difícil agregar tipos de mensajes, ID de mensaje, etc. a este registro de advertencia? Sé que ocurrió un error, pero ¿qué es? ¿Cuál es la información de contexto?
El tercer ejemplo negativo es "Magic Log". Un ejemplo real: ¡muchos programadores en el equipo saben que siguen 3 ≥ números! El número seguido de un número #, seguido de un registro de números pseudo-aleatorio significa "Se ha recibido el mensaje con ID XYZ". Nadie quiere cambiar este registro. Si alguien escribiera el teclado y seleccionara una cadena única "&&&!#", Encontraría rápidamente la información que quería.
El resultado es que todo el archivo de registro parece una gran cadena de caracteres aleatorios. Algunas personas no pueden evitar preguntarse si este es un programa de Perl.
Los archivos de registro deben ser legibles, claros y autodescribidos. No use números mágicos, valores registrados, números, IDS y su contexto. Registre los datos procesados y su significado. Registre lo que está haciendo el programa. Un buen registro debe ser un buen documento del código del programa.
¿He mencionado no imprimir una contraseña y tener información personal? Creo que no hay un programador tan estúpido.
Ajuste su formato
El formato de registro es una herramienta muy útil, que agrega invisiblemente información de contexto valiosa al registro. Pero debe pensar claramente sobre qué tipo de información se incluye en su formato. Por ejemplo, no tiene sentido registrar fechas en registros escritos cada ciclo por hora, porque su nombre de registro ya contiene esta información. Por el contrario, si no registra el nombre del hilo, cuando dos hilos funcionan en paralelo, no podrá rastrear los hilos a través de los registros: los registros se han superpuesto. En una aplicación única, está bien hacer esto, pero eso ya es cosa del pasado.
Desde mi experiencia, el formato de registro ideal debe incluir (excepto la información del registro en sí, por supuesto): hora actual (sin fecha, precisión de milisegundos), nivel de registro, nombre de hilo, nombre de registro simple (no con nombre completo) y mensajes. En logback se verá así:
<Appender name = "stdout"> <coder> <pather>%d {hh: mm: ss.sss}%-5level [%hilo] [%logger {0}]%m%n </mattern> </coder> </pperender>Nombres de archivos, nombres de clase, números de línea, no es necesario enumerarse, aunque parecen útiles. También he visto registro vacío en el código:
log.info (""); Debido a que el programador cree que el número de línea será parte del formato de registro, y él sabe que si el mensaje de registro vacío aparece en la línea 67 del archivo, significa que el usuario ha sido autenticado. No solo eso, los nombres de los métodos de nombre de clase de grabación o los números de línea tienen un gran impacto en el rendimiento.
Una característica más avanzada del marco de registro es el contexto de diagnóstico asignado. MDC es solo un mapa que es local para enhebrar. Puede colocar cualquier par de valores clave en este mapa, para que todos los registros de registro de este hilo puedan obtener información correspondiente de este mapa como parte del formato de salida.
Registre los parámetros y los valores de retorno del método
Si encuentra un error en la etapa de desarrollo, generalmente usa un depurador para rastrear la razón específica. Ahora digamos que ya no usará el depurador. Por ejemplo, debido a que este error apareció en el entorno del usuario hace unos días, todo lo que puede obtener son algunos registros. ¿Qué puedes descubrir de esto?
Si sigue el principio simple de imprimir parámetros de entrada y salida para cada método, no necesita un depurador en absoluto. Por supuesto, cada método puede acceder a sistemas externos, bloquear, esperar, etc., y estos deben tenerse en cuenta. Simplemente consulte el siguiente formato:
public String printDocument (documento doc, modo modo) {log.debug ("ingresar printDocument (doc = {}, mode = {})", doc, mode); String id = ...; // Operación de impresión larga log.debug ("dejar printDocument (): {}", id); ID de retorno;}Dado que registra los registros al principio y al final del método, puede encontrar códigos ineficientes manualmente e incluso detectar causas que pueden causar puntos muertos y hambre, solo necesita ver si no hay "dejar" después de "entrar". Si el significado del nombre de su método está claro, será algo agradable para borrar el registro. Del mismo modo, analizar excepciones es más fácil porque sabes lo que estás haciendo en cada paso. Si hay muchos métodos para grabar en el código, puede usar secciones AOP para completarlo. Esto reduce el código duplicado, pero debe tener mucho cuidado al usarlo, y si no tiene cuidado, puede conducir a una gran cantidad de salida de registro.
Los niveles más adecuados para este tipo de registro son la depuración y el rastro. Si encuentra que un método se llama con demasiada frecuencia y la grabación de su registro puede afectar el rendimiento, solo necesita reducir su nivel de registro o eliminar el registro directamente (¿o solo una de las llamadas de método completa?) Sin embargo, demasiados registros son mejores que menos. Piense en el registro como pruebas unitarias, su código debe cubrirse con registros al igual que sus pruebas unitarias en todas partes. Ninguna parte del sistema no requiere registros en absoluto. Recuerde, a veces necesita saber si su sistema funciona correctamente, y solo puede ver los registros que constantemente inundan la pantalla.
Observe sistemas externos
Esta sugerencia es un poco diferente de la anterior: si se está comunicando con un sistema externo, recuerde registrar los datos salientes y de lectura de su sistema. La integración del sistema es una tarea, y diagnosticar problemas entre dos aplicaciones (imagine diferentes compañías, entornos, equipos técnicos) es especialmente difícil. Recientemente encontramos que la grabación de contenido completo de mensajes, incluidos los encabezados SOAP y HTTP de Apache CXF, es muy efectivo durante la fase de integración y prueba del sistema.
Esto es costoso, y si afecta el rendimiento, solo puede apagar el registro. Pero de esta manera, su sistema puede funcionar muy rápido y colgar rápidamente, ¿por lo que no puede hacer nada al respecto? Al integrarse con sistemas externos, solo puede tener mucho cuidado y estar preparado para sacrificar algo de sobrecarga. Si tiene la suerte y la integración del sistema es manejada por el ESB, es mejor registrar la solicitud y la respuesta en el autobús. Puede consultar este componente de registro de Mule.
A veces, la cantidad de datos intercambiados con sistemas externos determina que es imposible para usted escribir todo. Por otro lado, es mejor mantener todo en el registro durante la fase de prueba y las etapas de liberación temprana y estar preparado para sacrificar el rendimiento. Esto se puede hacer ajustando el nivel de registro. Eche un vistazo a los siguientes consejos:
Colección <integer> requestIds = // ... if (log.isdeBugeNabled ()) log.deBug ("IDS de procesamiento: {}", requestIds); else log.info ("Tamaño de IDS de procesamiento: {}", requests.size ()); Si este registrador está configurado al nivel de depuración, imprime la colección completa de ID de solicitud. Si está configurado para imprimir información de información, solo generará el tamaño del conjunto. Puede preguntarme si olvidé la condición ISInfoenable, consulte la segunda sugerencia. Otra cosa que vale la pena señalar aquí es que el conjunto de ID no puede ser nulo. Aunque puede imprimir normalmente en depuración como nulo, es un gran puntero nulo cuando se configura como información. ¿Recuerdas los efectos secundarios mencionados en la cuarta sugerencia?
Excepciones correctas de registro
Primero, no registre las excepciones, deje que el marco o el contenedor hagan esto. Por supuesto, hay una excepción: si arroja excepciones (RMI, EJB, etc.) desde un servicio remoto, las excepciones se serializan para garantizar que puedan devolverse al cliente (parte de la API). De lo contrario, el cliente recibirá un NoclassDefFoundError u otra excepción extraña en lugar de un mensaje de error real.
La grabación de excepciones es una de las responsabilidades más importantes del registro, pero muchos programadores tienden a usar el registro como una forma de manejar las excepciones. Por lo general, solo devuelven el valor predeterminado (generalmente nulo, 0 o cadena vacía), y fingen que no pasó nada. A veces, primero registran la excepción, luego envuelven la excepción y luego la tiran:
log.error ("Excepción IO", e); arrojar una nueva customexception (e);De esta manera, la información de la pila generalmente se imprimirá dos veces, porque los lugares donde la excepción de MyCustomException se imprime nuevamente. Registro de registros, o envuélvalos y tírelo, no lo use al mismo tiempo, de lo contrario, sus registros se verán confusos.
¿Qué pasa si realmente queremos registrar? Por alguna razón (¿presumiblemente no leyendo API y documentación?), Creo que aproximadamente la mitad del registro está mal. ¿Una pequeña prueba, ¿cuál de las siguientes declaraciones de registro puede imprimir correctamente las excepciones de puntero nulo?
intente {entero x = nulo; ++ x;} capt (excepción e) {log.error (e); // a log.error (e, e); // b log.error ("" + e); // c log.error (e.ToString ()); // d log.error (e.getMessage ()); // e log.error (nulo, e); // f log.error ("", e); // g log.error ("{}", e); // h log.error ("{}", e.getMessage ()); // i log.error ("Error de lectura de lectura de la configuración del archivo:" + e); // j log.error ("Error en el archivo de configuración de lectura:" + E.getMessage ()); // k log.error ("Archivo de configuración de lectura de error", e); // l}¡Es extraño que solo G y L (esto es mejor) sean correctos! A y B no se compilan en SLF4J en absoluto. Otros tirarán la información de rastreo de pila o imprimirán información incorrecta. Por ejemplo, E no imprime nada porque la excepción del puntero nulo en sí no proporciona información de excepción y la información de la pila no se imprime. Recuerde, el primer parámetro suele ser la información de texto, sobre el error en sí. No escriba información de excepción en ella. Saldrá automáticamente después de imprimir el registro, frente a la información de la pila. Pero si desea imprimir esto, por supuesto, debe pasar la excepción al segundo parámetro.
Los registros deben ser legibles y fáciles de analizar
Ahora hay dos grupos de usuarios interesados en sus registros: los humanos (ya sea que usted esté de acuerdo o no, los codificadores están aquí) y las computadoras (generalmente scripts de shell escritos por administradores del sistema). Los registros deben ser adecuados para que ambos usuarios lo entiendan. Si alguien mira el registro de su programa detrás de usted y ve esto:
Entonces definitivamente no siguiste mi consejo. Los registros deben ser tan fáciles de leer y comprender como el código.
Por otro lado, si su programa genera medio GB de registros cada hora, nadie o ningún editor de texto gráfico puede finalizarlos. En este momento, nuestros viejos, Grep, Sed y Awk, vinieron cuando llegaron al campo. Si es posible, es mejor que los registros que grabe deje claro tanto a la computadora. No formatea números, use algunos formatos que hacen que sean regulares, etc., si no es posible, imprima los datos en dos formatos:
log.deBug ("Solicitar TTL establecer a: {} ({})", nueva fecha (ttl), ttl); // request ttl set to: mié 28 de abril 20:14:12 CEST 2010 (1272478452437) String String Duration = DurationFormeTilils.FormaldurationDurationswords (Duración, verdadera, verdadera, verdadera); {} MS ({}) ", DuraciónMillis, Duración); // Importación Tomada: 123456789ms (1 día 10 horas 17 minutos 36 segundos)Las computadoras ven "MS después de 1970 Epoch" Un formato de tiempo de este tipo le agradecerá, mientras que la gente está feliz de ver algo como "1 día 10 horas 17 minutos 36 segundos".
En resumen, el Journal también puede escribirse tan elegante como un poema, si está dispuesto a pensar en ello.
Lo anterior es una recopilación de información sobre el formato de salida de ajuste de registro de Java. Los amigos interesados pueden referirse a ello.