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 的预定义扩展点。在您的情况下,点击点是Appender

Log4j 已经附带了DBAppender,您可能希望扩展它以使用正则表达式过滤日志,然后将其余部分委托给DBAppender,因为它经过了很好的测试。以下是有关如何配置客户附加程序的示例

log4j.rootLogger=调试,S

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

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

如果你想提高性能,我建议你也考虑使用AsyncAppender和 DBAppender。

选项 2:如果您无权访问 tomcat 的 log4j 配置文件,则回退选项

与其编写自己的文件更改侦听器,不如查看SO 中的这篇文章。选择最符合您需求的一种。然后,您只剩下编写用于过滤和持久化数据库中的日志的代码。您可以使用此链接作为处理 RandomAccessFile的示例。

于 2010-12-24T17:53:47.287 回答
0

在这种情况下,我要做的第一件事是将读取不断增长的文件的问题与处理行的问题分开。

创建一个类GrowingFileReader,其readLine方法可以满足您的需求。然后剩下的代码就变得更简单了。

在匹配失败的情况下,你为什么要更新lastPosition呢?不应该保持原样吗?

于 2010-12-27T00:02:25.960 回答
0

我认为这不是检查新添加行的好方法。我建议您为 log4j 编写一个自定义附加程序。使用自定义附加程序,您可以通过事件获取每个新添加的行。这里有一个样本

和谷歌自定义附加程序。

于 2010-12-24T16:46:21.247 回答
0

RAF 的 readline 是一种阻塞方法,效率低下(逐字节读取并进行如此多的系统调用) 另请注意,在您的代码中 lines.getBytes().length 不能准确使用,因为 readLine 方法会跳过换行符/回车符。

要在 RAF 上使用 BufferedReader,请在此处查看我的答案https://stackoverflow.com/a/19867481/1282907

于 2013-11-08T20:19:27.940 回答