Weitere aktuelle Java-Titel finden Sie bei dpunkt.
 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index


14.5.3

Benutzerdefiniertes Logging: Syslog


Durch den hohen Grad an Modularisierung ist das Logging-Framework relativ einfach erweiterbar und ermöglicht somit die Anpassung des Protokollierungvorgangs. So ist es z. B. relativ einfach möglich, das Logging-Framework derart zu erweitern, dass auch Syslog-Einträge erzeugt werden können. Das J2SDK 1.4 besitzt per Voreinstellung allerdings keine Unterstützung für die Erzeugung von Syslog-Einträgen.

Syslog ist ein Logging-System, das auf Unix-Systemen anzutreffen ist und sich inzwischen zu einem Standard im Bereich der Protokollierung etabliert hat. Es gibt inzwischen sehr viele (auch nicht Unix-spezifische) Anwendungen, die in der Lage sind, Syslog-Einträge zu generieren. Ein großer Vorteil von Syslog ist, dass es eine relativ einfache Schnittstelle nach außen besitzt und auch in verteilten Systemen eingesetzt werden kann, d. h. man kann prinzipiell Syslog-Einträge von verschiedenen Rechnern über das Netz an einen zentralen Rechner schicken und dort sammeln. Das erleichtert die Administration bei verteilten Anwendungen, da man Log-Einträge von verschiedenen Anwendungen an einem Rechner sammeln und dort zentral analysieren kann.

Die Generierung eines Syslog-Eintrags ist recht einfach: Man sendet lediglich ein UDP-Paket an Port 514 eines Rechners, auf dem ein Syslog-Server läuft. Wenn der Server das Logging von anderen Rechnern erlaubt und entsprechend konfiguriert ist, wird der Eintrag dort im lokalen Syslog-Protokoll eingetragen. Der Inhalt des UDP-Pakets darf 1024 Zeichen nicht übersteigen, muss im US7-ASCII Zeichensatz definiert und sollte entsprechend formatiert sein. Über die Formatierung kann man dem Server Informationen wie z. B. Priorität und Art des Pakets (entspricht der Ebene im Java Logging-Framework), Zeit der Erzeugung oder Name des Rechners, auf dem der Protokolleintrag erzeugt wurde, übergeben. Die Formatierung ist jedoch nicht zwingend. Falsch formatierte Pakete werden ebenfalls akzeptiert, es wird jedoch der komplette Inhalt als Nachrichttext interpretiert. Für nicht erkannte Parameter (wie z. B. die Zeit der Erzeugung) werden in solch einem Fall entsprechende Default-Werte eingesetzt.

Für die konkrete Umsetzung mit dem Java Logging-Framework muss nun Folgendes gemacht werden: Formatierer sind immer von der Klasse Formatter abgeleitet und implementieren die Methode format(). Da die Formatierung hier Syslog-spezifisch ist, wird die neue Klasse SyslogFormatter genannt. Folgender Ausschnitt zeigt die format()-Methode:
  public String format(LogRecord rec) {
    StringBuffer message = new StringBuffer();
    try {
      // Priorität
      message.append("<");
      message.append(levelMap.get(rec.getLevel()));
      message.append(">");
      // Datum
      message.append(dateFormatter.format(new Date(rec.getMillis())));
      message.append(" ");
      // Hostname
      message.append(hostname);
      message.append(" ");
      // Message
      message.append(rec.getSourceClassName());
      message.append(".");
      message.append(rec.getSourceMethodName());
      message.append(": ");
      message.append(rec.getLevel());
      message.append(" ");
      message.append(rec.getMessage());
      message.append("\n");
    } catch(Exception ex) {
      ex.printStackTrace();
    }
    // Syslog Nachrichten sind max. 1024 Bytes lang
    if(message.length() > 1024)
      return message.substring(0, 1024);
    return message.toString();
  }

Da die Log-Levels des Logging-Frameworks nicht mit den Syslog-Prioritäten übereinstimmen, muss ein Mapping definiert werden, dass beide aufeinander abbildet. Diese Zuordnung wird im Formatierer einmalig initialisiert und in levelMap gespeichert.

Der benötigte Handler übernimmt die Funktion, einen Protokolleintrag per UDP an einen bestimmten Rechner zu einem definierten Port zu schicken. Hierzu wird die Klasse DatagramHandler implementiert. Der Konstruktor dieser Klasse bekommt den Namen des Zielrechners und die Portnummer übergeben:
  public DatagramHandler(String host, int port)
          throws SocketException, UnknownHostException {
    // Erzeuge DatagramSocket mit Verbindung zum Logging-Host
    socket = new DatagramSocket();
    address = new InetSocketAddress(host, port);
    socket.connect(address);
  }

In der publish()-Methode wird die eigentliche Arbeit durchgeführt:
  public void publish(LogRecord rec) {
    if(isLoggable(rec)) {
      try {
        // Formatiere Nachricht
        Formatter formatter = getFormatter();
        String message = formatter.format(rec);
        byte[] data = message.getBytes();
        // Addressiere Paket und setze Daten
        DatagramPacket packet =
          new DatagramPacket(data, data.length, address);
        // Verschicke das Paket
        socket.send(packet);
      } catch(Exception ex) {
          reportError(ex.getMessage(), ex,
                      ErrorManager.WRITE_FAILURE);
      }
    }
  }

Zunächst wird durch Aufruf von isLoggable()geprüft, ob der neue Protokolleintrag auch wirklich vom Handler verarbeitet werden soll. Falls ja, wird der Formatierer ermittelt und der Protokolleintrag über die format()-Methode formatiert. Anschließend werden die Daten in ein Byte-Array konvertiert und über UDP an den Zielrechner geschickt.

Tritt ein Fehler auf, kann man in Unterklassen von Handler über reportError() die Bearbeitung des Fehlers veranlassen. reportError() leitet den Fehler durch Aufruf der error()-Methode an seinen ErrorManager weiter. Der ErrorManager hat die Aufgabe, auf Fehler bei der Verarbeitung von Protokolleinträgen zu reagieren. Per Voreinstelllung ist ein Default-ErrorManager gesetzt, der den Fehler an die Standardausgabe gibt. Wenn man selbst auf diese Fehler reagieren möchte, kann man eine eigene Klasse von ErrorManager ableiten, die error()-Methode entsprechend überschreiben und mit der Methode setErrorManager() dem Handler zuordnen.

Mit den folgenden Zeilen kann man nun das Logging-Framework initialisieren, damit Protokolleinträge an einen Syslog-Server geschickt werden:
  Logger logger = Logger.getLogger("de.dpunkt.runtime");
  Handler h = new DatagramHandler("syslog.myhost.de", 514);
  h.setFormatter(new SyslogFormatter());
  logger.addHandler(h);
  logger.info("Syslog setup OK");

Material zum Beispiel


 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index

Copyright © 2002 dpunkt.Verlag, Heidelberg. Alle Rechte vorbehalten.