implementazione Java IO di Unix / Linux “tail -f”
Domanda
Mi chiedo quali tecniche e / o libreria da utilizzare per implementare la funzionalità del comando linux "tail -f". Sto essenzialmente alla ricerca di un calo di add-on / sostituzione per java.io.FileReader
. Il codice client potrebbe essere simile a questa:
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
}
Il pezzo mancante è una ragionevole implementazione di TailFileReader
. Dovrebbe essere in grado di leggere parti del file che esistono prima che il file viene aperto, così come le linee che vengono aggiunti.
Soluzione
La capacità di continuare a leggere un file, e aspettare fino a quando il file ha alcuni ulteriori aggiornamenti per non dovrebbe essere così difficile da realizzare in codice da soli. Ecco alcuni pseudo-codice:
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
}
}
Parto dal presupposto che si vorrebbe mettere questo tipo di funzionalità nel proprio thread, in modo che si può dormire e non interesserà tutte le altre zone della vostra applicazione. Si consiglia di esporre keepReading
in un setter in modo che i principali classi / altre parti dell'applicazione può chiudere in modo sicuro il filo verso il basso, senza altri mal di testa, semplicemente chiamando stopReading()
o qualcosa di simile.
Altri suggerimenti
Date un'occhiata a implementazione di Apache Commons Tailer classe. Sembra di gestire la rotazione dei log pure.
JLogTailer , che fa questa logica.
Il punto principale del codice è:
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();
}
Ho costruito una breve implementazione di "tail -f" a Scala di qualche tempo fa: tailf . Si prende cura di rotazione di file come bene e si può definire la propria logica che cosa fare quando raggiunge EOF o trova il file è stato rinominato.
Si può dare un'occhiata e la porta a Java, dal momento che in realtà non c'è niente di complesso in là. Alcune note: il file principale è Tail.scala e fondamentalmente definisce FollowingInputStream
che si prende cura di EOF / rinominare e follow
metodo, che avvolge FollowingInputStream
in un'enumerazione piena nella SequenceInputStream
. Così, non appena finisce FollowingInputStream
, SequenceInputStream
richiede elemento successivo da un Enumeration
e un altro FollowingInputStream
viene creato.
mi sono imbattuto di recente su rxjava file , Si tratta di un estensione della RxJava . In contrasto con le altre soluzioni di questo si avvale di NIO di 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 il codice sempre e solo dovrà essere eseguito su sistemi Unix, si può essere in grado di cavarsela con un solo sborsare e chiamando direttamente tail -f
.
Come più coinvolto alternativa, si può dare un'occhiata alla realizzazione di GNU coda e la porta che oltre a Java. (Io non sono sicuro se questo non sarebbe già rendere il codice un lavoro derivato, però.)
Ecco una breve storia che si potrebbe usare come puntatore:
Ho codificato TailingInputStream al lavoro per la stessa ragione. Esso utilizza fondamentalmente file e aggiornato il suo contenuto su richiesta e confrontato buffer interno se è cambiato significativamente (4kB timbro memoria IIRC) e poi fatto quello -f coda fa. Un po 'hacky, sì, ma funziona perfettamente e non pasticciare con i filetti o nulla di fantasia del genere -. È compatibile con tutta la strada fino alla 1.4.2 almeno
Detto questo, è stato molto più facile da fare che ReverseInputStream che è passato da fine del file per avviare e non morire se il file è stato aggiornato al volo ...
Just ha dovuto affrontare la stessa questione - trovato qui la "semplice" applicazione: Java coda .
* Great stuff * - pronto per la produzione;)
spero che il codice di citazione non cadrà qualche licenza.
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)]");
}
}
}
Ho trovato questo bel implementazione di coda.
Autore: amelandri
Souce da: 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)]");
}
}
}