web-dev-qa-db-ja.com

Java IO unix / linux "tail -f"の実装

Linuxコマンド「tail -f」の機能を実装するためにどのテクニックやライブラリを使用するのか疑問に思っています。私は基本的にJava.io.FileReaderのアドオン/置換のドロップを探しています。クライアントコードは次のようになります。

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
}

欠けている部分は、TailFileReaderの合理的な実装です。追加される行だけでなく、ファイルが開かれる前に存在するファイルの一部を読み取ることができるはずです。

68
Gary

ファイルの読み取りを継続し、ファイルがさらに更新されるまで待機する機能は、コードを自分で達成するのが難しくないはずです。擬似コードは次のとおりです。

_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
    }
}
_

このタイプの機能を独自のスレッドに入れて、スリープさせ、アプリケーションの他の領域に影響を与えないようにすることを想定しています。 keepReadingをセッターで公開して、メインクラス/アプリケーションの他の部分が、単にstopReading()または類似のものを呼び出すだけで、他の頭痛の種なしでスレッドを安全にシャットダウンできるようにします。

34
matt b

Tailer クラスのApache Commons実装を見てください。ログのローテーションも処理するようです。

57
Chetan Sastry

JLogTailer を確認します。これにより、このロジックが実行されます。

コードの主なポイントは次のとおりです。

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

Scala少し前に "tail -f"の短い実装を作成しました: tailf 。ファイルの回転も処理します。 EOFに到達したとき、またはファイルの名前が変更されたことを検出したときの処理を行う独自のロジック。

実際にそこには複雑なものは何もないので、Javaに移植してみてください。注:メインファイルは Tail.scala で、基本的には FollowingInputStream を定義し、EOF/renameと follow メソッド。FollowingInputStreamSequenceInputStreamの無制限の列挙にラップします。したがって、FollowingInputStreamが終了するとすぐに、SequenceInputStreamEnumerationから次の要素を要求し、別のFollowingInputStreamが作成されます。

9

最近つまずいたrxjava-file、これは RxJava の拡張です。他のソリューションとは対照的に、これは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);
}
4
cheffe

コードをUnixシステムでのみ実行する必要がある場合は、tail -fを直接呼び出して呼び出すだけで済む可能性があります。

より複雑な代替手段として、GNU tailの実装を見て、それをJavaに移植することができます。ただし、動作します。)

1
Daniel Werner

このニーステールの実装を見つけました。

著者:アメランドリ

ソース: 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)]");
        }
      }

}
0
Mahesh K

これは、ポインターとして使用できる短いストーリーです。

同じ理由で職場でTailingInputStreamをコーディングしました。基本的にFileを使用し、オンデマンドでコンテンツを更新し、大幅に変更された場合(4kBメモリスタンプIIRC)に内部バッファに対してチェックし、tail -fが行うことを行いました。はい、少しハックしますが、完全に機能し、スレッドやそのような派手なものを混乱させません-少なくとも1.4.2までは互換性があります。

とは言っても、ファイルの終わりから開始まで行って、ファイルがその場で更新されても死ななかったReverseInputStreamよりもはるかに簡単でした...

0
Esko

ちょうど同じ問題に直面しました-ここで「最も簡単な」実装を見つけました: Java Tail

*素晴らしいもの*-生産準備完了;)

コード引用が何らかのライセンスを落とさないことを願っています。

    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)]");
        }
      }
    }
0
ViPup