【问题标题】:RandomAccessFile issueRandomAccessFile 问题
【发布时间】:2010-12-20 03:19:14
【问题描述】:

我必须监听一个文件,当它的内容被添加时,我将读取新行,并处理新行的内容。 文件的长度永远不会减少。(其实就是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"

【问题讨论】:

  • 您可以尝试提供赏金。另外,请不要那样做。
  • 在日志中发布超过 1 行的示例日志文件,我也不清楚究竟是什么问题

标签: java loops io random-access


【解决方案1】:

我看到(从您的代码中)您的主要目标是过滤log entries/events,然后将过滤后的日志写入数据库。你有两个选择

选项 1: 最佳且正确的做法。但是你应该可以更改tomcat自带的log4j配置文件

如果是这种情况,那么最好的方法是使用 log4j 的预定义扩展点。在您的情况下,点击点是Appender

Log4j 已经带有DBAppender,您可能希望扩展它以使用正则表达式过滤日志,然后将其余部分委托给DBAppender,因为它已经过良好测试。下面是一个关于如何配置custome appender的示例

log4j.rootLogger=调试,S

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

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

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

选项 2: 如果您无权访问 tomcat 的 log4j 配置文件,则使用后备选项

与其编写自己的文件更改侦听器,不如查看this post in SO。选择最符合您需求的一种。然后,您只剩下编写用于过滤和持久化数据库中的日志的代码。你可以使用这个link as an example 来处理RandomAccessFile。

【讨论】:

  • 你的意思是tomcat的日志是log4j生成的,日志写到localhost_access_log.2010-12-20.txt,也可以写到db?在探索到db之前,我可以做一些过滤吗?
  • 我猜tomcat也使用log4j来生成日志。如果是,那么您可以按照我的帖子过滤并将其写入数据库。如果日志不是由 log4j 生成的,那么你就剩下选项 2。
【解决方案2】:

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

和谷歌自定义附加程序。

【讨论】:

    【解决方案3】:

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

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

    如果匹配失败,为什么还要更新lastPosition?不应该保持原样吗?

    【讨论】:

    • 使用 raf.getFilePointer()-line.getBytes().length; 有什么区别?
    【解决方案4】:

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

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

    【讨论】:

      猜你喜欢
      • 2018-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多