Lire logcat par programme dans l'application


116

Je souhaite lire et réagir aux journaux logcat dans mon application.

J'ai trouvé le code suivant:

try {
  Process process = Runtime.getRuntime().exec("logcat -d");
  BufferedReader bufferedReader = new BufferedReader(
  new InputStreamReader(process.getInputStream()));

  StringBuilder log=new StringBuilder();
  String line = "";
  while ((line = bufferedReader.readLine()) != null) {
    log.append(line);
  }
  TextView tv = (TextView)findViewById(R.id.textView1);
  tv.setText(log.toString());
  } 
catch (IOException e) {}

Ce code renvoie en effet les logs logcat qui ont été réalisés jusqu'au démarrage de l'application -

Mais est-il possible d'écouter en continu, même les nouveaux journaux logcat?


1
L'option -d videra le journal et se fermera. Supprimez simplement l'option -d et logcat ne se fermera pas.
Frohnzie

1
Comment puis-je l'effacer? - J'ai besoin de trouver une ligne spécifique concernant ma candidature
David

1
Aucune racine requise.
Luis

2
Si root n'est pas requis, seule la version de développement peut accéder à son propre journal? Et où exactement ce code est-il exécuté? avec dans l'application apk?
Ayyappa

2
J'ai essayé un test. Je n'ai pu lire que les événements de mon propre processus. Je pense que vous avez besoin de root pour lire les événements d'autres processus.
Yetti99

Réponses:


55

Vous pouvez continuer à lire les journaux, simplement en supprimant l'indicateur "-d" dans votre code ci-dessus.

L'indicateur "-d" demande à logcat d'afficher le contenu du journal et de quitter. Si vous supprimez l'indicateur, logcat ne se terminera pas et continuera d'envoyer toute nouvelle ligne qui lui est ajoutée.

N'oubliez pas que cela peut bloquer votre application si elle n'est pas correctement conçue.

bonne chance.


Si je supprime le "-d", l'application est bloquée et j'obtiens la boîte de dialogue de fermeture forcée.
David

