如何在Java中像“Tail -f”一样跟踪文件,而不需要保持文件打开状态(防止重命名/删除)?

15

我想在Java应用程序内部"tail -f"很多日志文件。

我已经通过监视文件大小和最后更新时间,并在文件大小或最后更新时间更改时重复打开文件并读取最后几个字节来使其工作--然后立即关闭它。这似乎存在问题,因为当记录器决定重命名文件时,我可能会一直保持它处于打开状态,这将导致某种问题。

我还希望检测到一个“Rolled”文件的机制比注意到文件大小减小更可靠...这似乎容易出错但不太可能发生。

由于我似乎无法访问文件描述符或其他低级文件实用程序,因此我可能无法重现tail的行为--但是否有任何技巧可以在不“锁定”文件以进行重命名/删除(Windows 7)的情况下读取文件?

我想另一种可能性是实际上产生一个tail -f进程并读取进程输出,但这似乎有点麻烦--我正在扫描大约60个日志文件,其中一些具有大量输出(大多数将处于空闲状态)。


5
如果您正在使用JDK 7,您可以使用java.nio.file.WatchService.poll()来轮询文件。 - Buhake Sindi
真的很棒的类!我认为这将解决检测截断的问题,但我仍然需要打开文件并读取文本的问题(尽管它也许可以让我做到这一点...我需要再仔细看看)。谢谢! - Bill K
我失去了你。是哪个操作系统?Windows 还是 Linux? - Cratylus
Windows 7,也许我使用git中的“Tail”命令让你感到困惑了? - Bill K
3个回答

8

Apache Commons有一个Tailer类,它能做到你想要的吗?如果可以,它的滚动检测机制以及阅读内容都可以满足你的需求。

如果不能,那可能没有办法使用纯Java实现。你需要一些帮助,例如使用C代码,在Windows上利用带有SH_DENYNO参数的fopen允许共享打开,或者通过执行系统命令来调用tail实现。

但是,由于打开日志文件的代码是导致锁定的罪魁祸首,即使这样也可能没有帮助。在这种情况下,唯一真正的选择是更改日志记录方式,因为这是锁定文件的罪魁祸首。Log4j可以使用SocketAppender等方法。


那个Tailer类看起来很棒。我们目前没有使用commons的那部分,但是引入它可能是值得的! - Bill K

6

使用apache.commons.io库

下面是一个简单的示例

public class LogTailTest {

/**
* TailerListener implementation.
*/
static public class ShowLinesListener extends TailerListenerAdapter {
    @Override
    public void handle(String line) {
        System.out.println(line);
    }
}

public static void main(String args[]) {

    TailerListener listener  = new ShowLinesListener();
    File file = new File("./test.log");

    Tailer tailer = new Tailer(file, listener, 1000);
    tailer.run();

    try {
        Thread.sleep(100000);
    } catch(InterruptedException ex) {
        Thread.currentThread().interrupt();
    }

    tailer.stop();
}

}


1
tailer.run() 会阻塞代码,所以你永远无法到达其后面的代码行。需要在单独的线程中运行 Tailer。 - Dave Moten

1
在Linux中,由于锁定只是“advisory”,您不会遇到问题。但在Windows中情况就不同了。 这应该取决于记录器(我假设是log4j)打开文件以进行日志记录的模式。 查看此answer中针对C#的内容,似乎有一个名为dwShareMode的参数可用于此类共享。 我相信NIO是正确的选择。请查看NIO API中是否有dwShareMode参数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接