programacion - Iniciando sesión en Java y en general: ¿Mejores prácticas?
manual de programacion android pdf (8)
Como una adición, creo que es importante referirse a Simple Logging Facade para Java (SLF4J) ( SL4J ). Debido a algunos problemas de utilizar diferentes marcos de registro en partes diversificadas de un gran proyecto, SLF4J es el estándar de facto para resolver un problema y gestionar estas partes con éxito, ¿no es así?
La segunda noción: parece que algunas tareas de la vieja escuela pueden ser sustituidas por Aspect-Oriented-Programming , Spring frmwrk tiene su propia implementation , AOP-approach para el registro considerado here en StackOverflow y here en Spring blog.
A veces, cuando veo mi código de registro, me pregunto si lo estoy haciendo bien. Puede que no haya una respuesta definitiva a eso, pero tengo las siguientes preocupaciones:
Clases de biblioteca
Tengo varias clases de biblioteca que pueden registrar algunos mensajes INFO
. Los errores fatales se informan como excepciones. Actualmente tengo una instancia de registrador estático en mis clases con el nombre de clase como nombre de registro. (Log4j''s: Logger.getLogger(MyClass.class)
)
¿Es este el camino correcto? Tal vez el usuario de esta clase de biblioteca no quiere ningún mensaje de mi implementación o quiere redirigirlos a un registro específico de la aplicación. ¿Debo permitir que el usuario configure un registrador del "mundo exterior"? ¿Cómo manejas esos casos?
Registros generales
En algunas aplicaciones, mis clases pueden querer escribir un mensaje de registro en un registro específico no identificado por el nombre de la clase. (Es decir: HTTP Request log
) ¿Cuál es la mejor manera de hacer tal cosa? Un servicio de búsqueda me viene a la mente ...
Con respecto a la creación de instancias de los registradores, he tenido cierto éxito al utilizar una plantilla de Eclipse Java para configurar mis registradores:
private static Logger log = Logger.getLogger(${enclosing_type}.class);
Esto evita el problema de una remoción de JVM con su rastro de pila, y reduce (trivialmente, quizás) la sobrecarga de la creación del rastro de pila en primer lugar.
Lo mejor de usar una plantilla como esta es que puedes compartirla con tu equipo si deseas establecer un estándar uniforme para los registradores.
Parece que IntelliJ admite el mismo concepto para una variable de plantilla que representa el nombre del tipo adjunto. No veo una manera de hacerlo fácilmente en NetBeans.
En la respuesta de @cletus , él escribió sobre el problema de
if (log.isDebugEnabled()) {
log.debug("val is " + value);
}
que se puede superar mediante el uso de SL4J . Proporciona una ayuda de formateo
log.debug("val is {}", value);
donde el mensaje solo se construye si el nivel es de depuración.
Entonces, hoy en día, se recomienda usar SL4J y su registrador complementario, Logback, por razones de rendimiento y estabilidad.
Estoy revisando los niveles de registro de una aplicación y actualmente estoy detectando un patrón:
private static final Logger logger = Logger.getLogger(Things.class)
public void bla() {
logger.debug("Starting " + ...)
// Do stuff
...
logger.debug("Situational")
// Algorithms
for(Thing t : things) {
logger.trace(...)
}
// Breaking happy things
if(things.isEmpty){
logger.warn("Things shouldn''t be empty!")
}
// Catching things
try {
...
} catch(Exception e) {
logger.error("Something bad happened")
}
logger.info("Completed "+...)
}
Un log4j2-file define un socket-appender, con un fail-over file-appender. Y un apilador de consola. A veces utilizo los marcadores log4j2 cuando la situación lo requiere.
Pensé que una perspectiva extra podría ayudar.
La opción preferida para el tipo de configuración de log4j que está describiendo es usar el archivo de configuración log4j . Esto permite a los usuarios de su implementación hacer exactamente lo que están pidiendo, ya que pueden anular su configuración más adelante con algo más adecuado para su propia implementación. Vea here para una cartilla muy completa.
Las siguientes son las pautas que sigo en todos mis proyectos para garantizar un buen rendimiento. He venido a formar este conjunto de directrices basadas en las aportaciones de diversas fuentes en Internet.
Al igual que hoy, creo que Log4j 2 es, de lejos, la mejor opción para iniciar sesión en Java.
Los puntos de referencia están disponibles here . La práctica que sigo para obtener el mejor rendimiento es la siguiente:
- Evito usar SLF4J en este momento por las siguientes razones:
- Tiene algunos problemas de concurrencia con marcadores que quiero usar para administrar el registro de sentencias de SQL (los marcadores no son tan potentes como slf4j - Consulte el primer comentario de Ralph Goers)
- No es compatible con Java 8 Lambda que, de nuevo, quiero usar para un mejor rendimiento (admite la expresión lambda en el registrador )
- Haga todos los registros regulares utilizando un registrador asíncrono para un mejor rendimiento
- Registre los mensajes de error en un archivo separado utilizando el registrador síncrono porque queremos ver los mensajes de error tan pronto como ocurra
- No utilice información de ubicación, como nombre de archivo, nombre de clase, nombre de método, número de línea en el registro regular porque para obtener esa información, el marco toma una instantánea de la pila y la recorre. Esto afecta el rendimiento. Por lo tanto, use la información de ubicación solo en el registro de errores y no en el registro normal
- Dado que estamos registrando errores en un archivo separado, es muy importante que registremos la información de contexto también en el registro de errores. Por ejemplo, si la aplicación encontró un error al procesar un archivo, imprima el nombre de archivo y el registro del archivo que se está procesando en el archivo de registro de errores junto con stacktrace.
- El archivo de registro debe ser grep-able y fácil de entender. Por ejemplo, si una aplicación procesa registros de clientes en múltiples archivos, cada mensaje de registro debe ser como a continuación:
12:01:00,127 INFO FILE_NAME=file1.txt - Processing starts 12:01:00,127 DEBUG FILE_NAME=file1.txt, CUSTOMER_ID=756 12:01:00,129 INFO FILE_NAME=file1.txt - Processing ends
- Registre todas las declaraciones SQL usando un marcador SQL como se muestra a continuación y use un filtro para habilitarlo o deshabilitarlo:
private static final Marker sqlMarker = MarkerManager.getMarker("SQL"); private void method1() { logger.debug(sqlMarker, "SELECT * FROM EMPLOYEE"); }
- Registre todos los parámetros usando Java 8 Lambdas. Esto guardará la aplicación del mensaje de formato cuando el nivel de registro dado esté deshabilitado:
int i=5, j=10; logger.info("Sample output {}, {}", ()->i, ()->j);
No use concatenación de cadenas. Use el mensaje parametrizado como se muestra arriba
Utilice la recarga dinámica de la configuración de registro para que la aplicación vuelva a cargar automáticamente los cambios en la configuración de registro sin necesidad de reiniciar la aplicación
No use
printStackTrace()
oSystem.out.println()
La aplicación debe cerrar el registrador antes de salir:
LogManager.shutdown();
- Finalmente, para referencia de todos, utilizo la siguiente configuración:
<?xml version="1.0" encoding="UTF-8"?> <Configuration monitorinterval="300" status="info" strict="true"> <Properties> <Property name="filePath">${env:LOG_ROOT}/SAMPLE</Property> <Property name="filename">${env:LOG_ROOT}/SAMPLE/sample </Property> <property name="logSize">10 MB</property> </Properties> <Appenders> <RollingFile name="RollingFileRegular" fileName="${filename}.log" filePattern="${filePath}/sample-%d{yyyy-dd-MM}-%i.log"> <Filters> <MarkerFilter marker="SQL" onMatch="DENY" onMismatch="NEUTRAL" /> </Filters> <PatternLayout> <Pattern>%d{HH:mm:ss,SSS} %m%n </Pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy interval="1" modulate="true" /> <SizeBasedTriggeringPolicy size="${logSize}" /> </Policies> </RollingFile> <RollingFile name="RollingFileError" fileName="${filename}_error.log" filePattern="${filePath}/sample_error-%d{yyyy-dd-MM}-%i.log" immediateFlush="true"> <PatternLayout> <Pattern>%d{HH:mm:ss,SSS} %p %c{1.}[%L] [%t] %m%n </Pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy interval="1" modulate="true" /> <SizeBasedTriggeringPolicy size="${logSize}" /> </Policies> </RollingFile> </Appenders> <Loggers> <AsyncLogger name="com" level="trace"> <AppenderRef ref="RollingFileRegular"/> </AsyncLogger> <Root includeLocation="true" level="trace"> <AppenderRef ref="RollingFileError" level="error" /> </Root> </Loggers> </Configuration>
- Las dependencias requeridas de Maven están aquí:
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>com.lmax</groupId> <artifactId>disruptor</artifactId> <version>3.3.6</version> </dependency> <!-- (Optional)To be used when working with the applications using Log4j 1.x --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-1.2-api</artifactId> <version>2.8.1</version> </dependency>
Probablemente he robado esto de algún lado, pero es lindo.
Reduce el riesgo de mezclar los registradores al copiar y reutilizar el pasado, y es menos de tipear.
En tu código:
private final static Logger logger = LoggerFactory.make();
... y en LoggerFactory:
public static Logger make() {
Throwable t = new Throwable();
StackTraceElement directCaller = t.getStackTrace()[1];
return Logger.getLogger(directCaller.getClassName());
}
(Tenga en cuenta que el stackdump se realiza durante la inicialización. La stacktrace probablemente no será optimizada por la JVM, pero en realidad no hay garantías)
Sus convenciones son bastante estándar y bastante bien (yo).
Lo que hay que observar es la fragmentación de la memoria a partir de llamadas de depuración no censuradas excesivas, por lo que, con Log4J (y la mayoría de los demás marcos de registro de Java), se llega a algo como esto:
if (log.isDebugEnabled()) {
log.debug("...");
}
porque la construcción de ese mensaje de registro (que probablemente no esté usando) podría ser costoso, especialmente si se hace miles o millones de veces.
Su registro de nivel INFO no debe ser demasiado "hablador" (y por lo que dice, parece que no lo es). Los mensajes INFO deben ser significativos y significativos en general, como la aplicación que se inicia y detiene. Cosas que tal vez quiera saber si encuentra un problema. El registro de nivel de depuración / nivel fino se usa más cuando tienes un problema que estás tratando de diagnosticar. El registro de depuración / fino generalmente solo se activa cuando es necesario. La información suele estar todo el tiempo.
Si alguien no quiere mensajes INFO específicos de sus clases, por supuesto pueden cambiar su configuración de log4j para no obtenerlos. Log4j es muy sencillo en este departamento (a diferencia del registro Java 1.4).
En cuanto a su cosa de HTTP, generalmente no he encontrado que sea un problema con el registro de Java porque generalmente una sola clase es responsable de lo que le interesa, por lo que solo debe colocarlo en un solo lugar. En el (raro en mi experiencia) cuando quieres mensajes de registro comunes en clases aparentemente no relacionadas, simplemente pon un token que se pueda grep fácilmente.