Ich frage mich, mit welchen Techniken und/oder Bibliotheken die Funktionalität des Linux-Befehls "tail -f" implementiert werden soll. Ich suche im Wesentlichen nach einem Tropfen Add-On/Ersatz für Java.io.FileReader
. Der Client-Code könnte ungefähr so aussehen:
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
}
Das fehlende Stück ist eine sinnvolle Implementierung von TailFileReader
. Es sollte in der Lage sein, vor dem Öffnen der Datei vorhandene Teile der Datei sowie die hinzugefügten Zeilen zu lesen.
Die Möglichkeit, eine Datei weiter zu lesen und zu warten, bis in der Datei weitere Updates für Sie vorhanden sind, sollte im Code selbst nicht so schwer sein. Hier ist ein 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
}
}
Ich würde davon ausgehen, dass Sie diese Art von Funktionalität in einen eigenen Thread einbinden möchten, damit Sie sie schlafen können und keine anderen Bereiche Ihrer Anwendung betreffen. Sie möchten keepReading
in einem Setter freigeben, damit Ihre Hauptklasse/andere Teile der Anwendung den Thread ohne weitere Kopfschmerzen sicher beenden können, indem Sie einfach stopReading()
oder etwas Ähnliches aufrufen.
Schauen Sie sich die Apache Commons-Implementierung von Tailer class an. Es scheint auch die Protokollrotation zu bewältigen.
Überprüfen Sie JLogTailer , wodurch diese Logik ausgeführt wird.
Der Hauptpunkt im Code ist:
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();
}
Ich habe vor einiger Zeit eine kurze Implementierung von "tail -f" in Scala erstellt: tailf . Es kümmert sich auch um die Dateirotation, und Sie können Ihre eigene Logik definieren, was zu tun ist, wenn EOF erreicht wird oder die Datei umbenannt wurde.
Sie können einen Blick darauf werfen und es nach Java portieren, da dort eigentlich nichts komplexes ist. Einige Anmerkungen: Die Hauptdatei ist Tail.scala und im Grunde definiert sie FollowingInputStream
, die sich um EOF/Rename und follow
Methode kümmert, wodurch FollowingInputStream
in SequenceInputStream
in eine unbegrenzte Aufzählung eingeschlossen wird. Sobald also FollowingInputStream
beendet ist, fordert SequenceInputStream
das nächste Element von einer Enumeration
an, und eine andere FollowingInputStream
wird erstellt.
Ich bin vor kurzem über rxjava-file gestolpert. Es ist eine Erweiterung von RxJava . Im Gegensatz zu den anderen Lösungen wird Java von NIO verwendet.
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);
}
Ich habe diese Nice-Tail-Implementierung gefunden.
Urheber: amelandri
Souce von: 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)]");
}
}
}
Wenn Ihr Code nur auf Unix-Systemen ausgeführt werden muss, können Sie u. U. direkt mit tail -f
telefonieren.
Als etwas aufwendigere Alternative können Sie einen Blick auf die Implementierung von GNU End und Port werfen, die zu Java führen. (Ich bin nicht sicher, ob dies Ihren Code nicht bereits zu einer abgeleiteten Arbeit machen würde.)
Hier ist eine kurze Geschichte, die Sie als Zeiger verwenden können:
Ich habe TailingInputStream aus dem gleichen Grund bei der Arbeit codiert. Es verwendet grundsätzlich File und aktualisiert seinen Inhalt bei Bedarf und prüft es mit dem internen Puffer, wenn er sich signifikant geändert hat (4-KB-Speicherstempel IIRC) und dann das getan hat, was der tail -f macht. Ein bisschen hackig, ja, aber es funktioniert perfekt und verwirrt sich nicht mit Threads oder irgendetwas anderem - so ist es zumindest bis 1.4.2 kompatibel.
Das war jedoch viel einfacher als ReverseInputStream, das vom Ende der Datei bis zum Start ging und nicht starb, wenn die Datei im laufenden Betrieb aktualisiert wurde ...
Gerade stand das gleiche Problem - die "einfachste" Implementierung hier: Java Tail .
* Tolles Zeug * - fertig für die Produktion;)
Ich hoffe, dass die Code-Zitierung keine Lizenz fallen lässt.
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)]");
}
}
}