Configurar el tiempo de intervalo e imprimir registros regularmente
Después de recibir una solicitud, el registro se imprime regularmente a través de log4j. La descripción del requisito es la siguiente: el registro debe poder imprimirse regularmente, y el intervalo de tiempo puede coincidir. Hablando de tiempo, en primer lugar, pienso en la clase DailyrollingFilePepperse. Varios tiempos. Según DatePattern, puede consultar la clase SimpleDateFormat. Algunas configuraciones de sincronización comunes son las siguientes:
A través de la observación, descubrí que no hay un formato de fecha similar a N minutos, por lo que escribí una clase personalizada basada en la clase DailyRollingFilePepperse. El proceso es el siguiente:
1) Copie el código fuente de la clase DailyRollingFilePeppers y cambie el nombre de MinuterOllingAppender. Para configurarlo en log4j.xml, agregue el Intervaltime de la configuración del elemento y agregue los métodos establecer y obtener;
privado int intervaltime = 10;
2) Dado que la clase DailyRollingFilePepperse usa la clase RollingCalendar para calcular la siguiente hora de intervalo, y necesita pasar el intervalo de parámetros, la clase RollingCalendar se modifica como una clase interna; Dado que su método es calcular la hora de la próxima acción de reinscripción basada en la fecha de la fecha, no se necesita otro modo de tiempo en este momento, el método de modificación es el siguiente:
Fecha pública getNextCheckdate (fecha ahora) {this.settime (ahora); this.set (calendario.second, 0); this.set (calendario.milliseCond, 0); this.add (calendario.minute, intervaltime); return getTime (); }3) Cuando el modo de tiempo coincide de acuerdo con los minutos, el modo de tiempo debe deshabilitarse. Cámbielo a Static Final y elimine los parámetros de fecha de fecha en el método GET, SET y el constructor MinuterOllingAppender en la respuesta.
String static privado datePattern = "'.'yyyyy-mm-dd-hh-mm'.log'";
Del mismo modo, CompuTecheckPeriod (), que sirve múltiples puntos de fecha, también se puede eliminar; La transformación se ha completado, y la categoría de productos terminados es la siguiente:
paquete net.csdn.blog; import java.io.file; import java.io.ioException; import java.io.interruptedioException; import java.text.simpledateFormat; import java.util.calendar; import java.util.date; import java.util.gregoriancalendar; importar org.apache.log4j.fileAppender; importar org.apache.log4j.layout; importar org.apache.log4j.helpers.loglog; importar org.apache.log4j.spi.loggingEvent; /** * Timerable por minuto Appender * * @Author Coder_XIA * * /public class MinuterollingAppender extiende FileAppender { /** * El patrón de fecha. Por defecto, el patrón se establece en "'.'yyyyy-mm-dd" * que significa reinversión diaria. */ private static string datePattern = "'.'yyyyy-mm-dd-hh-mm'.log'"; / *** Tiempo de intervalo, unidad: minutos*/ private int intervaltime = 10; /** * El archivo de registro se cambiará el nombre al valor de la variable ProchuledFileName * cuando se ingrese el siguiente intervalo. Por ejemplo, si el período de reinversión * es de una hora, el archivo de registro se cambiará el nombre al valor de * "ProchuledFileName" al comienzo de la próxima hora. * * El tiempo de precisión cuando se produce un reinscrito dependiendo de la actividad de registro. */ String private ScheduledFileName; /*** La próxima vez que estimemos que debe ocurrir una reinversión. */ Private Long NextCheck = System.CurrentTimemillis () - 1; Fecha ahora = nueva fecha (); SimpleDateFormat SDF; RollingCalendar rc = new RollingCalendar (); /*** El constructor predeterminado no hace nada. */public MinuterollingAppender () {}/** * Instanciar a <code> MinuterollingAppender </code> y abra el archivo * diseñado por <code> FileName </code>. El nombre de archivo abierto se convertirá en el destino * OUPT para este apéndice. */ public minuterollingAppender (diseño de diseño, nombre de archivo de cadena) lanza ioexception {super (diseño, nombre de archivo, true); activateOptions (); } / ** * @return el intervaltime * / public int getIntervaltime () {return Intervaltime; } / ** * @param intervaltime * el intervaltime para establecer * / public void setIntervaltime (int intervaltime) {this.intervaltime = intervaltime; } @Override public void activateOptions () {super.activateOptions (); if (FileName! = NULL) {Now.setTime (System.CurrentTimemillis ()); sdf = new SimpleDateFormat (datePattern); Archivo archivo = nuevo archivo (nombre de archivo); ProchuledFileName = filename + sdf.format (nueva fecha (file.lastModified ())); } else {loglog .Error ("O opciones de archivo o de fecha no se configuran para appender [" + name + "]."); }} /*** Revuelve el archivo actual en un nuevo archivo. */ void rollover () lanza ioexception {string fatedFileName = filename + sdf.format (ahora); // Es demasiado pronto para rodar porque todavía estamos dentro de los límites // del intervalo actual. Se producirá una reinversión una vez que se alcance el intervalo // siguiente. if (ProchuledFileName.equals (fatedFileName)) {return; } // Cierre el archivo actual y cambie el nombre a DatedFileName this.closeFile (); Archivo de archivo = nuevo archivo (ProchuledFileName); if (target.exists ()) {target.delete (); } Archivo archivo = nuevo archivo (nombre de archivo); Resultado booleano = file.renameto (objetivo); if (resultado) {loglog.debug (nombre de archivo + " ->" + programadofileName); } else {Loglog.error ("Error al cambiar el nombre [" + nombre de archivo + "] a [" + ProchuledFileName + "]."); } try {// Esto también cerrará el archivo. Esto está bien ya que múltiples operaciones de cierre son seguras. this.setFile (nombre de archivo, verdadero, this.bufferedio, this.buffersize); } catch (ioException e) {ErrgeHandler.error ("setFile (" + nombre de archivo + ", true) la llamada fallida"); } ProchuledFileName = DatedFileName; } /*** Este método diferencia a MinuterOllingAppender de su súper clase. * * <p> * Antes de registrar realmente, este método verificará si es hora de hacer * una reinversión. Si es así, programará la próxima hora de reinscripción y luego * reinversión. * */ @Override protegido Void Subappend (Evento LoggingEvent) {Long n = System.CurrentTimemillis (); if (n> = nextCheck) {ahora.setTime (n); nextcheck = rc.getNextCheckmillis (ahora); intente {reucilla (); } Catch (IOException IOE) {if (IOE InstanceOf InterruptedioException) {Thread.CurrentThread (). Interrupt (); } Loglog.error ("rollover () fallido", IOE); }} super.subappend (evento); } /*** RollingCalendar es una clase de ayuda para MinuterOllingAppender. Dado un tipo de periodicidad * y la hora actual, calcula el inicio del siguiente intervalo *. * */ class RollingCalendar extiende Gregoriancalendar {privado estático final Long SerialVersionUid = -3560331770601814177l; RollingCalendar () {super (); } public Long getNextcheckmillis (fecha ahora) {return getNextCheckdate (ahora) .gettime (); } fecha pública getNextCheckdate (fecha ahora) {this.settime (ahora); this.set (calendario.second, 0); this.set (calendario.milliseCond, 0); this.add (calendario.minute, intervaltime); return getTime (); }}}El archivo de configuración de prueba es el siguiente:
<? xml versión = "1.0" encoding = "utf-8"?> <! DocType log4j: Sistema de configuración "log4j.dtd"> <log4j: configuración xmlns: log4j = "http://jakarta.apache.org/log4j/" <name de aperender = "myfile"> <name = "file" file "" valor valor = "log4jTest.log"/> <param name = "append" value = "true"/> <param name = "intervaltime" value = "2"/> <lElout> <Param name = "conversionPathern" value = "%p%d (%c:%l)-%m%n"/> </loutOut> </pender> ref = "myfile"/> </roter> </log4j: configuración>
Con respecto a la implementación de tiempo, también puede usar la implementación del temporizador proporcionada por Java, que elimina el cálculo y la comparación del tiempo cada vez que registra el registro. La diferencia es en realidad configurar un hilo y llamar al método de vuelco. La implementación es la siguiente:
paquete net.csdn.blog; import java.io.file; import java.io.ioException; import java.text.simpledateFormat; import java.util.date; import java.util.timer; import java.util.timerTask; importar org.apache.log4j.fileAppender; importar org.apache.log4j.layout; importar org.apache.log4j.helpers.loglog; clase pública TimeTaskrollingAppender extiende FileApeppender { /*** El patrón de fecha. Por defecto, el patrón se establece en "'.'yyyyy-mm-dd" * que significa reinversión diaria. */ private static final string datePattern = "'.'yyyyy-mm-dd-hh-mm'.log'"; / *** Tiempo de intervalo, unidad: minutos*/ private int intervaltime = 10; SimpleDateFormat sdf = new SimpleDateFormat (datePattern); /*** El constructor predeterminado no hace nada. */public TimeTaskRollingAppender () {}/** * Instancia un <code> TimeTkrollingAppender </code> y abra el archivo * diseñado por <code> FileName </code>. El nombre de archivo abierto se convertirá en el destino * OUPT para este apéndice. */ public TimerTaskRollingAppender (diseño de diseño, nombre de archivo de cadena) lanza ioexception {super (diseño, nombre de archivo, true); activateOptions (); } / ** * @return el intervaltime * / public int getIntervaltime () {return Intervaltime; } / ** * @param intervaltime * el intervaltime para establecer * / public void setIntervaltime (int intervaltime) {this.intervaltime = intervaltime; } @Override public void activateOptions () {super.activateOptions (); Temporizador temporizador = new Timer (); Timer.schedule (nuevo LogTimerTask (), 1000, Intervaltime * 60000); } class logTimerTask extiende TimeTask {@Override public void run () {String DatedFileName = FileName + SDF.Format (new Date ()); CloseFile (); Archivo de archivo = nuevo archivo (fatedFileName); if (target.exists ()) Target.delete (); Archivo archivo = nuevo archivo (nombre de archivo); Resultado booleano = file.renameto (objetivo); if (resultado) loglog.debug (nombre de archivo + " ->" + fatedFileName); else loglog.error ("Error al cambiar el nombre [" + nombre de archivo + "] a [" + DatedFileName + "]."); intente {setfile (nombre de archivo, true, bufferedio, buffersize); } catch (ioException e) {ErrgeHandler.error ("setFile (" + nombre de archivo + ", true) la llamada fallida"); }}}}}Sin embargo, hay dos problemas con la implementación anterior:
1) concurrencia
Un lugar donde pueden ocurrir problemas de concurrencia después de llamar a CloseFile () en Run (), el método Subappend () simplemente escribe el registro. En este momento, el archivo está cerrado y se informará el siguiente error:
java.io.ioException: Stream se cerró en Sun.nio.cs.streamencoder.ensureOpen (fuente desconocida) en sun.nio.cs.streamencoder.write (fuente desconocida) en sun.nio.cs.streamencoder.write (fuente desconocida) en java.io.outputputreamwriter.write (Fuente desconocida) en java.io.writ.writer.writer.writer) en ..............................La solución es relativamente simple. Simplemente haga que todo el método Run () sincronizado y agregue la palabra clave sincronizada; Sin embargo, el autor no ha resuelto la situación en la que se puede perder el registro si realmente quiere escribirlo y la velocidad de escritura es lo suficientemente rápida;
Usar un temporizador para implementar es más simple, pero si las tareas en el temporizador se ejecutan durante demasiado tiempo, ocupará exclusivamente el objeto del temporizador, lo que hace que las tareas posteriores no puedan ejecutarse en ningún momento. La solución también es más simple. La clase de temporizador de la versión de la versión de hilo de hilo, ProgramedExecutorService, se utiliza para implementar lo siguiente:
/ ** * */ paquete net.csdn.blog; import java.io.file; import java.io.ioException; import java.text.simpledateFormat; import java.util.date; import java.util.concurrent.executors; import java.util.concurrent.timeunit; importar org.apache.log4j.fileAppender; importar org.apache.log4j.layout; importar org.apache.log4j.helpers.loglog; /** * @Author Coder_xia * <p> * Use ProgramedExeCuTorService para implementar registros de impresión de configuración cronometrada * <p> * * /Public ClassedExeCutorServiceAppender extiende FileApeppender { /** * el patrón de fecha. Por defecto, el patrón se establece en "'.'yyyyy-mm-dd" * que significa reinversión diaria. */ private static final string datePattern = "'.'yyyyy-mm-dd-hh-mm'.log'"; / *** Tiempo de intervalo, unidad: minutos*/ private int intervaltime = 10; SimpleDateFormat sdf = new SimpleDateFormat (datePattern); /*** El constructor predeterminado no hace nada. */public ScheduledExeCuTorServiceAppender () {}/** * Instanciar a <code> ProchuledExecutorServiceAppender </code> y abra el archivo * diseñado por <code> FileName </code>. El nombre de archivo abierto se convertirá * en el destino de salida para este apéndice. */ public ScheduledExecutorServiceAppender (diseño de diseño, nombre de archivo de cadena) lanza ioexception {super (diseño, nombre de archivo, verdadero); activateOptions (); } / ** * @return el intervaltime * / public int getIntervaltime () {return Intervaltime; } / ** * @param intervaltime * el intervaltime para establecer * / public void setIntervaltime (int intervaltime) {this.intervaltime = intervaltime; } @Override public void activateOptions () {super.activateOptions (); Ejecutors.newsinglethreadscheduledExecutor (). ScheduleAtfixedRate (new LogTimerTask (), 1, Intervaltime * 60000, TimeUnit.milliseConds); } class logTrimerTask implementa runnable {@Override public void run () {string fatedFileName = filename + sdf.format (new date ()); CloseFile (); Archivo de archivo = nuevo archivo (fatedFileName); if (target.exists ()) Target.delete (); Archivo archivo = nuevo archivo (nombre de archivo); Resultado booleano = file.renameto (objetivo); if (resultado) loglog.debug (nombre de archivo + " ->" + fatedFileName); else loglog.error ("Error al cambiar el nombre [" + nombre de archivo + "] a [" + DatedFileName + "]."); intente {setfile (nombre de archivo, true, bufferedio, buffersize); } catch (ioException e) {ErrgeHandler.error ("setFile (" + nombre de archivo + ", true) la llamada fallida"); }}}}}Con respecto a la implementación del tiempo, este es casi el final. El valor predeterminado es generar un nuevo archivo de registro en 10 minutos. Puede configurarlo usted mismo al configurarlo. Sin embargo, hay un peligro oculto. Si la persona de configuración no sabe que el intervalo de tiempo es de minutos, si cree que es segundos, tiene 600 y luego abre una depuración para generar un archivo de registro con G, definitivamente será un desastre. La siguiente transformación es combinar el tamaño máximo de la filtrina y el número máximo de archivos de copia de seguridad para que coincidan, y luego mejorarlo nuevamente. La próxima vez, continuaremos describiendo el proceso de transformación.
Agregar configuración de nombre del módulo
Mencioné la implementación de clase personalizada de la impresión cronometrada de LOG4J, por lo que no hablaré sobre el tamaño y el número de archivos de copia de seguridad especificados. Puedo agregarlo desde el código de copia de la clase RollingFilePepperse a la clase personalizada anterior. Lo único que debe resolverse es el problema de concurrencia, es decir, cuando el archivo de cambio de nombre está cerrado, se informará un error de flujo de salida cerrado cuando ocurra un evento de registro.
Ahora hay tal escenario de aplicación, y a menudo hay:
1. El proyecto contiene múltiples proyectos diferentes;
2. El mismo proyecto contiene diferentes módulos.
Para el primer caso, puede configurar log4j <catogery = "test"> y luego usar el siguiente método al generar registrador:
Logger logger = logger.getLogger ("test");En el segundo caso, esperamos imprimir diferentes módulos en el mismo archivo de registro, pero esperamos imprimir el nombre del módulo en el registro para localizar el problema cuando hay un problema. Por lo tanto, este artículo debe agregar el nombre de modulenado de configuración a la clase Appender. Comencemos la transformación a continuación. A diferencia de la impresión cronometrada, utilizamos la clase RollingFileAppender como clase base para la transformación.
Primero, agregue el elemento de configuración Modulename y agregue los métodos Get and Set;
Dado que se hereda de RollingFileAppender, solo necesita formatear los datos en LoggingEvent en Subappend (), agregar el método FormatInfo para formatear los datos, y se omite el código;
La categoría final del producto es la siguiente:
paquete net.csdn.blog; importar org.apache.log4j.category; importar org.apache.log4j.RollingFilePeppers; importar org.apache.log4j.spi.loggingEvent; / ** * @author coder_xia * */ public class ModulePeppender extiende RollingFileAppender {private String ModuleName; / ** * @return The ModuleName */ public String getModuleName () {return ModuleName; } / ** * @param ModuleName * The ModuleName to set * / public void setModuleName (string moduleName) {this.moduleName = moduleName; } / ** * Formato Imprimir contenido * * @param Event * Event * @return msg * / private string formatInfo (event logGingEvent) {StringBuilder sb = new StringBuilder (); if (modulename! = null) {sb.append (moduleName) .append ("|"); sb.append (event.getMessage ()); } return sb.ToString (); } @Override public void Subappend (Evento LoggingEvent) {String msg = formatInfo (evento); super.subappend (nuevo logGingEvent (category.class.getName (), event .getLogger (), event.getLevel (), msg, null)); }}