3

ファイルをリッスンする必要があります。その内容が追加されたら、新しい行を読み取り、新しい行の内容に取り組みます。ファイルの長さが短くなることはありません(実際には、Tomcatログファイルです)。

私は次のコードを使用します:


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();
    }
}

さて、私の問題は、ファイルの新しい行に書き込まれている行が完了していない場合、デッドループが発生することです。

たとえば、Tomcatがファイルに新しい行を書き込もうとした場合:

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"

そして、行の一部だけが書き込まれる場合(例:< 10.33.2.45 --- [08 / Dec / 2010:08:44:43 +0800] "GET /poi.txt HTTP / 1.1" 200 672 >)、これで、定義したパターンと一致しないため、つまり、tomcatは書き込み作業を完了しないため、ファイルポインターをロールバックし、2秒間スリープしてから、もう一度読み取ります。

スリープ時間中、行の最後の部分はまだ書き込まれている可能性があります(実際、テスト用にTomcatではなく書き込みます)。私の意見では、randomaccessfileはパターンに一致する新しい行を読み取りますが、そうではないようです。

誰でもコードをチェックすることができますか?

:ログファイルの形式は、次のように「結合」されます。

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"
4

4 に答える 4

3

(コードから)あなたの主な目的は、ログエントリ/イベントをフィルタリングしてから、フィルタリングされたログをデータベースに書き込むことであることがわかります。2つのオプションがあります

オプション1: 最善かつ正しい方法。ただし、tomcatに付属しているlog4j構成ファイルを変更できるはずです。

この場合、これを行う最良の方法は、log4jの事前定義された拡張ポイントを使用することです。あなたの場合、タッピングポイントはアペンダーです

Log4jにはすでにDBAppenderが付属しており正規表現を使用してログをフィルタリングし、十分にテストされているため、残りをDBAppenderに委任するために拡張することができます。以下は、customeアペンダーを構成する方法の例です。

log4j.rootLogger = DEBUG、S

log4j.appender.S = com.gurock.smartinspect.log4j.MyCustomAppender

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

パフォーマンスを向上させたい場合は、 AsyncAppenderとDBAppenderの使用も検討することをお勧めします。

オプション2: Tomcatのlog4j構成ファイルにアクセスできない場合のフォールバックオプション

独自のファイル変更リスナーを作成する代わりに、SOでこの投稿を見てください。ニーズに最適なものを選択してください。そうすれば、DBでログをフィルタリングして永続化するためのコードを書くだけで済みます。このリンクは、 RandomAccessFileを処理するための例として使用できます。

于 2010-12-24T17:53:47.287 に答える
0

新しく追加された行をチェックする良い方法ではないと思います。log4j用のカスタムアペンダーを作成することをお勧めします。カスタムアペンダーを使用すると、イベントで新しく追加されたすべての行を取得できます。ここにサンプルがあります

そして、カスタムアペンダーのためのグーグル。

于 2010-12-24T16:46:21.247 に答える
0

この状況で私が最初に行うことは、増大するファイルの読み取りの問題と行の処理の問題を分離することでした。

メソッドが必要なことを実行するクラスGrowingFileReaderを作成します。readLineそうすれば、残りのコードはより単純になります。

一致しなかった場合、なぜ更新lastPositionするのですか?そのままにしておくべきではないですか?

于 2010-12-27T00:02:25.960 に答える
0

RAFのreadlineはブロッキングメソッドであり、非効率的です(バイトごとに読み取り、非常に多くのシステムコールを実行します)。また、コードラインでは、readLineメソッドが改行/キャリッジリターン文字をスキップするため、getBytes()。lengthを正確に使用できないことに注意してください。

RAFでBufferedReaderを使用するには、ここで私の答えを確認してくださいhttps://stackoverflow.com/a/19867481/1282907

于 2013-11-08T20:19:27.940 に答える