Java - RandomAccessFile(模拟Linux tail函数)

发布于 2024-09-29 00:42:09 字数 1155 浏览 4 评论 0原文

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

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

看起来 raf.readLine()永远不会达到 NULL,因为我相信这个日志文件写入的速度太快,以至于我们进入了“无尽的猫和老鼠”循环

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

Java IO implementation of unix/linux "tail -f" has a similar problem; but the solution is not viable for log files that generate about 50-100 lines per second.

I have an algorithm that emulates the tail functionality in Linux. For example,

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
}

My problem is when: "something was added to the file" (referring to the else if() statement).

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

The program blocks at while ((curLine = raf.readLine()).... after 15 seconds of run-time! (Note: that the program runs right for the first 15 seconds).

It appears that raf.readLine() is never hitting NULL, because I believe this log file is being written so fast that we go into an "endless cat and mouse" loop.

What's the best way to emulate Linux's tail?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

扬花落满肩 2024-10-06 00:42:09

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

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

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

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

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

I would think that you would be best served by grabbing a block of bytes based on the file's length, then release the file and parse a ByteArrayInputStream (instead of trying to read directly from the file).

So use RandomAccessFile#read(byte[]), and size the buffer using the returned file length. You won't always show the exact end of the file, but that is to be expected with this sort of polling algorithm.

As an aside, this algorithm is horrible - you are running IO operations in a crazy tight loop - the calls to File#length() will block, but not very much. Expect this routine to take your app to it's knees CPU-wise. I don't necessarily have a better solution for you (well - actually, I do - have the source application write to a stream instead of a file - but I recognize that isn't always feasible).

In addition to the above, you may want to introduce a polling delay (sleep the thread by 100ms each loop - it looks to me like you are displaying to a GUI - a 100ms delay won't hurt anyone, and will greatly improve the performance of the swing operations).

ok - final beef: You are adjusting a Swing component from what (I hope) is code not running on the EDT. Use SwingWorker#invokeLater() to update your text pane.

御守 2024-10-06 00:42:09

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

在 else if 语句下:

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

这就是问题所在。 myTextPane(JTextPane 的派生类)的append(String) 方法在每一行追加上引发“setCaretPosition()”,这是错误的!

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

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

然后,附加 StringBuffer 并瞧... setCaretPosition() 不再阻塞!

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

It appears I have found the problem and created a solution.

Under the else if statement:

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

This was the problem. the append(String) method of myTextPane (which is a derived class of JTextPane) evoked "setCaretPosition()" on every line append which IS BAD!!

That meant that setCaretPosition() was called 50-100 Hz trying to "scroll down." This caused a blocking overhead to the interface.

A simple solution was to create a StringBuffer class and append "curLine" until raf.readLine() read null.

Then, append the StringBuffer and voila ... no more blocking from setCaretPosition()!

Thanks to Kevin for bringing me towards the correct direction.

月竹挽风 2024-10-06 00:42:09

您始终可以执行 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
    }

You could always exec the tail program:

    BufferedReader in = new BufferedReader(new InputStreamReader(
            Runtime.getRuntime().exec("tail -F /tmp/myFile.txt").getInputStream()));
    String line;
    while ((line = in.readLine()) != null) {
        // process line
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文