Lesen Sie logcat programmgesteuert in der Anwendung


116

Ich möchte Logcat-Protokolle in meiner Anwendung lesen und darauf reagieren.

Ich habe folgenden Code gefunden:

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) {}

Dieser Code gibt tatsächlich die Logcat-Protokolle zurück, die bis zum Start der Anwendung erstellt wurden.

Aber ist es möglich, auch neue Logcat-Protokolle kontinuierlich anzuhören?


1
Die Option -d gibt das Protokoll aus und beendet es. Entfernen Sie einfach die Option -d und logcat wird nicht beendet.
Frohnzie

1
Wie kann ich es löschen? - Ich muss eine bestimmte Zeile bezüglich meiner Bewerbung finden
David

1
Keine Wurzel erforderlich.
Luis

2
Wenn root nicht benötigt wird, kann nur der Entwicklungsbuild auf sein eigenes Protokoll zugreifen? Und wo genau wird dieser Code ausgeführt? mit in der apk anwendung?
Ayyappa

2
Ich habe einen Test versucht. Ich konnte nur die Ereignisse meines eigenen Prozesses lesen. Ich denke, Sie brauchen root, um andere Prozessereignisse zu lesen.
Yetti99

Antworten:


55

Sie können die Protokolle weiterlesen, indem Sie das Flag "-d" in Ihrem obigen Code entfernen.

Das Flag "-d" weist logcat an, den Protokollinhalt anzuzeigen und zu beenden. Wenn Sie das Flag entfernen, wird logcat nicht beendet und es werden weiterhin neue Zeilen gesendet.

Denken Sie daran, dass dies Ihre Anwendung blockieren kann, wenn sie nicht richtig entworfen wurde.

Viel Glück.


Wenn ich das "-d" entferne, bleibt die Anwendung hängen und ich erhalte den erzwungenen Dialog zum Schließen.
David

21
Wie ich oben sagte, würde es ein sorgfältiges Anwendungsdesign erfordern. Der obige Code muss in einem separaten Thread ausgeführt werden (um ein Blockieren der Benutzeroberfläche zu vermeiden). Wenn Sie die Textansicht in der Benutzeroberfläche mit Protokollinformationen aktualisieren möchten, müssen Sie einen Handler verwenden, um die Informationen an die Benutzeroberfläche zurückzusenden.
Luis

2
Hallo Luis, kannst du bitte einen Beispielcode für einen getrennten Thread posten?
img.simone

Siehe Antwort stackoverflow.com/a/59511458/1185087 Es werden Coroutinen verwendet, die den UI-Thread nicht blockieren.
user1185087

8

Sie können Ihren Logcat mit dieser Methode löschen, die ich verwende, um Logcat nach dem Schreiben in eine Datei zu löschen, um doppelte Zeilen zu vermeiden:

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

Ich habe gerade festgestellt, dass das Löschen des Protokolls einige Zeit dauern kann. Wenn Sie also das Protokoll löschen und dann sofort lesen, wurden einige alte Einträge möglicherweise noch nicht gelöscht. Darüber hinaus können einige neu hinzugefügte Protokolleinträge gelöscht werden, da sie hinzugefügt werden, bevor das Löschen abgeschlossen ist. Es macht keinen Unterschied, selbst wenn Sie process.waitfor () aufrufen.
Tom Rutchik

6

Mit Coroutinen und dem Beamten Bibliotheken Lifecycle-Livedata-Ktx und Lifecycle-Viewmodel-Ktx ist das ganz einfach:

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) }
        }
    }
}

Verwendung

val logCatViewModel by viewModels<LogCatViewModel>()

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

Toller Vorschlag. Ich frage mich, ob es einen Weg gibt, diese Arbeit mit WebViewstatt zu machen TextView.
Fatih

4

Hier ist ein schnelles Zusammenstellen / Ablegen, mit dem alle aktuellen oder alle neuen (seit einer letzten Anforderung) Protokollelemente erfasst werden können.

