Zip ‘n Roll mit log4j

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

Tags: ,

About The Author

Ralf

Other posts by

Author his web site

28

01 2010

1Trackbacks/Pingbacks

  1. Adding Custom Log-Appender to Pax-Logging | notizblog 21 12 12

Your Comment