Read logcat programmatically within application

85,001

Solution 1

You can keep reading the logs, just by removing the "-d" flag in your code above.

The "-d" flag instruct to logcat to show log content and exit. If you remove the flag, logcat will not terminate and keeps sending any new line added to it.

Just have in mind that this may block your application if not correctly designed.

good luck.

Solution 2

With coroutines and the official lifecycle-livedata-ktx and lifecycle-viewmodel-ktx libraries it's simple like that:

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

Solution 3

You can clear your logcat with this method i'm using to clear after writing logcat to a file to avoid duplicated lines:

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

Solution 4

Here is a quick put-together/drop-in that can be used for capturing all current, or all new (since a last request) log items.

You should modify/extend this, because you might want to return a continuous-stream rather than a LogCapture.

The Android LogCat "Manual": 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 your project which performs activity logging:

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

When you want to capture everything you logged through "Logger"

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

When you get hungry and you want to snack on more logs

LogCapture nextCapture = capture.getNextCapture();

You can get the capture as a string with

String captureString = capture.toString();

Or you can get the log items of the capture with

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

There is no exact static method to capture foreign log keys but there is a way none the less

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

Using the above will also permit you to call Logger.this.nextCapture on the foreign capture.

Solution 5

The "-c" flag clears the buffer.

-c Clears (flushes) the entire log and exits.

Share:
85,001
David
Author by

David

An Android Developer that loves what he does! :) Github: https://github.com/davidbeloo

Updated on September 12, 2021

Comments

  • David
    David over 2 years

    I want to read and react to logcat logs within my application.

    I found the following code:

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

    This code indeed returns the logcat logs that made until the application was started -

    But is it possible to continuously listen to even new logcat logs?

  • David
    David over 11 years
    If I remove the "-d" the application stucked and I'm getting the force closed dialog.
  • Luis
    Luis over 11 years
    As I said above, it would required a carefull application design. You need to have the above code running in a separate thread (to avoid blocking the UI), and as you want to update the textview in the UI with log information, you need to use a Handler to post the information back to UI.
  • yuva ツ
    yuva ツ about 11 years
    how to use -c in above code.if found something in log i want to clear it
  • djdance
    djdance almost 10 years
    yuva, just do: process = Runtime.getRuntime().exec("logcat -c"); bufferedReader = new BufferedReader( new InputStreamReader(process.getInputStream()), 1024); line = bufferedReader.readLine();
  • Hypersoft Systems
    Hypersoft Systems almost 7 years
    This is generally the best method to perform logging and analysis due to the low-overhead[in processing] strategy. There may or may not be a bug in this code. logcat treatment of time selection must be greater than time given for a correct match. Lack of a correct time selection algorithm will create a duplicate log entry at the first element of nextCapture.
  • Hypersoft Systems
    Hypersoft Systems almost 7 years
    The lack of documentation on time formats and locales associated with android-logcat's time selector option may have created bugs which will require time-format-interpolation-modifications.
  • img.simone
    img.simone over 6 years
    Hi.. I used your code but the result is always empty. Is still valid?
  • img.simone
    img.simone over 6 years
    Hi Luis, please can you post an example code of a separated thread?
  • Hypersoft Systems
    Hypersoft Systems over 6 years
    @img.simone; I have updated this with my code revisions. Android's logcat is broken in a few ways. The first, is that the date format output of logcat has no year component, which makes a date comparison fallback to the year 1970; secondly the -t and -T option with a date doesn't actually start spitting out logs at the date specified, so we have to parse the date and compare it with a numeric date synchronized to the year 1970. I can't test this update for you, but it should definitely work; as the code comes from a working repository with amendments specific to this context.
  • Hypersoft Systems
    Hypersoft Systems over 6 years
    You can find the working code here under git.hsusa.core.log.controller.AndroidLogController.java; You may wish to use my hscore library instead of this "quick & dirty" solution. To do logging with hscore you would use: public final static SmartLogContext log = SmartLog.getContextFor("MyLogContext"); to get started. It works pretty much the same way with a better API. You can use my git hub issue tracker if you need ANY help with that.
  • user1185087
    user1185087 over 4 years
    See answer stackoverflow.com/a/59511458/1185087 it's using coroutines which don't block the UI thread.
  • Fatih
    Fatih almost 4 years
    Great suggestion. I wonder if there is a way to make this work with WebView instead of TextView.
  • Tom Rutchik
    Tom Rutchik almost 4 years
    I just discovered that clearing the log can take some time. So if you clear the log and then read the log immediate, some old entries may not yet been cleared. In addition, some newly added log entries can be deleted as because they get added before the clear completes. It doesn't make a difference even if you call process.waitfor().
  • Arsenius
    Arsenius over 3 years
    But why I cannot access logcat of another application? Runtime.getRuntime().exec("logcat some.other.app")
  • FEBRYAN ASA PERDANA
    FEBRYAN ASA PERDANA about 3 years
    @Arsenius what is the version of Android OS in your device? Starting from Android5/6/7 and higher you can not read info about other apps, including its status and logs. So there's absolutely no way to do that...................... unless you're rooted.
  • Eyal Elbaz
    Eyal Elbaz about 3 years
    is there any way I can to this in nonUI thread ?
  • DeXter
    DeXter almost 3 years
    @EyalElbaz I believe this answer is on nonUI thread. liveData(viewModelScope.coroutineContext + Dispatchers.IO) makes the logcat codes work on a subthread for IO operations using Coroutines in Kotlin.
  • Akito
    Akito over 2 years
    In case someone else has trouble resolving viewModels(): stackoverflow.com/a/69970914/7061105
  • spartygw
    spartygw over 2 years
    I don't know if this was allowed back in 2012 but it isn't any longer. developer.android.com/reference/android/…
  • NeverEndingQueue
    NeverEndingQueue about 2 years
    I am wondering is there any way to filter by log verbosity?
  • Nicolas Mage
    Nicolas Mage about 2 years
    @NeverEndingQueue I am not sure if you mean applying a filter to the data or as a string operation on the logs, but an efficient way to do this would be changing the above implementation with a LiveData stream to a Kotlin Flow stream and then apply a sorting/filtering intermediary operator (function) before you collect/observe the data. Overview/Flow Creation developer.android.com/kotlin/flow Modifying (filter/sort) The Stream developer.android.com/kotlin/flow#modify
  • Nicolas Mage
    Nicolas Mage about 2 years
    @NeverEndingQueue if you want to try to filter the logs similar to how you would in an IDE you might find this helpful as well. These commands can be fed to logcat and it looks like the -e flag with a regex might work for what you want, filtering based on type of log output (v, d, i, e, etc..) or even a logtag. developer.android.com/studio/command-line/logcat#Syntax