简体   繁体   English

unix / linux的Java IO实现“tail -f”

[英]Java IO implementation of unix/linux “tail -f”

I'm wondering what techniques and/or library to use to implement the functionality of the linux command "tail -f ". 我想知道用什么技术和/或库来实现linux命令“tail -f”的功能。 I'm essentially looking for a drop in add-on/replacement for java.io.FileReader . 我基本上是在寻找java.io.FileReader附加/替换。 Client code could look something like this: 客户端代码可能如下所示:

TailFileReader lft = new TailFileReader("application.log");
BufferedReader br = new BufferedReader(lft);
String line;
try {
  while (true) {
    line= br.readLine();
    // do something interesting with line
  }
} catch (IOException e) {
  // barf
}

The missing piece is a reasonable implementation of TailFileReader . 缺少的部分是TailFileReader的合理实现。 It should be able to read parts of the file that exist before the file is opened as well as the lines that are added. 它应该能够读取文件打开前存在的文件部分以及添加的行。

Take a look at Apache Commons implementation of Tailer class. 看看Apache Commons实现的Tailer类。 It does seem to handle log rotation as well. 它似乎也处理日志轮换。

The ability to continue to read a file, and wait around until the file has some more updates for you shouldn't be that hard to accomplish in code yourself. 能够继续读取文件,并等待文件为您提供更多更新,这本身并不难以在代码中完成。 Here's some pseudo-code: 这是一些伪代码:

BufferedReader br = new BufferedReader(...);
String line;
while (keepReading) {
    line = reader.readLine();
    if (line == null) {
        //wait until there is more of the file for us to read
        Thread.sleep(1000);
    }
    else {
        //do something interesting with the line
    }
}

I would assume that you would want to put this type of functionality in its own Thread, so that you can sleep it and not affect any other areas of your application. 我假设您希望将此类功能放在其自己的Thread中,以便您可以将其休眠并且不会影响应用程序的任何其他区域。 You would want to expose keepReading in a setter so that your main class / other parts of the application can safely shut the thread down without any other headaches, simply by calling stopReading() or something similar. 您可能希望在setter中公开keepReading ,以便您的主类/应用程序的其他部分可以安全地关闭线程,而无需任何其他麻烦,只需调用stopReading()或类似的东西。

Check JLogTailer , which does this logic. 检查JLogTailer ,它执行此逻辑。

The main point in the code is: 代码中的要点是:

public void run() {
    try {
        while (_running) {
            Thread.sleep(_updateInterval);
            long len = _file.length();
            if (len < _filePointer) {
                // Log must have been jibbled or deleted.
                this.appendMessage("Log file was reset. Restarting logging from start of file.");
                _filePointer = len;
            }
            else if (len > _filePointer) {
                // File must have had something added to it!
                RandomAccessFile raf = new RandomAccessFile(_file, "r");
                raf.seek(_filePointer);
                String line = null;
                while ((line = raf.readLine()) != null) {
                    this.appendLine(line);
                }
                _filePointer = raf.getFilePointer();
                raf.close();
            }
        }
    }
    catch (Exception e) {
        this.appendMessage("Fatal error reading log file, log tailing has stopped.");
    }
    // dispose();
}

I've built a short implementation of "tail -f" in Scala some time ago: tailf . 我前段时间在Scala中构建了一个简短的“tail -f”实现: tailf It takes care of file rotation as well and you may define your own logic what to do when it reaches EOF or finds the file has been renamed. 它还负责文件轮换,您可以定义自己的逻辑,当它到达EOF时发生了什么,或者发现文件已被重命名。

You may take a look and port it to Java, since actually there is nothing complex in there. 您可以查看并将其移植到Java,因为实际上没有任何复杂的东西。 Few notes: the main file is Tail.scala and basically it defines FollowingInputStream which takes care of EOF/rename and follow method, which wraps FollowingInputStream into an unbounded enumeration in SequenceInputStream . 几点注意事项:主文件是Tail.scala和基本上它定义FollowingInputStream这需要EOF /重命名和照顾follow方法,它包装FollowingInputStream成无界枚举SequenceInputStream So, as soon as FollowingInputStream ends, SequenceInputStream requests next element from an Enumeration and another FollowingInputStream gets created. 因此,只要FollowingInputStream结束, SequenceInputStream就会从Enumeration请求下一个元素,并创建另一个FollowingInputStream

I stumbled recently over rxjava-file , It is an extension of RxJava . 我最近在rxjava文件上跌跌撞撞,它是RxJava的扩展。 In contrast to the other solutions this makes use of Java's NIO. 与其他解决方案相比,这使用了Java的NIO。

import rx.Observable;
import rx.functions.Action1;
import com.github.davidmoten.rx.FileObservable;

// ... class definition omitted

