Programing

유닉스 / 리눅스“tail -f”의 Java IO 구현

lottogame 2020. 11. 5. 07:39
반응형

유닉스 / 리눅스“tail -f”의 Java IO 구현


Linux 명령 "tail -f"의 기능을 구현하는 데 사용할 기술 및 / 또는 라이브러리가 무엇인지 궁금합니다. 본질적으로 .NET에 대한 추가 기능 / 대체 기능을 찾고 있습니다 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. 파일을 열기 전에 존재하는 파일의 일부와 추가 된 행을 읽을 수 있어야합니다.


계속해서 파일을 읽고 파일에 더 많은 업데이트가있을 때까지 기다릴 수있는 기능은 코드에서 직접 수행하는 것이 그렇게 어렵지 않습니다. 다음은 의사 코드입니다.

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

이러한 유형의 기능을 자체 스레드에 배치하여 잠자기 상태로 유지하고 응용 프로그램의 다른 영역에 영향을 미치지 않도록해야한다고 가정합니다. keepReadingsetter에서 노출 하여 메인 클래스 / 애플리케이션의 다른 부분이 단순히 호출 stopReading()또는 유사한 것을 통해 다른 골칫거리없이 스레드를 안전하게 종료 할 수 있습니다 .


Tailer 클래스 의 Apache Commons 구현을 살펴보십시오 . 로그 회전도 처리하는 것 같습니다.


이 로직을 수행하는 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();
}

얼마 전에 Scala에서 "tail -f"의 짧은 구현을 만들었 습니다 : tailf . 파일 회전도 처리하며 EOF에 도달하거나 파일 이름이 변경된 것을 발견 할 때 수행 할 작업을 사용자 고유의 논리를 정의 할 수 있습니다.

실제로 거기에는 복잡한 것이 없기 때문에 살펴보고 Java로 이식 할 수 있습니다. 몇 가지 참고 사항 : 기본 파일은 Tail.scala 이며 기본적으로 FollowingInputStreamEOF / 이름 바꾸기 및 follow메서드를 처리하는 것이 무엇인지 정의 FollowingInputStream하며 SequenceInputStream. 따라서 FollowingInputStream종료 하자마자 SequenceInputStreaman Enumeration및 다른 요소에서 다음 요소를 요청합니다 FollowingInputStream.


나는 최근에 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);
}

코드가 유닉스 시스템에서만 실행되어야한다면 그냥 셸링하고 tail -f직접 호출하는 것만으로도 벗어날 수 있습니다 .

보다 복잡한 대안으로 GNU tail의 구현을 살펴보고이를 Java로 포팅 할 수 있습니다. (하지만 이것이 이미 코드를 파생 작업으로 만들지 않았는지 확실하지 않습니다.)


다음은 포인터로 사용할 수있는 짧은 이야기입니다.

같은 이유로 직장에서 TailingInputStream을 코딩했습니다. 기본적으로 File을 사용하고 필요에 따라 내용을 새로 고치고 크게 변경된 경우 (4kB 메모리 스탬프 IIRC) 내부 버퍼에 대해 확인한 다음 tail -f가 수행하는 작업을 수행했습니다. 약간 해키, 예,하지만 완벽하게 작동하고 Threads 또는 이와 같은 멋진 것을 엉망으로 만들지 않습니다. 적어도 1.4.2까지 다시 호환됩니다.

즉, 파일의 끝에서 시작하여 파일이 즉시 업데이트되면 죽지 않는 ReverseInputStream보다 훨씬 쉬웠습니다.


동일한 문제에 직면했습니다 . 여기서 "가장 간단한"구현을 찾았습니다. 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)]");
        }
      }
    }

I found this nice tail implementation.

Author : amelandri

Souce from : 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)]");
        }
      }

}

참고URL : https://stackoverflow.com/questions/557844/java-io-implementation-of-unix-linux-tail-f

반응형