21
Comme je l'ai dit ci-dessus, cela nécessiterait une conception d'application soignée. Vous devez exécuter le code ci-dessus dans un thread distinct (pour éviter de bloquer l'interface utilisateur), et comme vous souhaitez mettre à jour la vue de texte dans l'interface utilisateur avec les informations du journal, vous devez utiliser un gestionnaire pour publier les informations dans l'interface utilisateur.
Luis

2
Salut Luis, pouvez-vous poster un exemple de code d'un fil séparé?
img.simone

Voir la réponse stackoverflow.com/a/59511458/1185087 il utilise des coroutines qui ne bloquent pas le thread d'interface utilisateur.
user1185087

8

Vous pouvez effacer votre logcat avec cette méthode que j'utilise pour effacer après avoir écrit logcat dans un fichier pour éviter les lignes dupliquées:

public void clearLog(){
     try {
         Process process = new ProcessBuilder()
         .command("logcat", "-c")
         .redirectErrorStream(true)
         .start();
    } catch (IOException e) {
    }
}

Je viens de découvrir que l'effacement du journal peut prendre un certain temps. Ainsi, si vous effacez le journal, puis lisez le journal immédiatement, certaines anciennes entrées peuvent ne pas encore être effacées. En outre, certaines entrées de journal nouvellement ajoutées peuvent être supprimées car elles sont ajoutées avant la fin de l'effacement. Cela ne fait aucune différence même si vous appelez process.waitfor ().
Tom Rutchik

6

Avec les coroutines et les bibliothèques officielles lifecycle-livingata-ktx et lifecycle-viewmodel-ktx , c'est simple comme ça:

class LogCatViewModel : ViewModel() {
    fun logCatOutput() = liveData(viewModelScope.coroutineContext + Dispatchers.IO) {
        Runtime.getRuntime().exec("logcat -c")
        Runtime.getRuntime().exec("logcat")
                .inputStream
                .bufferedReader()
                .useLines { lines -> lines.forEach { line -> emit(line) }
        }
    }
}

Usage

val logCatViewModel by viewModels<LogCatViewModel>()

logCatViewModel.logCatOutput().observe(this, Observer{ logMessage ->
    logMessageTextView.append("$logMessage\n")
})

Excellente suggestion. Je me demande s'il existe un moyen de faire fonctionner cela avec WebViewau lieu de TextView.
Fatih

4

Voici un montage / dépôt rapide qui peut être utilisé pour capturer tous les éléments de journal actuels ou tous les nouveaux (depuis une dernière demande).

Vous devez modifier / étendre cela, car vous souhaiterez peut-être renvoyer un flux continu plutôt qu'un LogCapture.

Le "manuel" Android LogCat: https://developer.android.com/studio/command-line/logcat.html

import android.util.Log;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Stack;

/**
* Created by triston on 6/30/17.
*/

public class Logger {

  // http://www.java2s.com/Tutorial/Java/0040__Data-Type/SimpleDateFormat.htm
  private static final String ANDROID_LOG_TIME_FORMAT = "MM-dd kk:mm:ss.SSS";
  private static SimpleDateFormat logCatDate = new SimpleDateFormat(ANDROID_LOG_TIME_FORMAT);

  public static String lineEnding = "\n";
  private final String logKey;

  private static List<String> logKeys = new ArrayList<String>();

  Logger(String tag) {
    logKey = tag;
    if (! logKeys.contains(tag)) logKeys.add(logKey);
  }

  public static class LogCapture {
    private String lastLogTime = null;
    public final String buffer;
    public final List<String> log, keys;
    LogCapture(String oLogBuffer, List<String>oLogKeys) {
      this.buffer = oLogBuffer;
      this.keys = oLogKeys;
      this.log = new ArrayList<>();
    }
    private void close() {
      if (isEmpty()) return;
      String[] out = log.get(log.size() - 1).split(" ");
      lastLogTime = (out[0]+" "+out[1]);
    }
    private boolean isEmpty() {
      return log.size() == 0;
    }
    public LogCapture getNextCapture() {
      LogCapture capture = getLogCat(buffer, lastLogTime, keys);
      if (capture == null || capture.isEmpty()) return null;
      return capture;
    }
    public String toString() {
      StringBuilder output = new StringBuilder();
      for (String data : log) {
        output.append(data+lineEnding);
      }
      return output.toString();
    }
  }

  /**
   * Get a list of the known log keys
   * @return copy only
   */
  public static List<String> getLogKeys() {
    return logKeys.subList(0, logKeys.size() - 1);
  }

  /**
   * Platform: Android
   * Get the logcat output in time format from a buffer for this set of static logKeys.
   * @param oLogBuffer logcat buffer ring
   * @return A log capture which can be used to make further captures.
   */
  public static LogCapture getLogCat(String oLogBuffer) { return getLogCat(oLogBuffer, null, getLogKeys()); }

  /**
   * Platform: Android
   * Get the logcat output in time format from a buffer for a set of log-keys; since a specified time.
   * @param oLogBuffer logcat buffer ring
   * @param oLogTime time at which to start capturing log data, or null for all data
   * @param oLogKeys logcat tags to capture
   * @return A log capture; which can be used to make further captures.
   */
  public static LogCapture getLogCat(String oLogBuffer, String oLogTime, List<String> oLogKeys) {
    try {

      List<String>sCommand = new ArrayList<String>();
      sCommand.add("logcat");
      sCommand.add("-bmain");
      sCommand.add("-vtime");
      sCommand.add("-s");
      sCommand.add("-d");

      sCommand.add("-T"+oLogTime);

      for (String item : oLogKeys) sCommand.add(item+":V"); // log level: ALL
      sCommand.add("*:S"); // ignore logs which are not selected

      Process process = new ProcessBuilder().command(sCommand).start();

      BufferedReader bufferedReader = new BufferedReader(
        new InputStreamReader(process.getInputStream()));

      LogCapture mLogCapture = new LogCapture(oLogBuffer, oLogKeys);
      String line = "";

      long lLogTime = logCatDate.parse(oLogTime).getTime();
      if (lLogTime > 0) {
        // Synchronize with "NO YEAR CLOCK" @ unix epoch-year: 1970
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date(oLogTime));
        calendar.set(Calendar.YEAR, 1970);
        Date calDate = calendar.getTime();
        lLogTime = calDate.getTime();
      }

      while ((line = bufferedReader.readLine()) != null) {
        long when = logCatDate.parse(line).getTime();
        if (when > lLogTime) {
          mLogCapture.log.add(line);
          break; // stop checking for date matching
        }
      }

      // continue collecting
      while ((line = bufferedReader.readLine()) != null) mLogCapture.log.add(line);

      mLogCapture.close();
      return mLogCapture;
    } catch (Exception e) {
      // since this is a log reader, there is nowhere to go and nothing useful to do
      return null;
    }
  }

  /**
   * "Error"
   * @param e
   */
  public void failure(Exception e) {
    Log.e(logKey, Log.getStackTraceString(e));
  }

  /**
   * "Error"
   * @param message
   * @param e
   */
  public void failure(String message, Exception e) {
    Log.e(logKey, message, e);
  }

  public void warning(String message) {
    Log.w(logKey, message);
  }

  public void warning(String message, Exception e) {
    Log.w(logKey, message, e);
  }

  /**
   * "Information"
   * @param message
   */
  public void message(String message) {
    Log.i(logKey, message);
  }

  /**
   * "Debug"
   * @param message a Message
   */
  public void examination(String message) {
    Log.d(logKey, message);
  }

  /**
   * "Debug"
   * @param message a Message
   * @param e An failure
   */
  public void examination(String message, Exception e) {
    Log.d(logKey, message, e);
  }

}

