Wer mit rotierenden Logfiles arbeitet und keinen Festplattenplatz zu verschwenden hat, kann mit ein paar Zeilen Code Apaches log4j zur Komprimierung der rollierten Logs bewegen.
Der Einsatz von rollierenden Logs ist weit verbreitet und ein wertvolles Hilfsmittel zur Analyse von Systemen und Applikationen. Logfiles können dabei nach unterschiedlichen Kriterien rolliert werden (Datum, Grösse, etc.). Allerdings kann der dadurch generierte Mehrwert schnell zerstört werden, wenn die Logfiles mit der Zeit zu viel Festplattenspeicher für sich beanspruchen. Um dem entgegen zu wirken sollte man die rollierten Logs entsprechend komprimieren.
Für die Komprimierung der Logs können unterschiedliche Strategien verfolgt werden: Komplettlösungen wie beispielsweise logrotate übernehmen im Linux-Umfeld Rotation und Komprimierung der Logs. Ansonsten werden zumeist selbsterstellte Skripts genutzt. Diese Ansätze sind aber nicht immer praktikabel, da sie entweder für die eigene Plattform nicht verfügbar sind, eventuell sehr aufwendig sind oder gar mit der gewünschten Rotationsstrategie kollidieren.
Im Java-Umfeld erfreut sich Apaches mächtiges Logging-Framework log4j großer Beliebtheit. Neben vielen anderen gibt es auch hier sog. Appender zur Log-Rotation. Allerdings wurde hier keine Komprimierung integriert. Mit Hilfe von Java-Bordmitteln aus dem Package java.util.zip kann man das relativ unspektakulär ändern…
Kurz gesagt: Der RollingFileAppender wird zum RollingZipAppender
Um das zu erreichen muss man lediglich von der Klasse RollingFileAppender ableiten, ein wenig ZIP-Funktionalität hinzufügen und die Methode rollOver() überschreiben…
... public class RollingZipAppender extends RollingFileAppender { @Override public void rollOver() { File target; File file; if (qw != null) { LogLog.debug("rolling over count=" + ((CountingQuietWriter) qw).getCount()); } LogLog.debug("maxBackupIndex="+maxBackupIndex); // If maxBackups <= 0, then there is no file renaming to be done. if(maxBackupIndex > 0) { // Delete the oldest file, to keep Windows happy. file = new File(fileName + '.' + maxBackupIndex + ".zip"); //modified by me if (file.exists()) file.delete(); // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2} for (int i = maxBackupIndex - 1; i >= 1; i--) { file = new File(fileName + "." + i + ".zip"); //modified by me if (file.exists()) { target = new File(fileName + '.' + (i + 1) + ".zip"); //modified by me LogLog.debug("Renaming file " + file + " to " + target); file.renameTo(target); } } // Rename fileName to fileName.1 target = new File(fileName + "." + 1); this.closeFile(); // keep windows happy. file = new File(fileName); LogLog.debug("Renaming file " + file + " to " + target); file.renameTo(target); //added by me - call ZIP facility boolean archiveResult = archiveFile(target); if(archiveResult) { target.delete(); } else { LogLog.error("Failed to zip file ["+target.getPath()+"]."); } } try { // This will also close the file. This is OK since multiple // close operations are safe. this.setFile(fileName, false, bufferedIO, bufferSize); } catch(IOException e) { LogLog.error("setFile("+fileName+", false) call failed.", e); } } ...
Die modifizierte Methode rollOver()
Die Methode archiveFile() übernimmt mithilfe des java.util.zip Package das Komprimieren der Logfiles.
Der Code dafür sieht wie folgt aus…
... // archive log file boolean archiveFile(File logFile) { FileOutputStream fOut; ZipOutputStream zOut; // necessary because of possible IOException try { fOut = new FileOutputStream(logFile.getPath()+".zip"); zOut = new ZipOutputStream(fOut); FileInputStream fIn = new FileInputStream(logFile); BufferedInputStream bIn = new BufferedInputStream(fIn); // new ZipEntry to put into the archive ZipEntry entry = new ZipEntry(logFile.getCanonicalFile().getName()); zOut.putNextEntry(entry); // create a byte array byte[] barray = new byte[1024]; // byte count variable int bytes; // read the BufferedInputStream and write it entirely to the archive while((bytes = bIn.read(barray, 0, 1024)) > -1) { zOut.write(barray, 0, bytes); } // clean up zOut.flush(); zOut.close(); fOut.close(); return true; } catch (IOException ioE) { return false; } } ...
Die Methode archiveFile()
Der Custom-Appender kann jetzt – wie vom RollingFileAppender gewohnt – konfiguriert werden…
... <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/";> <appender name="DefaultAppender" class="mypackage.RollingZipAppender"> <param name="File" value="mylog.log" /> <param name="Append" value="true" /> <param name="MaxFileSize" value="3000KB" /> <layout> <param name="ConversionPattern" value="%d{DATE} %-5p %-15c{1}: %m%n"/> </layout> </appender> ...
log4j.xml Konfigurationsdatei
Analog dazu kann natürlich auch der DailyRollingFileAppender erweitert werden.
Probiert’s einfach aus