Je me demande quelles techniques et/ou bibliothèque utiliser pour implémenter les fonctionnalités de la commande linux "tail -f". Je suis essentiellement à la recherche d’une solution de remplacement/remplacement pour Java.io.FileReader
. Le code client pourrait ressembler à ceci:
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
}
La pièce manquante est une implémentation raisonnable de TailFileReader
. Il devrait être capable de lire des parties du fichier qui existaient avant son ouverture, ainsi que les lignes ajoutées.
La possibilité de continuer à lire un fichier et d’attendre que le fichier contienne quelques mises à jour supplémentaires pour vous ne devrait pas être aussi difficile à accomplir dans le code vous-même. Voici un 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
}
}
Je suppose que vous voudriez mettre ce type de fonctionnalité dans son propre thread, afin que vous puissiez le mettre en veille et ne pas affecter d’autres zones de votre application. Vous souhaitez exposer keepReading
dans un séparateur afin que votre classe principale/d'autres parties de l'application puissent fermer le thread en toute sécurité, sans autre problème, simplement en appelant stopReading()
ou quelque chose de similaire.
Jetez un coup d’œil à l’application Apache Commons de Tailer class. Il semble également gérer la rotation des journaux.
Vérifiez JLogTailer , qui effectue cette logique.
Le point principal dans le code est:
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();
}
J'ai construit une courte implémentation de "tail -f" dans Scala il y a quelque temps: tailf . Il prend également en charge la rotation des fichiers et vous pouvez définir votre propre logique quant à ce qu'il faut faire lorsqu'il atteint EOF ou découvre que le fichier a été renommé.
Vous pouvez y jeter un coup d'œil et le porter en Java, car il n'y a rien de complexe là-dedans. Quelques notes: le fichier principal est Tail.scala et définit fondamentalement FollowingInputStream
qui prend en charge EOF/rename et la méthode follow
, qui englobe FollowingInputStream
dans une énumération sans limite dans SequenceInputStream
. Ainsi, dès que FollowingInputStream
se termine, SequenceInputStream
demande l'élément suivant d'un Enumeration
et un autre FollowingInputStream
est créé.
Je suis tombé récemment sur fichier-rxjava, c'est une extension de RxJava . Contrairement aux autres solutions, cette solution utilise le NIO de 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);
}
J'ai trouvé cette implémentation de Nice tail.
Auteur: amelandri
Source: 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)]");
}
}
}
Si votre code ne doit jamais fonctionner que sur des systèmes Unix, vous pourrez peut-être vous contenter d’appeler et d’appeler directement tail -f
.
En guise d'alternative plus complexe, vous pouvez jeter un coup d'œil sur l'implémentation de GNU tail et le transférer en Java. (Je ne sais pas si cela ne ferait pas déjà de votre code un travail dérivé, cependant.)
Voici une courte histoire que vous pourriez utiliser comme pointeur:
J'ai codé TailingInputStream au travail pour la même raison. Fondamentalement, il utilise Fichier et actualise son contenu à la demande. Il vérifie également si la mémoire tampon interne a été modifiée de manière significative (tampon de mémoire de 4 Ko, IIRC). Un peu hacky, oui, mais cela fonctionne parfaitement et ne plaisante pas avec Threads ou quelque chose d'extraordinaire comme ça - il est compatible jusqu'à la version 1.4.2 au moins.
Cela dit, c'était beaucoup plus facile à faire que ReverseInputStream, qui allait de bout en bout de fichier et qui ne mourait pas si le fichier était mis à jour à la volée ...
Juste a été confronté au même problème - trouvé la mise en œuvre "la plus simple" ici: Java Tail .
* Excellent matériel * - prêt pour la production;)
J'espère que le code-citation ne lâchera pas une licence.
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)]");
}
}
}