Dans votre projet qui effectue la journalisation des activités:

Logger log = new Logger("SuperLog");
// perform logging methods

Lorsque vous souhaitez capturer tout ce que vous avez connecté via "Logger"

LogCapture capture = Logger.getLogCat("main");

Lorsque vous avez faim et que vous voulez grignoter plus de bûches

LogCapture nextCapture = capture.getNextCapture();

Vous pouvez obtenir la capture sous forme de chaîne avec

String captureString = capture.toString();

Ou vous pouvez obtenir les éléments du journal de la capture avec

String logItem = capture.log.get(itemNumber);

Il n'y a pas de méthode statique exacte pour capturer les clés de journal étrangères, mais il existe néanmoins un moyen

LogCapture foreignCapture = Logger.getLogCat("main", null, foreignCaptureKeyList);

L'utilisation de ce qui précède vous permettra également de faire appel Logger.this.nextCaptureà la capture étrangère.


C'est généralement la meilleure méthode pour effectuer la journalisation et l'analyse en raison de la stratégie de faible surcharge [en traitement]. Il peut y avoir ou non un bogue dans ce code. Le traitement logcat de la sélection du temps doit être supérieur au temps donné pour une correspondance correcte. L'absence d'un algorithme de sélection de temps correct créera une entrée de journal en double au premier élément de nextCapture.
Hypersoft Systems

Le manque de documentation sur les formats d'heure et les paramètres régionaux associés à l'option de sélection d'heure d'Android-logcat peut avoir créé des bogues qui nécessiteront des modifications d'interpolation de format d'heure.
Hypersoft Systems

Salut .. J'ai utilisé votre code mais le résultat est toujours vide. Est toujours valide?
img.simone

@ img.simone; J'ai mis à jour cela avec mes révisions de code. Le logcat d'Android est cassé de plusieurs manières. Le premier, c'est que la sortie de format de date de logcat n'a pas de composant d'année, ce qui fait un repli de comparaison de date vers l'année 1970; deuxièmement, les options -t et -T avec une date ne commencent pas réellement à cracher des journaux à la date spécifiée, nous devons donc analyser la date et la comparer avec une date numérique synchronisée à l'année 1970. Je ne peux pas tester cela mise à jour pour vous, mais cela devrait certainement fonctionner; car le code provient d'un référentiel de travail avec des modifications spécifiques à ce contexte.
Hypersoft Systems

1
Vous pouvez trouver le code de travail ci- dessous git.hsusa.core.log.controller.AndroidLogController.java; Vous voudrez peut-être utiliser ma bibliothèque hscore au lieu de cette solution "rapide et sale". Pour faire la journalisation avec hscore, vous utiliseriez: public final static SmartLogContext log = SmartLog.getContextFor("MyLogContext");pour commencer. Cela fonctionne à peu près de la même manière avec une meilleure API. Vous pouvez utiliser mon traqueur de problèmes git hub si vous avez besoin de TOUTE aide pour cela.
Hypersoft Systems

3

L'indicateur "-c" efface le tampon.

-c Efface (vide) tout le journal et quitte.


comment utiliser -c dans le code ci-dessus.si vous avez trouvé quelque chose dans le journal, je veux l'effacer
yuva ツ

yuva, il suffit de faire: process = Runtime.getRuntime (). exec ("logcat -c"); bufferedReader = new BufferedReader (nouveau InputStreamReader (process.getInputStream ()), 1024); ligne = bufferedReader.readLine ();
djdance

1
            //CLEAR LOGS
            Runtime.getRuntime().exec("logcat -c");
            //LISTEN TO NEW LOGS
            Process pq=Runtime.getRuntime().exec("logcat v main");
            BufferedReader brq = new BufferedReader(new InputStreamReader(pq.getInputStream()));
            String sq="";
            while ((sq = brq.readLine()) != null)
            {
              //CHECK YOUR MSG HERE 
              if(sq.contains("send MMS with param"))
              {
              }
            }

J'utilise ceci dans mon application et cela fonctionne. Et vous pouvez utiliser le code ci-dessus dans Timer Task afin qu'il n'arrête pas votre thread principal

        Timer t;
        this.t.schedule(new TimerTask()
        {
          public void run()
          {
            try
            {
                ReadMessageResponse.this.startRecord();//ABOVE METHOD HERE

            }
            catch (IOException ex)
            {
              //NEED TO CHECK SOME VARIABLE TO STOP MONITORING LOGS 
              System.err.println("Record Stopped");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            finally
            {
                ReadMessageResponse.this.t.cancel();
            }
          }
        }, 0L);
      }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.