Configurar o tempo de intervalo e imprimir logs regularmente
Depois de receber uma solicitação, o log é impresso regularmente através do log4J. A descrição do requisito é a seguinte: o log precisa ser impresso regularmente e o intervalo de tempo pode ser correspondido. Falando em tempo, antes de tudo, penso na classe DailyrollingFilePpender. Vários horários. De acordo com o DatePattern, você pode consultar a classe SimpleDateFormat. Algumas configurações de tempo comum são as seguintes:
Através da observação, descobri que não há formato de data semelhante a N minutos, então escrevi uma classe personalizada baseada na classe DailyrollingFilePpender. O processo é o seguinte:
1) Copie o código -fonte da classe DailyrollingFilePpender e renomeie -o MinuterollingAppender. Para configurá -lo no log4j.xml, adicione o intervalo de item de configuração e adicione os métodos de conjunto e obtenha;
IntervalTime privado int = 10;
2) Como a classe DailyrollingFileAppender usa a classe RollingCalendar para calcular o próximo tempo de intervalo e precisa passar no intervalo de parâmetro, a classe RollingCalendar é modificada como uma classe interna; Como seu método é calcular o horário da próxima ação de rolagem com base no DatePattern, nenhum outro modo de tempo é necessário neste momento, o método de modificação é o seguinte:
Public Date getNextCheckDate (data agora) {this.setTime (agora); this.set (calendário.second, 0); this.set (calendar.millisEcond, 0); this.add (calendário.Minute, intervaloTime); retornar getTime (); }3) Quando o modo de tempo for correspondido de acordo com a ata, o modo de tempo precisa ser desativado. Altere -o para a final estática e remova os parâmetros DatePattern no método GET, Set e MinuterollingAppender Constructor na resposta.
String estática privada datepattern = "'.'yyyy-mm-dd-hh-mm'.log'";
Da mesma forma, o ComputecheckPeriod (), que serve vários datepatterns, também pode ser excluído; A transformação foi concluída e a categoria de produto final é a seguinte:
pacote net.csdn.blog; importar java.io.file; importar java.io.ioException; importar java.io.InterruptedioException; importar java.text.simpledateFormat; importar java.util.calendar; importar java.util.date; importar 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; /** * Timerável por minuto Appender * * @author coder_xia * * /public class MinuterollingAppender estende o FileAppender { /** * o padrão de data. Por padrão, o padrão está definido como "'.'yyyy-mm-dd" * significa rolagem diária. */ String estática privada datepattern = "'.'yyyy-mm-dd-hh-mm'.log'"; / *** Tempo de intervalo, unidade: minutos*/ private Int intervalTime = 10; /** * O arquivo de log será renomeado para o valor da variável ScheduledFileName * quando o próximo intervalo for inserido. Por exemplo, se o período de rolagem * for uma hora, o arquivo de log será renomeado para o valor de * "ScheduledFileName" no início da próxima hora. * * O tempo de precisão quando uma rolagem ocorre dependendo da atividade de registro. */ String privada ScheduledFilename; /*** Na próxima vez que estimarmos uma rollover deve ocorrer. */ Private Long NextCheck = System.currenttimemillis () - 1; Data agora = new date (); SimpledateFormat sdf; RollingCalendar rc = new RollingCalendar (); /*** O construtor padrão não faz nada. */public minuterollingAppend () {}/** * instancia um <code> MinuterollingAppender </code> e abra o arquivo * projetado por <code> arquivo </code>. O nome do arquivo aberto se tornará o destino * do Appender. */ public MinuterollingAppender (layout Layout, String filename) lança ioexception {super (layout, nome do arquivo, true); ativateOptions (); } / ** * @RETURN O intervalo * / public int getIntervaltime () {retorna intervalTime; } / ** * @param intervaloTime * O intervalo para definir * / public void setIntervalTime (Int intervalTime) {this.intervaltime = intervalTime; } @Override public void ativateOptions () {super.activateOptions (); if (nome do arquivo! = null) {agora.setTime (System.CurrentTimemillis ()); sdf = new SimpleDateFormat (datePattern); Arquivo de arquivo = novo arquivo (nome do arquivo); agendadofilename = nome do arquivo + sdf.format (new Date (file.lastmodified ())); } else {logLog .error ("As opções de arquivo ou datePattern não estão definidas para Appender [" + nome + "]."); }} /*** Rollover o arquivo atual para um novo arquivo. */ void rollover () lança ioexception {string datefilename = filename + sdf.format (agora); // É muito cedo para rolar porque ainda estamos dentro dos limites // do intervalo atual. A rolagem ocorrerá assim que o // do próximo intervalo for atingido. if (agendadofilename.equals (datedfileName)) {return; } // Feche o arquivo atual e renomeie -o para o DateDFileName this.CloseFile (); Target de arquivo = novo arquivo (agendadoFilename); if (Target.Exists ()) {Target.Delete (); } Arquivo de arquivo = novo arquivo (nome do arquivo); resultado booleano = file.renameto (destino); if (resultado) {logLog.debug (nome do arquivo + " ->" + agendadofilename); } else {LogLog.error ("Falha ao renomear [" + FileName + "] para [" + agendadofilename + "]."); } tente {// Isso também fechará o arquivo. Tudo bem, pois várias operações de fechamento são seguras. this.setFile (nome do arquivo, true, this.bufferedio, this.buffersize); } catch (ioexception e) {errorHandler.error ("setFile (" + filename + ", true) CHAMADA FALHADO."); } scheduledfilename = datefilename; } /*** Este método diferencia o MinuterollingAppender de sua super classe. * * <p> * Antes de realmente realmente registrar, esse método verificará se é hora de fazer * uma rolagem. Se for, ele agendará o próximo horário de rolagem e depois * a rolagem. * */ @Override Protected void Subappend (Evento LoggingEvent) {Long n = System.CurrentTimEmillis (); if (n> = nextCheck) {agora.setTime (n); nextCheck = rc.getNextCheckMillis (agora); tente {rollover (); } catch (ioexception ioe) {if (ioof of interruptedioException) {thread.currentThread (). interrupt (); } LogLog.error ("rollover () falhou.", Ioe); }} super.subappend (evento); } /*** RollingCalendar é uma classe auxiliar para MinuterollingAppender. Dado um * tipo de periodicidade e o horário atual, ele calcula o início do próximo intervalo *. * */ classe RollingCalendar estende GregoriaNCalendar {private estático final serialversionuid = -3560331770601814177l; RollingCalendar () {super (); } public long getNextCheckMillis (date agora) {return getNextCheckdate (agora) .gettime (); } public date getNextCheckDate (data agora) {this.setTime (agora); this.set (calendário.second, 0); this.set (calendar.millisEcond, 0); this.add (calendário.Minute, intervaloTime); retornar getTime (); }}}O arquivo de configuração de teste é o seguinte:
<? xml versão = "1.0" coding = "utf-8"?> <! DOCTYPE LOG4J: Sistema de configuração "log4j.dtd"> <log4j: configuração xmlns: log4j = "http://jakarta.apache.org/log4j/"> <pender Names = (htttp://jakarta.apache.org/log4j/ "> <pender Names = (htttp://jakarta.apache.org/log4j/"> <pender Names = "Apnder Names =" Apnder " value="log4jTest.log" /> <param name="Append" value="true" /> <param name="intervalTime" value="2"/> <layout> <param name="ConversionPattern" value="%p %d (%c:%L)- %m%n" /> </layout> </appender> <root> <priority value="debug"/> <appender-ref ref = "myfile"/> </raul> </g4J: Configuration>
Em relação à implementação de tempo, você também pode usar a implementação do timer fornecida pelo Java, que elimina o cálculo e a comparação do tempo toda vez que gravar o log. A diferença é realmente configurar um thread e chamar o método de rolagem. A implementação é a seguinte:
pacote net.csdn.blog; importar java.io.file; importar java.io.ioException; importar java.text.simpledateFormat; importar java.util.date; importar java.util.timer; importar java.util.timertak; importar org.apache.log4j.FileAppender; importar org.apache.log4j.layout; importar org.apache.log4j.helpers.loglog; classe pública TimerTaskrollingAppender estende o FileAppender { /*** o padrão de data. Por padrão, o padrão está definido como "'.'yyyy-mm-dd" * significa rolagem diária. */ String final estática privada datepattern = "'.'yyyy-mm-dd-hh-mm'.log'"; / *** Tempo de intervalo, unidade: minutos*/ private Int intervalTime = 10; SimpledateFormat sdf = new SimpleDateFormat (datePattern); /*** O construtor padrão não faz nada. */public TimerTaskRollingAppend () {}/** * Instancie um <code> TimerTaskRollingAppender </code> e abra o arquivo * projetado pelo <code> FileName </code>. O nome do arquivo aberto se tornará o destino * do Appender. */ public TimerTaskrollingAppender (layout Layout, nome do arquivo de string) lança IoException {super (layout, nome do arquivo, true); ativateOptions (); } / ** * @RETURN O intervalo * / public int getIntervaltime () {retorna intervalTime; } / ** * @param intervaloTime * O intervalo para definir * / public void setIntervalTime (Int intervalTime) {this.intervaltime = intervalTime; } @Override public void ativateOptions () {super.activateOptions (); Timer timer = new Timer (); Timer.Schedule (new LogtimerTask (), 1000, intervalado * 60000); } classe LogtimerTask estende o TimerTask {@Override public void run () {String datefilename = FileName + sdf.format (new Date ()); CloseFile (); Target de arquivo = novo arquivo (DateDFileName); if (Target.Exists ()) Target.Delete (); Arquivo de arquivo = novo arquivo (nome do arquivo); resultado booleano = file.renameto (destino); if (resultado) logLog.debug (nome do arquivo + " ->" + datefilename); else LogLog.error ("falhou em renomear [" + nome do arquivo + "] para [" + datedfilename + "]."); tente {setfile (nome do arquivo, true, bufferio, buffersize); } catch (ioexception e) {errorHandler.error ("setFile (" + filename + ", true) CHAMADA FALHADO."); }}}}}No entanto, existem dois problemas com a implementação acima:
1) Concorrência
Um local onde os problemas de simultaneidade podem ocorrer após ligar para o CloseFile () em Run (), o método ASSPENDEND () Acontece que escreve o log. Neste momento, o arquivo está fechado e o seguinte erro será relatado:
java.io.ioException: o fluxo fechado em sun.nio.cs.streamEncoder.ensureopen (fonte desconhecida) em sun.nio.cs.streamenCoder.write (fonte desconhecida) em sun.outputreamster.wreamEncoder.write (fonte de desconhecida) at java.io.out.Outstream. ..................A solução é relativamente simples. Basta fazer o método run () inteiro () sincronizado e adicionar a palavra -chave sincronizada; No entanto, o autor não resolveu a situação em que o log pode ser perdido se ele realmente quiser escrever e a velocidade de escrita é rápida o suficiente;
O uso do timer a ser implementado é mais simples, mas se as tarefas no timer forem executadas por muito tempo, elas ocuparão exclusivamente o objeto do timer, tornando as tarefas subsequentes incapazes de serem executadas a qualquer momento. A solução também é mais simples. A classe Trex Pool Version Timer ScheduledExecutorService é usada para implementar o seguinte:
/ ** * */ pacote net.csdn.blog; importar java.io.file; importar java.io.ioException; importar java.text.simpledateFormat; importar java.util.date; importar java.util.concurrent.executores; importar 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 ScheduleDexecutorService para implementar os logs de impressão de configuração cronometrados * <p> * * /public class ScheduledeExecutorServicePpender estende o FileAppender { /** * o padrão de data. Por padrão, o padrão está definido como "'.'yyyy-mm-dd" * significa rolagem diária. */ String final estática privada datepattern = "'.'yyyy-mm-dd-hh-mm'.log'"; / *** Tempo de intervalo, unidade: minutos*/ private Int intervalTime = 10; SimpledateFormat sdf = new SimpleDateFormat (datePattern); /*** O construtor padrão não faz nada. */public ScheduleDexecutorServicePpender () {}/** * Instanciar um <code> scheduledExecutorServicePpender </code> e abrir o arquivo * projetado por <code> arquivo </code>. O nome do arquivo aberto se tornará * o destino de OPUT para este Appender. */ public ScheduleDexecutorServiceAppender (layout de layout, nome do arquivo de string) lança ioexception {super (layout, nome do arquivo, true); ativateOptions (); } / ** * @RETURN O intervalo * / public int getIntervaltime () {retorna intervalTime; } / ** * @param intervaloTime * O intervalo para definir * / public void setIntervalTime (Int intervalTime) {this.intervaltime = intervalTime; } @Override public void ativateOptions () {super.activateOptions (); Executores.NewsingLeThreadScheduledExecutor (). ScheduleAtFixedRate (new LogtimerTask (), 1, intervaloTime * 60000, timeUnit.millisEconds); } classe LogtimerTask implementa Runnable {@Override public void run () {string datefilename = filename + sdf.format (new Date ()); CloseFile (); Target de arquivo = novo arquivo (DateDFileName); if (Target.Exists ()) Target.Delete (); Arquivo de arquivo = novo arquivo (nome do arquivo); resultado booleano = file.renameto (destino); if (resultado) logLog.debug (nome do arquivo + " ->" + datefilename); else LogLog.error ("falhou em renomear [" + nome do arquivo + "] para [" + datedfilename + "]."); tente {setfile (nome do arquivo, true, bufferio, buffersize); } catch (ioexception e) {errorHandler.error ("setFile (" + filename + ", true) CHAMADA FALHADO."); }}}}}Em relação à implementação do tempo, este é quase o fim. O padrão é gerar um novo arquivo de log em 10 minutos. Você pode definir você mesmo ao configurá -lo. No entanto, há um perigo oculto. Se a pessoa de configuração não souber que o intervalo de tempo é minutos, se você acha que é segundos, você tem 600 e, em seguida, abra uma depuração para gerar um arquivo de log com G, ele definitivamente será um desastre. A transformação a seguir é combinar o tamanho máximo do rollingfileppender e o número máximo de arquivos de backup para corresponder e, em seguida, melhorá -lo novamente. Da próxima vez, continuaremos descrevendo o processo de transformação.
Adicionar configuração de nome do módulo
Mencionei a implementação da classe personalizada da impressão com tempo de log4j, para não falar sobre o tamanho e o número de arquivos de backup especificados. Eu posso adicioná -lo do código de cópia da classe ROLLINGFILEPPENEPE para a classe personalizada anterior. A única coisa que precisa ser resolvida é o problema de simultaneidade, ou seja, quando o arquivo de renomeação é fechado, um erro de fluxo de saída fechado será relatado quando ocorrer um evento de log.
Agora existe esse cenário de aplicação, e muitas vezes há:
1. O projeto contém vários projetos diferentes;
2. O mesmo projeto contém módulos diferentes.
Para o primeiro caso, você pode configurar o Log4J <CATOGERY = "TEST"> e depois usar o seguinte método ao gerar Logger:
Logger logger = logger.getLogger ("test");No segundo caso, esperamos imprimir módulos diferentes no mesmo arquivo de log, mas esperamos imprimir o nome do módulo no log para localizar o problema quando houver um problema. Portanto, este artigo precisa adicionar o moduleno de configuração à classe Appender. Vamos começar a transformação abaixo. Diferentemente da impressão cronometrada, usamos a classe RollingFilePpender como a classe base para transformação.
Primeiro, adicione o moduleno do item de configuração e adicione os métodos GET e Set;
Como é herdado do RollingFileAppender, você só precisa formatar os dados no LoggingEvent em Subappend (), adicione o método FormatInfo para formatar os dados e o código é omitido;
A categoria final do produto é a seguinte:
pacote net.csdn.blog; importar org.apache.log4j.category; importar org.apache.log4j.rollingFileAppender; importar org.apache.log4j.spi.loggingEvent; / ** * @author coder_xia * */ public class Moduleppender estende RollingFilePpender {private String moduleName; / ** * @return the moduleName */ public string getModuleName () {return moduleName; } / ** * @param ModuleName * O moduleName para definir * / public void setModuleName (String moduleName) {this.moduleName = moduleName; } / ** * Formato Imprimir conteúdo * * @param evento * evento * @return msg * / private string formatinfo (evento 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 (new LoggingEvent (category.class.getName (), event .getLogger (), event.getLevel (), msg, null)); }}