Програмно читати logcat у програмі


116

Я хочу читати та реагувати на журнали logcat у своїй програмі.

Я знайшов такий код:

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

Цей код дійсно повертає журнали logcat, які були зроблені до запуску програми -

Але чи можна постійно слухати навіть нові журнали logcat?


1
Параметр -d скидає журнал і вийде. Просто видаліть опцію -d і logcat не вийде.
Frohnzie

1
Як я можу це очистити? - Мені потрібно знайти конкретний рядок щодо моєї заяви
Девід

1
Корінь не потрібно.
Луїс

2
Якщо корінь не потрібен, тільки розробка розробки може отримати доступ до власного журналу? І де саме виконується цей код? у програмі apk?
Айяппа

2
Я спробував тест. Я зміг лише прочитати події власного процесу. Я думаю, вам потрібен root, щоб прочитати інші події процесів.
Yetti99

Відповіді:


55

Ви можете продовжувати читати журнали, просто видаливши прапор "-d" у своєму коді вище.

Прапор "-d" вказує logcat, щоб показати вміст журналу та вихід. Якщо ви видалите прапор, logcat не припиняється і продовжує надсилати до нього будь-який новий рядок.

Майте на увазі, що це може заблокувати вашу програму, якщо вона не правильно розроблена.

Щасти.


Якщо я видаляю "-d", додаток застряг, і я закриваю діалогове вікно.
Девід

21
Як я вже говорив вище, це вимагатиме ретельної розробки додатків. Потрібно, щоб вищезазначений код працював в окремому потоці (щоб уникнути блокування інтерфейсу користувача), і оскільки ви хочете оновити перегляд тексту в інтерфейсі з інформацією журналу, вам потрібно використовувати обробник, щоб відправити інформацію назад в інтерфейс користувача.
Луїс

2
Привіт Луїс, будь ласка, можете опублікувати приклад коду окремої нитки?
img.simone

Дивіться відповідь stackoverflow.com/a/59511458/1185087 він використовує супроводи, які не блокують потік інтерфейсу користувача.
користувач1185087

8

Ви можете очистити свою реєстраційну систему за допомогою цього методу, який я використовую для очищення після запису logcat у файл, щоб уникнути дублювання рядків:

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

Щойно я виявив, що очищення журналу може зайняти деякий час. Тож якщо ви очистите журнал, а потім прочитаєте журнал негайно, деякі старі записи ще не можуть бути очищені. Крім того, деякі щойно додані записи журналу можна видалити, оскільки вони додаються до завершення очищення. Це не має значення, навіть якщо ви викликаєте process.waitfor ().
Том Рутчик

6

З підпрограмами та офіційними бібліотеками lifecycle-livedata-ktx та lifecycle-viewmodel-ktx це просто так:

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

Використання

val logCatViewModel by viewModels<LogCatViewModel>()

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

Чудова пропозиція. Цікаво, чи є спосіб зробити цю роботу WebViewзамість цього TextView.
Фатих

4

Ось швидкий збір / випадок, який можна використовувати для фіксації всіх поточних або всіх нових (з останнього запиту) елементів журналу.

Вам слід змінити / розширити це, оскільки ви, можливо, захочете повернути безперервний потік, а не LogCapture.

"Посібник" для 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);
  }

}

У вашому проекті, який здійснює журнал активності:

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

Коли ви хочете зафіксувати все, що ви ввійшли через "Logger"

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

Коли ви зголоднієте і хочете перекусити більше журналів

LogCapture nextCapture = capture.getNextCapture();

Ви можете отримати захоплення у вигляді рядка за допомогою

String captureString = capture.toString();

Або ви можете отримати елементи журналу захоплення за допомогою

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

Не існує точного статичного методу для зйомки іноземних ключів журналу, але все ж таки існує спосіб

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

Використання вищезазначеного також дозволить вам зателефонувати Logger.this.nextCaptureна іноземний захоплення.


Це, як правило, найкращий метод для ведення журналів та аналізу завдяки стратегії з низькими накладними витратами [в обробці]. Помилка в цьому коді може бути, а може і не бути. Вибір часу logcat повинен бути більшим, ніж час, призначений для правильної відповідності. Відсутність правильного алгоритму вибору часу створить повторюваний запис журналу на першому елементі nextCapture.
Hypersoft Systems

Відсутність документації про часові формати та локалі, пов’язані з опцією вибору часу для android-logcat, можливо, створила помилки, які потребують змін у форматі часу, інтерполяції.
Hypersoft Systems

Привіт .. Я використовував ваш код, але результат завжди порожній. Чи все ще діє?
img.simone

@ img.simone; Я оновив це за допомогою моїх версій коду. Локальна система Android розбита кількома способами. По-перше, це те, що вихід формату дати logcat не має компонента року, що робить порівняння дати порівняння з 1970 роком; по-друге, опція -t і -T з датою насправді не починає випилювати журнали на вказану дату, тому ми повинні проаналізувати дату та порівняти її з числовою датою, синхронізованою до 1970 року. Я не можу перевірити це оновлення для вас, але воно обов'язково має працювати; оскільки код надходить з робочого сховища зі змінами, специфічними для цього контексту.
Hypersoft Systems

1
Ви можете знайти робочий код тут під git.hsusa.core.log.controller.AndroidLogController.java; Ви можете використовувати мою бібліотеку hscore замість цього "швидкого та брудного" рішення. Для ведення журналу за допомогою hscore ви б використовували: public final static SmartLogContext log = SmartLog.getContextFor("MyLogContext");для початку. Це працює майже так само, як і кращий API. Ви можете використовувати мій трекер випуску git hub, якщо вам потрібна БУДЬ-яка допомога з цим.
Hypersoft Systems

3

Прапор "-c" очищає буфер.

-c Очищає (змиває) весь журнал і виходить.


як використовувати -c у вищевказаному коді. Якщо знайдено щось у журналі, я хочу його очистити
yuva,

yuva, просто роби: process = Runtime.getRuntime (). exec ("logcat -c"); bufferedReader = новий BufferedReader (новий 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"))
              {
              }
            }

Я використовую це у своєму додатку, і це працює. І ви можете використовувати вищезазначений код у Timer Task, щоб він не зупинив вашу основну нитку

        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);
      }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.