2009-02-17 17 views
61

Je me demande quelles techniques et/ou bibliothèque utiliser pour implémenter la fonctionnalité de la commande linux "tail -f". Je cherche essentiellement une baisse de add-on/remplacement pour java.io.FileReader. Le code client pourrait ressembler à ceci:Java IO implémentation d'unix/linux "tail -f"

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 mise en œuvre raisonnable TailFileReader. Il devrait être capable de lire les parties du fichier qui existent avant l'ouverture du fichier ainsi que les lignes ajoutées.

Répondre

28

La possibilité de continuer à lire un fichier et d'attendre que le fichier ait d'autres mises à jour ne soit pas si difficile à exécuter dans le code. Voici quelques 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 voulez mettre ce type de fonctionnalité dans son propre fil, de sorte que vous pouvez dormir et ne pas affecter d'autres domaines de votre application. Vous voudrez exposer keepReading dans un setter afin que votre classe principale/d'autres parties de l'application peuvent fermer le fil en toute sécurité sans aucun autre mal de tête, simplement en appelant stopReading() ou quelque chose de similaire.

+4

Note: Si vous voulez la queue, utilisez br.skip (file.length()); J'ai expérimenté avec RandomAccessReader() mais c'est * très * lent. –

+8

Ceci ne prend pas en compte les troncatures de fichiers; ce code échoue si le fichier journal est écrasé ... ce qui est une caractéristique essentielle de tail! –

+0

Cela ne prend pas en charge le remplacement des fichiers journaux. – sheki

0

Voici une histoire courte que vous pouvez utiliser comme pointeur:

J'ai codé TailingInputStream au travail pour la même raison. Il utilise essentiellement File et rafraîchit son contenu à la demande et vérifie le tampon interne s'il a changé de manière significative (4 Ko de mémoire tampon IIRC) puis fait ce que le tail -f fait. Un peu hacky, oui, mais ça marche parfaitement et ne plaisante pas avec Threads ou quoi que ce soit de fantaisiste - c'est compatible tout au moins jusqu'à 1.4.2.

Cela dit, il était beaucoup plus facile à faire que ReverseInputStream qui est passé de la fin du fichier pour démarrer et ne pas mourir si le fichier a été mis à jour à la volée ...

11

Vérifiez JLogTailer, qui fait 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(); 
} 
+0

JLogTailer ne semble pas avoir de bibliothèque. – sheki

+0

@sheki suffit d'utiliser le pot? @aldrinleal Je ne voulais pas créer une nouvelle réponse ... je viens de coller le code ici. J'aime la version plus simple (+ rapide?) De matt plus :) – Karussell

0

Si votre code ne jamais devra fonctionner sur les systèmes Unix, vous pourriez être en mesure de sortir avec juste bombardements et appeler tail -f directement. En tant qu'alternative plus impliquée, vous pouvez jeter un oeil à l'implémentation de GNU tail et port sur Java. (Je ne suis pas sûr que cela ne fasse pas déjà de votre code un travail dérivé, cependant.)

+1

Je ne suis pas familier avec la façon dont Java gère les commandes shell; étant donné que 'tail -f' ne se ferme jamais, cela va-t-il bloquer l'application Java? –

+0

Non Il ne fera pas Java à accrocher .. J'ai écrit l'application similaire et sortira bientôt sur sourceforge – Makky

+0

Si vous avez la poignée de connexion, vous pouvez fermer la session et la connexion .... – Makky

8

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 à faire quand il atteint EOF ou trouve que le fichier a été renommé.

Vous pouvez jeter un coup d'oeil et le porter sur Java, car il n'y a rien de complexe là-dedans. Quelques notes: le fichier principal est Tail.scala et en gros il définit FollowingInputStream qui prend en charge EOF/rename et la méthode follow, qui enveloppe FollowingInputStream en une énumération non bornée en SequenceInputStream. Ainsi, dès que FollowingInputStream se termine, SequenceInputStream demande l'élément suivant à partir d'un Enumeration et un autre FollowingInputStream est créé.

+0

ce facile et fonctionne super merci Alex. – CruncherBigData

53

Jetez un oeil à la mise en œuvre Apache Commons de la classe Tailer.Il semble également gérer la rotation des journaux.

+0

Merci beaucoup! BTW: Si logrotation est faite correctement ('cp logfile oldfile;> logfile') alors la solution de matt devrait fonctionner car la référence du fichier n'est pas perdue! – Karussell

+0

Soyez averti: si vous voulez seulement avoir la queue à la fin du fichier, alors Tailer a quelques problèmes même dans la version 2.4 (le plus récent de cette écriture). Voir: https://issues.apache.org/jira/browse/IO-279?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel –

0

Juste était confronté au même problème - trouvé l'implémentation "la plus simple" ici: Java Tail.

* Great stuff * - prêt pour la production;)

J'espère que le code citation ne baissera 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)]"); 
     } 
     } 
    } 
+1

pas de problème :). merci pour la citation –

+0

ce code peut-il gérer la rotation des fichiers? – CruncherBigData

2

récemment je suis tombé sur rxjava-file, il est une extension de RxJava. Contrairement aux autres solutions, cela utilise 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); 
} 
+0

Pour moi, les appels d'abonnement semblent juste bloquer indéfiniment, ne jamais revenir? – PlexQ

+0

@PlexQ avez-vous simplement copier et coller? Souhaitez-vous votre code? – cheffe

0

J'ai trouvé cette belle implémentation de la queue.

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

}