Pergunta

Eu estou querendo saber quais as técnicas e / ou biblioteca para usar para implementar a funcionalidade do comando linux "tail -f". Estou essencialmente à procura de uma queda no add-on / substituição para java.io.FileReader. código de cliente poderia ser algo como isto:

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
}

A peça que falta é uma implementação razoável de TailFileReader. Ele deve ser capaz de ler partes do arquivo que existem antes que o arquivo é aberto, bem como as linhas que são adicionados.

Foi útil?

Solução

A capacidade de continuar a ler um arquivo, e esperar até que o arquivo tem mais algumas atualizações para você não deve ser tão difícil de realizar em código você mesmo. Aqui estão algumas pseudo-código:

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

Eu diria que você iria querer colocar esse tipo de funcionalidade em seu próprio segmento, para que você possa dormir e não afetará quaisquer outras áreas de sua aplicação. Você gostaria de expor keepReading em um setter de modo que sua classe principal / outras partes do aplicativo pode fechar com segurança a linha para baixo, sem quaisquer outras dores de cabeça, simplesmente chamando stopReading() ou algo similar.

Outras dicas

Dê uma olhada na implementação do Apache Commons de Tailer classe. Parece para lidar com rotação de log também.

Verifique JLogTailer , que faz essa lógica.

O ponto principal no código é:

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

Eu construí uma pequena implementação de "tail -f" em Scala há algum tempo: tailf . Ela cuida de rotação arquivo tão bem e você pode definir a sua própria lógica o que fazer quando atinge EOF ou encontrar o arquivo foi renomeado.

Você pode dar uma olhada e porta-lo para Java, uma vez que na verdade não há nada de complexo lá. Algumas notas: o arquivo principal é Tail.scala e, basicamente, define FollowingInputStream que cuida de EOF / renomeação e follow método, que envolve FollowingInputStream em uma enumeração ilimitada em SequenceInputStream. Então, assim como fins FollowingInputStream, SequenceInputStream solicita próximo elemento de uma Enumeration e outro FollowingInputStream é criado.

Eu tropecei recentemente sobre rxjava-file , é uma extensão do RxJava . Em contraste com as outras soluções, isso faz uso de NIO do Java.

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

Se o seu código só vai ter que correr em sistemas Unix, você pode ser capaz de fugir com apenas descascar para fora e chamando tail -f diretamente.

Como uma alternativa mais envolvido, você poderia dar uma olhada na implementação de cauda GNU e porta que mais de Java. (Eu não tenho certeza se isso não seria já fazer o seu código de uma obra derivada, no entanto.)

Aqui está uma pequena história que você poderia usar como um ponteiro:

Eu codificado TailingInputStream no trabalho para a mesma razão. Ele basicamente usa arquivos e atualizado o seu conteúdo sob demanda e verificados contra buffer interno se mudou significativamente (4KB selo memória IIRC) e, em seguida, fez o que o tail -f faz. A hacky pouco, sim, mas ele funciona perfeitamente e não faz confusão com linhas ou fantasia qualquer coisa assim. - É compatível com todo o caminho de volta para 1.4.2, pelo menos

Dito isto, era muito mais fácil de fazer do que ReverseInputStream que passou de final de arquivo para iniciar e não morreu, se o arquivo foi atualizado na mosca ...

Apenas foi confrontado com o mesmo problema - encontrado a implementação "mais simples" aqui: Java cauda.

* Great stuff * - pronto para a produção;)

Eu espero que o código-citação não vai cair alguma licença.

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

Eu encontrei esta aplicação agradável cauda.

Autor: amelandri

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

}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top