2010-12-20 8 views
3

Ich muss eine Datei listen, wenn der Inhalt hinzugefügt wird, werde ich die neue Zeile lesen und den Inhalt der neuen Zeile bearbeiten. Die Länge der Datei wird niemals verringert (in Wirklichkeit ist es die Tomcat-Protokolldatei).RandomAccessFile Problem

Ich verwende die folgenden Codes:


import java.io.FileNotFoundException; 
import java.io.IOException; 
import java.io.RandomAccessFile; 

import org.apache.log4j.Logger; 

import com.zjswkj.analyser.ddao.LogEntryDao; 
import com.zjswkj.analyser.model.LogEntry; 
import com.zjswkj.analyser.parser.LogParser; 

public class ListenTest { 
    private RandomAccessFile raf; 
    private long    lastPosition; 
    private String    logEntryPattern = "^([\\d.]+) (\\S+) (\\S+) \\[([\\w:/]+\\s[+\\-]\\d{4})\\] \"(.+?)\" (\\d{3}) (\\S+) \"([^\"]+)\" \"([^\"]+)\""; 
    private static Logger  log    = Logger.getLogger(ListenTest.class); 

    public void startListenLogOfCurrentDay() { 

     try { 
      if (raf == null) 
       raf = new RandomAccessFile(
         "/tmp/logs/localhost_access_log.2010-12-20.txt", 
         "r"); 
      String line; 
      while (true) { 
       raf.seek(lastPosition); 
       while ((line = raf.readLine()) != null) { 
        if (!line.matches(logEntryPattern)) { 
         // not a complete line,roll back 
         lastPosition = raf.getFilePointer() - line.getBytes().length; 
         log.debug("roll back:" + line.getBytes().length + " bytes"); 
         if (line.equals("")) 
          continue; 
         log.warn("broken line:[" + line + "]"); 
         Thread.sleep(2000); 
        } else { 
         // save it 
         LogEntry le = LogParser.parseLog(line); 
         LogEntryDao.saveLogEntry(le); 
         lastPosition = raf.getFilePointer(); 
        } 
       } 
      } 
     } catch (FileNotFoundException e) { 
      log.error("can not find log file of today"); 
     } catch (IOException e) { 
      log.error("IO Exception:" + e.getMessage()); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 

    public static void main(String[] args) { 
     new ListenTest().startListenLogOfCurrentDay(); 
    } 
} 

Nun, mein Problem ist, dass, wenn eine Linie, die in die Akten der neuen Zeile geschrieben wird nicht abgeschlossen ist, eine tote Schleife auftreten.

Zum Beispiel, wenn der Kater in die Datei eine neue Zeile zu schreiben versuchen:

10.33.2.45 - - [08/Dec/2010:08:44:43 +0800] "GET /poi.txt HTTP/1.1" 200 672 "-" "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8" 

Und wenn nur ein Teil der Zeile geschrieben (zum Beispiel: < 10.33.2.45 - - [08/Dez/2010: 08: 44: 43 +0800] "GET /poi.txt HTTP/1.1" 200 672>), jetzt, da es nicht mit dem Muster übereinstimmen kann, das ich definiert habe, das heißt, tomcat schließt das Schreiben nicht ab arbeite, also werde ich versuchen, den filepointer zurückzusetzen und 2 Sekunden schlafen und dann wieder lesen.

Während der Ruhezeit, der letzte Teil der Zeile geschrieben vielleicht noch (in der Tat schreibe ich sie eher als Tomcat für Test), meiner Meinung nach randomaccessfile liest eine neue Zeile, die das Muster übereinstimmen kann, aber es scheint nicht.

Jeder kann die Codes überprüfen lassen?

HINWEIS: das Format der Protokolldatei „combined“ wie folgt aus:

10.33.2.45 - - [08/Dec/2010:08:44:43 +0800] "GET /poi.txt HTTP/1.1" 200 672 "-" "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8" 
+0

verwenden Sie versuchen, eine Prämie anbieten können. Auch, bitte tun Sie das nicht. – Will

+0

Post die Beispiel-Log-Datei mit mehr als 1 Zeile in den Protokollen und auch ich bin nicht klar, was genau das Problem ist –

Antwort

3

Ich sehe (von Ihrem Code), dass Ihr Hauptziel darin besteht, die log entries/events zu filtern und dann die gefilterten Protokolle in die Datenbank zu schreiben. Sie haben 2 Optionen

Option 1:Beste und der richtige Weg zu tun. Aber Sie sollten in der Lage sein, die log4j-Konfigurationsdatei zu ändern, die mit Tomcat

ist Wenn dies der Fall ist, ist der beste Weg, dies zu tun, die vordefinierten Erweiterungspunkte Log4j verwenden. In Ihrem Fall ist die Entnahmestelle der Appender

Log4j kommt bereits mit dem DBAppender, dass Sie die Protokolle mit Ihrem regulären Ausdruck zu filtern und dann delegieren den Rest DBAppender könnte erweitert werden soll, da es gut getestet wird. Unten ist ein Beispiel dafür, wie appender

log4j.rootLogger = DEBUG, S

log4j.appender.S = com.gurock.smartinspect.log4j die custome zu konfigurieren.MyCustomAppender

log4j.appender.S.layout = org.apache.log4j.SimpleLayout

Ich schlage vor, Sie AsyncAppender und DBAppender betrachten auch verwenden, wenn Sie die Leistung verbessern wollen.

Option 2: Fallback-Option, wenn Sie

Statt Ihre eigene Datei ändern Hörer zu schreiben, schauen this post in SO haben keinen Zugriff auf die Datei log4j config Katers. Wählen Sie diejenige, die Ihren Bedürfnissen am besten entspricht. Sie haben dann nur noch Schreibcode zum Filtern und Behalten der Log-in-DB. Sie können diese link as an example für den Umgang mit RandomAccessFile verwenden.

+0

Meinst du das Tomcat-Protokoll wird von log4j generiert, die Protokolle werden in den localhost_access_log.2010-12-20 geschrieben .txt, und es kann auch in db geschrieben werden? Bevor ich zu db geforscht habe, kann ich filtering machen? – hguser

+0

Ich rate Tomcat verwendet auch log4j, um die Protokolle zu generieren. Wenn ja, dann können Sie filtern und schreiben Sie es in die db, indem Sie meinen Beitrag folgen. Wenn die Protokolle nicht von log4j generiert werden, bleibt die Option 2. –

0

Ich denke, es ist zu überprüfen, neu aufgenommener Linien kein guter Weg ist. Ich empfehle Ihnen, einen benutzerdefinierten Appender für log4j zu schreiben. Mit einem benutzerdefinierten Appender können Sie jede neu hinzugefügte Zeile mit einem Event versehen. Es gibt ein Beispiel here

Und Google für benutzerdefinierte appender.

0

Das erste, was ich in dieser Situation tun würde, war, das Problem des Lesens einer wachsenden Datei von der Frage der Verarbeitung der Zeilen zu trennen.

Erstellen Sie eine Klasse GrowingFileReader, deren readLine Methode funktioniert, was Sie wollen. Dann wird der Rest des Codes einfacher.

Im Falle einer fehlgeschlagenen Übereinstimmung, warum aktualisieren Sie lastPosition überhaupt? Sollte es nicht so bleiben wie es ist?

+0

Was ist der Unterschied bei der Verwendung von raf.getFilePointer() - line.getBytes(). Length ;? – hguser

0

RAF's readline ist eine blockierende Methode und ist ineffizient (liest Byte für Byte und führt so viele Systemaufrufe durch). Beachten Sie, dass lines.getBytes(). Length in Ihrem Code nicht genau verwendet werden kann, da die readLine-Methode Zeilenumbrüche/Zeilenumbrüche überspringt Zeichen zurückgeben.

Um BufferedReader auf RAF überprüfen meine Antwort hier https://stackoverflow.com/a/19867481/1282907