Sie sollten dies ändern / erweitern, da Sie möglicherweise einen kontinuierlichen Stream anstelle eines LogCapture zurückgeben möchten.

Das Android LogCat "Handbuch": 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);
  }

}

In Ihrem Projekt, das die Aktivitätsprotokollierung durchführt:

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

Wenn Sie alles erfassen möchten, was Sie über "Logger" angemeldet haben

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

Wenn Sie hungrig werden und mehr Holzscheite essen möchten

LogCapture nextCapture = capture.getNextCapture();

Sie können das Capture als String mit erhalten

String captureString = capture.toString();

Oder Sie können die Protokollelemente der Erfassung mit abrufen

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

Es gibt keine genaue statische Methode zum Erfassen von Fremdprotokollschlüsseln, aber es gibt trotzdem eine Möglichkeit

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

Wenn Sie die oben genannten Informationen verwenden, können Sie auch Logger.this.nextCapturedie Fremdaufnahme abrufen .


Dies ist im Allgemeinen die beste Methode zur Durchführung von Protokollierung und Analyse aufgrund der Strategie mit geringem Overhead [bei der Verarbeitung]. Möglicherweise liegt ein Fehler in diesem Code vor oder nicht. Die logcat-Behandlung der Zeitauswahl muss länger sein als die für eine korrekte Übereinstimmung angegebene Zeit. Wenn kein korrekter Zeitauswahlalgorithmus vorhanden ist, wird beim ersten Element von nextCapture ein doppelter Protokolleintrag erstellt.
Hypersoft Systems

Das Fehlen einer Dokumentation zu Zeitformaten und Gebietsschemas im Zusammenhang mit der Zeitauswahloption von android-logcat hat möglicherweise zu Fehlern geführt, die Änderungen der Zeitformat-Interpolation erfordern.
Hypersoft Systems

Hallo .. Ich habe Ihren Code verwendet, aber das Ergebnis ist immer leer. Ist noch gültig?
img.simone

@ img.simone; Ich habe dies mit meinen Code-Revisionen aktualisiert. Der Logcat von Android ist in vielerlei Hinsicht kaputt. Das erste ist, dass die Datumsformatausgabe von logcat keine Jahreskomponente enthält, wodurch ein Datumsvergleich auf das Jahr 1970 zurückgreift. Zweitens beginnt die Option -t und -T mit einem Datum nicht, Protokolle zum angegebenen Datum auszuspucken. Daher müssen wir das Datum analysieren und mit einem numerischen Datum vergleichen, das mit dem Jahr 1970 synchronisiert ist. Ich kann dies nicht testen Update für Sie, aber es sollte auf jeden Fall funktionieren; da der Code aus einem funktionierenden Repository mit Änderungen stammt, die für diesen Kontext spezifisch sind.
Hypersoft Systems

1
Den Arbeitscode finden Sie hier unter git.hsusa.core.log.controller.AndroidLogController.java; Möglicherweise möchten Sie meine hscore-Bibliothek anstelle dieser "schnellen und schmutzigen" Lösung verwenden. Um die Protokollierung mit hscore durchzuführen, verwenden Sie: public final static SmartLogContext log = SmartLog.getContextFor("MyLogContext");um loszulegen. Mit einer besseren API funktioniert es fast genauso. Sie können meinen Git Hub Issue Tracker verwenden, wenn Sie dabei Hilfe benötigen.
Hypersoft Systems

3

Das Flag "-c" löscht den Puffer.

-c Löscht (löscht) das gesamte Protokoll und beendet es.


Wie verwende ich -c im obigen Code? Wenn ich etwas im Protokoll gefunden habe, möchte ich es löschen
Yuva

yuva, mach einfach: process = Runtime.getRuntime (). exec ("logcat -c"); bufferedReader = neuer BufferedReader (neuer InputStreamReader (process.getInputStream ()), 1024); line = 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"))
              {
              }
            }

Ich benutze dies in meiner App und es funktioniert. Und Sie können den obigen Code in Timer Task verwenden, damit Ihr Haupt-Thread nicht gestoppt wird

        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);
      }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.