1

unix/linux "tail -f" 的Java IO实现也有类似的问题;但该解决方案不适用于每秒生成约 50-100 行的日志文件。

我有一个模拟 Linux 中尾部功能的算法。例如,

File _logFile = new File("/tmp/myFile.txt");
long _filePtr = _logFile.length();

while (true)
{
     long length = _logFile.length();
     if (length < _filePtr)
     {
            // means file was truncated
     }
     else if (length > _filePtr)
     {
            // means something was added to the file
     } 
// we ignore len = _filePtr ... nothing was written to file
}

我的问题是什么时候:“文件中添加了一些东西”(指的是 else if() 语句)。

else if (length > _filePtr)
{
     RandomAccessFile _raf = new RandomAccessFile(_logFile, "r");
     raf.seek(_filePtr);

     while ((curLine = raf.readLine()) != null)
           myTextPane.append(curLine);

        _filePtr = raf.getFilePointer();
        raf.close();
}

程序在 while ((curLine = raf.readLine()).... 运行 15 秒后阻塞!(注意:程序在前 15 秒内运行正确)。

raf.readLine() 似乎永远不会达到 NULL,因为我相信这个日志文件的写入速度非常快,以至于我们进入了“无休止的猫捉老鼠”循环。

模拟 Linux 尾巴的最佳方法是什么?

4

3 回答 3

1

我认为最好根据文件的长度抓取一个字节块,然后释放文件并解析一个 ByteArrayInputStream (而不是尝试直接从文件中读取)。

所以使用 RandomAccessFile#read(byte[]),并使用返回的文件长度来调整缓冲区的大小。您不会总是显示文件的确切结尾,但这种轮询算法是可以预料的。

顺便说一句,这个算法很糟糕——你在一个疯狂的紧密循环中运行 IO 操作——对 File#length() 的调用会阻塞,但不会阻塞很多。期望这个例程让你的应用程序在 CPU 方面崩溃。我不一定为您提供更好的解决方案(嗯 - 实际上,我确实 - 让源应用程序写入流而不是文件 - 但我认识到这并不总是可行的)。

除了上述之外,您可能还需要引入轮询延迟(每个循环使线程休眠 100 毫秒 - 在我看来,您正在向 GUI 显示 - 100 毫秒的延迟不会伤害任何人,并且会大大提高性能的摆动操作)。

好的 - 最后的牛肉:您正在调整一个 Swing 组件(我希望)不是在 EDT 上运行的代码。使用 SwingWorker#invokeLater() 更新您的文本窗格。

于 2010-10-28T04:33:49.973 回答
0

看来我已经找到了问题并创建了解决方案。

在 else if 语句下:

while ((curLine = raf.readLine()) != null)
    myTextPane.append(curLine);

这就是问题所在。myTextPane(它是 JTextPane 的派生类)的 append(String) 方法在每一行 append 上都引发了“setCaretPosition()”,这很糟糕!

这意味着 setCaretPosition() 被称为 50-100 Hz 试图“向下滚动”。这导致了接口的阻塞开销。

一个简单的解决方案是创建一个 StringBuffer 类并附加“curLine”,直到 raf.readLine() 读取为空。

然后,追加 StringBuffer 和瞧…… setCaretPosition() 不再阻塞!

感谢凯文带我走向正确的方向。

于 2010-10-28T14:52:50.477 回答
0

你总是可以执行 tail 程序:

    BufferedReader in = new BufferedReader(new InputStreamReader(
            Runtime.getRuntime().exec("tail -F /tmp/myFile.txt").getInputStream()));
    String line;
    while ((line = in.readLine()) != null) {
        // process line
    }
于 2010-10-28T17:23:20.107 回答