public void tailLogFile() throws InterruptedException {
    Observable<String> tailer = FileObservable.tailer()
                                .file("application.log") // absolute path
                                .tailText();

    tailer.subscribe(
        new Action1<String>() {
            @Override
            public void call(String line) {
                System.out.println("you got line: " + line);
            }
        },
        new Action1<Throwable>() {
            @Override
            public void call(Throwable e) {
                System.out.println("you got error: " + e);
                e.printStackTrace();
            }
        }
    );

// this solution operates threaded, so something  
// is required that prevents premature termination

    Thread.sleep(120000);
}

If your code only ever will have to run on Unix systems, you may be able to get away with just shelling out and calling tail -f directly. 如果您的代码只需要在Unix系统上运行,那么您可以直接进行shell-out并直接调用tail -f

As a more involved alternative, you could take a look at the implementation of GNU tail and port that over to Java. 作为一个更具参与性的替代方案,您可以查看GNU尾部和端口的实现,以及Java。 (I'm not sure whether this wouldn't already make your code a derivative work, though.) (不过我不确定这是否会使你的代码成为衍生作品。)

Just was faced with the same issue - found the "simplest" implementation here: Java Tail . 刚刚面临同样的问题 - 在这里找到了“最简单”的实现: Java Tail

* Great stuff * - ready for production ;) * 很棒的东西* - 准备好生产;)

I hope the code-citation will not drop some license. 我希望代码引用不会丢失一些许可证。

    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.IOException;

    /**
     * Java implementation of the Unix tail command
     * 
     * @param args[0] File name
     * @param args[1] Update time (seconds). Optional. Default value is 1 second
     * 
     * @author Luigi Viggiano (original author) http://it.newinstance.it/2005/11/19/listening-changes-on-a-text-file-unix-tail-implementation-with-java/
     * @author Alessandro Melandri (modified by)
     * */
    public class Tail {

      static long sleepTime = 1000;

      public static void main(String[] args) throws IOException {

        if (args.length > 0){

          if (args.length > 1)
        sleepTime = Long.parseLong(args[1]) * 1000;

          BufferedReader input = new BufferedReader(new FileReader(args[0]));
          String currentLine = null;

          while (true) {

        if ((currentLine = input.readLine()) != null) {
          System.out.println(currentLine);
          continue;
        }

        try {
          Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
          break;
        }

          }
          input.close();

        } else {
          System.out.println("Missing parameter!\nUsage: java JavaTail fileName [updateTime (Seconds. default to 1 second)]");
        }
      }
    }

Here's a short story which you could use as a pointer: 这是一个可以用作指针的简短故事:

I've coded TailingInputStream at work for the very same reason. 我出于同样的原因编写了TailingInputStream。 It basically uses File and refreshed its contents on demand and checked against internal buffer if it has changed significantly (4kB memory stamp IIRC) and then did what the tail -f does. 它基本上使用File并按需刷新其内容,如果内部缓冲区发生了显着变化(4kB内存标记IIRC),则检查内部缓冲区,然后执行tail -f的操作。 A bit hacky, yes, but it works perfectly and doesn't mess with Threads or anything fancy like that - it's compatible all the way back to 1.4.2 at least. 有点hacky,是的,但是它完美无缺,并且不会弄乱线程或类似的任何东西 - 它至少可以兼容1.4.2。

That said, it was a lot easier to do than ReverseInputStream which went from file's end to start and didn't die if the file was updated on the fly... 也就是说,它比ReverseInputStream要容易得多,后者从文件的结尾开始,并且如果文件在运行中更新则不会死...

I found this nice tail implementation. 我发现这个很好的尾部实现。

Author : amelandri 作者:amelandri

Souce from : https://gist.github.com/amelandri/1376896 Souce来自: https ://gist.github.com/amelandri/1376896

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

/**
 * Java implementation of the Unix tail command
 * 
 * @param args[0] File name
 * @param args[1] Update time (seconds). Optional. Default value is 1 second
 * 
 * @author Luigi Viggiano (original author) http://it.newinstance.it/2005/11/19/listening-changes-on-a-text-file-unix-tail-implementation-with-java/
 * @author Alessandro Melandri (modified by)
 * */
public class Tail {

  static long sleepTime = 1000;

  public static void main(String[] args) throws IOException {

    if (args.length > 0){

      if (args.length > 1)
        sleepTime = Long.parseLong(args[1]) * 1000;

      BufferedReader input = new BufferedReader(new FileReader(args[0]));
      String currentLine = null;

      while (true) {

        if ((currentLine = input.readLine()) != null) {
          System.out.println(currentLine);
          continue;
        }

        try {
          Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
          break;
        }

      }
      input.close();

    } else {
      System.out.println("Missing parameter!\nUsage: java JavaTail fileName [updateTime (Seconds. default to 1 second)]");
        }
      }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM