Konfigurieren Sie die Intervallzeit- und Druckprotokolle regelmäßig
Nach Erhalt einer Anforderung wird das Protokoll regelmäßig über LOG4J gedruckt. Die Anforderung Beschreibung lautet wie folgt: Das Protokoll muss regelmäßig gedruckt werden können, und das Zeitintervall kann übereinstimmen. Apropos Timing, zunächst denke ich an die DailyrollingFileAppender -Klasse. Verschiedene Timings. Nach dem Datumstattern können Sie sich auf die SimpleDateFormat -Klasse beziehen. Einige gängige Zeiteinstellungen sind wie folgt:
Durch die Beobachtung stellte ich fest, dass es kein Datum -Format gibt, das den n Minuten ähnlich ist, also schrieb ich eine benutzerdefinierte Klasse, die auf der DailyrollingFileAppender -Klasse basiert. Der Prozess ist wie folgt:
1) Kopieren Sie den Quellcode der DailyrollingFileAppender -Klasse und benennen Sie ihn MinuTerollingAppender um. Um es in log4j.xml zu konfigurieren, fügen Sie die Intervalenzzeit für Konfiguration Element hinzu und fügen Sie den Satz hinzu und erhalten Sie Methoden.
private INT -Intervalentime = 10;
2) Da die DailyrollingFileAppender -Klasse die RollingCalendar -Klasse verwendet, um die nächste Intervallzeit zu berechnen, und die Parameterintervalenzzeit übergeben muss, wird die RollingCalendar -Klasse als interne Klasse geändert. Da seine Methode darin besteht, die Zeit der nächsten Rollover -Aktion basierend auf dem Datumspattern zu berechnen, ist zu diesem Zeitpunkt kein anderer Zeitmodus erforderlich. Die Änderungsmethode lautet wie folgt:
öffentliches Datum GetNextCheckdate (Datum jetzt) {this.settime (jetzt); this.set (calendar.second, 0); this.set (calendar.millisecond, 0); this.add (Calendar.minute, Intervaltime); GetTime zurückgeben (); }3) Wenn der Zeitmodus gemäß den Minuten übereinstimmt, muss der Zeitmodus deaktiviert werden. Ändern Sie es in ein statisches Finale und entfernen Sie die Datumspattern -Parameter in der GET -Methode und im Konstruktor für MinuterollingAppender in der Antwort.
private static String datePattern = "'.YYYY-MM-DD-HH-MM'.log'";
In ähnlicher Weise kann ComputerneckPeriod (), das mehreren Datumstopfen dient, ebenfalls gelöscht werden. Die Transformation wurde abgeschlossen und die Kategorie "Ferde Produkt" ist wie folgt:
Paket net.csdn.blog; Import Java.io.file; importieren java.io.ioException; importieren java.io.interruptedioxception; Java.Text.SimpledateFormat importieren; import Java.util.calendar; import Java.util.date; Import Java.util.gregoriancalendar; import org.apache.log4j.fileAppender; import org.apache.log4j.layout; import org.apache.log4j.helpers.loglog; import org.apache.log4j.spi.loggeEvent; /** * Timerable nach Minute Appender * * @Author codier_xia * * /public class minuterollingAppender erweitert FileAppender { /** * Das Datumsmuster. Standardmäßig ist das Muster auf "'.'yyyy-mm-dd" * eingestellt. */ private statische String-DatePattern = "'.'yyyy-mm-dd-hh-mm'.log'"; / *** Intervallzeit, Einheit: Minuten*/ private Int -Intervalentime = 10; /** * Die Protokolldatei wird in den Wert des geplanten fileName * -Variablen umbenannt, wenn das nächste Intervall eingegeben wird. Wenn beispielsweise die Rollover * -Perenz eine Stunde beträgt, wird die Protokolldatei zu Beginn der nächsten Stunde in den Wert von * "afulled fileName" umbenannt. * * Die Präzisionszeit, in der ein Überschlag auftritt, je nach Protokollierungsaktivität. */ private Zeichenfolge afulled fileName; /*** Wenn das nächste Mal ein Rollover schätzt, sollte ein Rollover auftreten. */ privat long NextCheck = System.currentTimemillis () - 1; Datum jetzt = neues Datum (); SimpledateFormat SDF; RollingCalendar RC = New RollingCalendar (); /*** Der Standardkonstruktor tut nichts. */public minuterollingAppender () {}/** * Instantieren Sie einen <code> minuterollingAppender </code> und öffnen Sie die von <code> Dateiname </code> entworfene Datei *. Der geöffnete Dateiname wird zum Ziel für diesen Appender. */ public minuterollingAppender (Layout -Layout, String Dateiname) löst IOException (Super (Layout, Dateiname, true) aus; aktivateOptions (); } / ** * @return die Intervalenzzeit * / public int getIntervaltime () {RückgabeintervalleTime; } / ** * @Param IntervALTime * Die IntervALTIME -TO SET * / public void setIntervaltime (int -IntervALTime) {this.Intervaltime = Intervaltime; } @Override public void activeOptions () {Super.ActivateOptions (); if (Dateiname! SDF = new SimpledateFormat (Datumspattern); Datei Datei = neue Datei (Dateiname); planedFileName = Dateiname + SDF.Format (neues Datum (Datei.LastModified ())); } else {loglog .Eror ("entweder Datei- oder Dateioptionen werden für Appender [" + name + "] nicht festgelegt. }} /*** Überrollen Sie die aktuelle Datei zu einer neuen Datei. */ void rollover () löscht ioException {String datFileName = Dateiname + sdf.format (jetzt); // Es ist zu früh, um uns umzusetzen, weil wir uns immer noch innerhalb der // Grenzen des aktuellen Intervalls befinden. Rollover tritt auf, sobald das nächste Intervall erreicht ist. if (planedFileName.equals (datedFileName)) {return; } // Die aktuelle Datei schließen und in datedFileName this.closeFile () umbenennen; Datei target = new Datei (enderFileName); if (target.exists ()) {target.delete (); } Datei Datei = neue Datei (Dateiname); boolean result = file.renameto (Ziel); if (result) {logLog.debug (Dateiname + " ->" + ungenefileName); } else {logLog.Error ("Es wurde nicht umbenannt [" + Dateiname + "] in [" + ungedFileName + "]."). } try {// Dies schließt auch die Datei. Dies ist in Ordnung, da mehrere // schließende Operationen sicher sind. this.setFile (Dateiname, true, this.bufferedio, this.buffersize); } catch (ioException e) {errorHandler.Error ("setFile (" + fileName + ", true) call fehlgeschlagen."); } teplanedFileName = datedFileName; } /*** Diese Methode unterscheidet MinuTerollingAppender von seiner Superklasse. * * <p> * Bevor Sie tatsächlich angemeldet sind, wird diese Methode prüfen, ob es Zeit ist, * einen Rollover zu machen. Wenn dies der Fall ist, wird die nächste Rollover -Zeit geplant und dann * Rollover. * */ @Override Protected void subAppend (LoggingEvent Ereignis) {long n = system.currentTimemillis (); if (n> = NextCheck) {now.settime (n); NextCheck = rc.getNextCheckmillis (jetzt); versuche {rollover (); } catch (ioException ioe) {if (ioe instanceof interruptedioException) {thread.currentThread (). interrupt (); } Loglog.Error ("rollover () fehlgeschlagen.", IOE); }} Super.subAppend (Ereignis); } /*** RollingCalendar ist eine Helferklasse für MinuterollingAppender. Bei einem * Periodizitätstyp und der aktuellen Zeit berechnet er den Beginn des nächsten * Intervalls. * */ Class RollingCalendar erweitert Gregoriancalendar {private statische endgültige lange Serialversionuid = -3560331770601814177L; RollingCalendar () {Super (); } public Long GetNextCheckmillis (Datum jetzt) {return getNextCheckdate (jetzt) .getTime (); } public date getNextCheckdate (Datum jetzt) {this.settime (jetzt); this.set (calendar.second, 0); this.set (calendar.millisecond, 0); this.add (Calendar.minute, Intervaltime); GetTime zurückgeben (); }}}Die Testkonfigurationsdatei lautet wie folgt:
<? value = "log4JTest.log"/> <param name = "append" value = "true"/> <param name = "IntervalimTime" value = "2"/> <layout> <param name = "ConversionPattern" value = "%p%d (%c:%l)-%M%N"/> </layout> </appender> <- ref = "myFile"/> </root> </log4j: Konfiguration>
In Bezug auf die Timing -Implementierung können Sie auch die von Java bereitgestellte Timer -Implementierung verwenden, wodurch jedes Mal, wenn Sie das Protokoll aufzeichnen, die Berechnung und den Vergleich der Zeit beseitigt. Der Unterschied besteht tatsächlich darin, einen Thread einzurichten und die Rollover -Methode aufzurufen. Die Implementierung ist wie folgt:
Paket net.csdn.blog; Import Java.io.file; importieren java.io.ioException; Java.Text.SimpledateFormat importieren; import Java.util.date; import Java.util.timer; import Java.util.timertask; import org.apache.log4j.fileAppender; import org.apache.log4j.layout; import org.apache.log4j.helpers.loglog; Die öffentliche Klasse TimertaskRollingAppender erweitert FileAppender { /*** Das Datumsmuster. Standardmäßig ist das Muster auf "'.'yyyy-mm-dd" * eingestellt. */ private statische endgültige String-Datumspattern = "'.YYYY-MM-DD-HH-MM'.Log'"; / *** Intervallzeit, Einheit: Minuten*/ private Int -Intervalentime = 10; SimpleDateFormat SDF = new SimpledateFormat (Datumspattern); /*** Der Standardkonstruktor tut nichts. */public timerTaskRollingAppender () {}/** * Instantieren Sie einen <code> timerTaskRollingAppender </code> und öffnen Sie die von <code> Dateiname </code> entworfene Datei *. Der geöffnete Dateiname wird zum Ziel für diesen Appender. */ public timerTaskRollingAppender (Layout -Layout, String Dateiname) löst IOException {Super (Layout, Dateiname, true) aus; aktivateOptions (); } / ** * @return die Intervalenzzeit * / public int getIntervaltime () {RückgabeintervalleTime; } / ** * @Param IntervALTime * Die IntervALTIME -TO SET * / public void setIntervaltime (int -IntervALTime) {this.Intervaltime = Intervaltime; } @Override public void activeOptions () {Super.ActivateOptions (); Timer Timer = new Timer (); Timer.Schedule (neuer logtimerTask (), 1000, Intervaltime * 60000); } class logTimerTask erweitert TimerTask {@Override public void run () {String datefileName = Dateiname + SDF.Format (new Date ()); CloseFile (); Datei target = neue Datei (datedFileName); if (target.exists ()) target.delete (); Datei Datei = neue Datei (Dateiname); boolean result = file.renameto (Ziel); if (Ergebnis) loglog.debug (Dateiname + " ->" + datedFileName); sonst loglog.Error ("Es wurde nicht umbenannt [" + Dateiname + "] in [" + datedFileName + "]."). Versuchen Sie {setFile (Dateiname, true, bufferedio, buffersize); } catch (ioException e) {errorHandler.Error ("setFile (" + fileName + ", true) call fehlgeschlagen."); }}}}}Es gibt jedoch zwei Probleme mit der obigen Implementierung:
1) Parallelität
Ein Ort, an dem Parallelitätsprobleme auftreten können, nachdem nach dem Aufrufen von closeFile () in run () die methode subAppend () das Protokoll geschrieben hat. In diesem Moment ist die Datei geschlossen und der folgende Fehler wird gemeldet:
Java.io.ioxception: Stream bei Sun.nio.cs.StreamEncoder.ensureOpen (unbekannte Quelle) bei sun.nio.cs.streamencoder.write (unbekannte Quelle) bei sun.nio.cs.streamcoder.write (Unbekannte Quelle) auf java.outputstreamwrite (unbekannte Quelle). ..................Die Lösung ist relativ einfach. Machen Sie einfach die gesamte Run () -Methode synchronisiert und fügen Sie das synchronisierte Schlüsselwort hinzu. Der Autor hat jedoch nicht die Situation gelöst, in der das Protokoll verloren geht, wenn er es wirklich schreiben will und die Schreibgeschwindigkeit schnell genug ist.
Die Verwendung von Timer zum Implementieren ist einfacher, aber wenn die Aufgaben in Timer zu lange ausgeführt werden, werden sie das Timer -Objekt ausschließlich besetzen, sodass die nachfolgenden Aufgaben zu keinem Zeitpunkt ausgeführt werden können. Die Lösung ist auch einfacher. Mit der Thread Pool -Versions -Timer -Klasse ungedExecutorService wird Folgendes implementiert:
/ ** * */ package net.csdn.blog; Import Java.io.file; importieren java.io.ioException; Java.Text.SimpledateFormat importieren; import Java.util.date; import Java.util.concurrent.executors; Import Java.util.Concurrent.TimeUnit; import org.apache.log4j.fileAppender; import org.apache.log4j.layout; import org.apache.log4j.helpers.loglog; /** * @Author codier_xia * <p> * Verwenden Sie planedexecutorService, um zeitgesteuerte Konfigurationsdruckprotokolle zu implementieren. Standardmäßig ist das Muster auf "'.'yyyy-mm-dd" * eingestellt. */ private statische endgültige String-Datumspattern = "'.YYYY-MM-DD-HH-MM'.Log'"; / *** Intervallzeit, Einheit: Minuten*/ private Int -Intervalentime = 10; SimpleDateFormat SDF = new SimpledateFormat (Datumspattern); /*** Der Standardkonstruktor tut nichts. */public planedexecutorServiceAppender () {}/** * Instantieren Sie einen <code> enderExecutorServiceAppender </code> und öffnen Sie die von <code> Dateiname </code> entworfene Datei *. Der geöffnete Dateiname wird * zum Ziel für diesen Appender. */ public afuledexecutorServiceAppender (Layout -Layout, String Dateiname) löst IOException {Super (Layout, Dateiname, true) aus; aktivateOptions (); } / ** * @return die Intervalenzzeit * / public int getIntervaltime () {RückgabeintervalleTime; } / ** * @Param IntervALTime * Die IntervALTIME -TO SET * / public void setIntervaltime (int -IntervALTime) {this.Intervaltime = Intervaltime; } @Override public void activeOptions () {Super.ActivateOptions (); Executors.newsinglethreadScheduledexecutor (). ScheduleatFixedRate (New LogTimerTask (), 1, Intervaltime * 60000, TimeUnit.Milliseconds); } class logTimerTask implementiert runnable {@Override public void run () {String datefileName = Dateiname + SDF.Format (new Date ()); CloseFile (); Datei target = neue Datei (datedFileName); if (target.exists ()) target.delete (); Datei Datei = neue Datei (Dateiname); boolean result = file.renameto (Ziel); if (Ergebnis) loglog.debug (Dateiname + " ->" + datedFileName); sonst loglog.Error ("Es wurde nicht umbenannt [" + Dateiname + "] in [" + datedFileName + "]."). Versuchen Sie {setFile (Dateiname, true, bufferedio, buffersize); } catch (ioException e) {errorHandler.Error ("setFile (" + fileName + ", true) call fehlgeschlagen."); }}}}}In Bezug auf die Umsetzung des Timings ist dies fast das Ende. Die Standardeinstellung besteht darin, in 10 Minuten eine neue Protokolldatei zu generieren. Sie können es selbst einstellen, wenn Sie es konfigurieren. Es besteht jedoch eine versteckte Gefahr. Wenn die Konfigurationsperson nicht weiß, dass das Zeitintervall Minuten beträgt, wenn Sie der Meinung sind, dass es Sekunden beträgt, haben Sie 600 und öffnen ein Debuggen, um eine Protokolldatei mit G zu generieren, es wird definitiv eine Katastrophe sein. Die folgende Transformation besteht darin, die maximale Größe des RollingFileAppender und die maximale Anzahl von Sicherungsdateien zu kombinieren und sie dann erneut zu verbessern. Das nächste Mal werden wir den Transformationsprozess weiter beschreiben.
Modulname -Konfiguration hinzufügen
Ich habe die benutzerdefinierte Klassenimplementierung von LOG4J Timed -Druck erwähnt, sodass ich nicht über die angegebene Größe und Anzahl der Sicherungsdateien sprechen werde. Ich kann es aus dem RollingFileAppender -Kopiercode zum Kopiercode der vorherigen benutzerdefinierten Klasse hinzufügen. Das einzige, was gelöst werden muss, ist das Problem der Parallelität, dh wenn die Umbenennungsdatei geschlossen ist, wird ein geschlossener Fehler des Ausgabestreams angegeben, wenn ein Protokollereignis erfolgt.
Es gibt jetzt ein solches Anwendungsszenario, und es gibt oft:
1. Das Projekt enthält mehrere verschiedene Projekte;
2. Das gleiche Projekt enthält verschiedene Module.
Für den ersten Fall können Sie log4j <catogery = "test"> konfigurieren und dann die folgende Methode beim Generieren von Logger verwenden:
Logger logger = logger.getLogger ("test");Im zweiten Fall hoffen wir, verschiedene Module in dieselbe Protokolldatei zu drucken. Wir hoffen jedoch, den Modulnamen im Protokoll auszudrucken, um das Problem zu finden, wenn ein Problem vorliegt. Daher muss dieser Artikel den Konfigurationsmodulennamen zur Appender -Klasse hinzufügen. Beginnen wir die Transformation unten. Im Gegensatz zum zeitgesteuerten Druck verwenden wir die RollingFileAppender -Klasse als Basisklasse zur Transformation.
Fügen Sie zunächst den Modulennamen des Konfigurationselements hinzu und fügen Sie die GET- und Set -Methoden hinzu.
Da es von RollingFileAppender geerbt wird, müssen Sie die Daten nur in der Protokollierung in subAppend () formatieren, die Formatinfo -Methode hinzufügen, um die Daten zu formatieren, und der Code wird weggelassen.
Die Endproduktkategorie lautet wie folgt:
Paket net.csdn.blog; import org.apache.log4j.category; import org.apache.log4j.rollingFileAppender; import org.apache.log4j.spi.loggeEvent; / ** * @author codier_xia * */ public class moduleAppender erweitert RollingFileAppender {private String modulenname; / ** * @return den ModulenNAME */ public String getModulenName () {return modulename; } / ** * @param modulenName * Der Modulenname zum Set * / public void setMoDulenName (String -Modulenname) {this.modulenName = modulename; } / ** * Formatinhalte drucken * * @param Ereignis * Ereignis * @return msg * / private String -Formatinfo (LoggingEvent Event) {StringBuilder sb = new StringBuilder (); if (modulename! = null) {sb.append (modulename) .Append ("|"); sb.Append (Event.getMessage ()); } return sb.toString (); } @Override public void subAppend (LoggingEvent Ereignis) {String msg = formatinfo (Ereignis); super.subAppend (neuer loggingEvent (category.class.getName (), event .getLogger (), event.getlevel (), msg, null)); }}