Чтение 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 02

1
Как мне его очистить? - Мне нужно найти конкретную строку, касающуюся моего заявления
Дэвид

1
Корень не требуется.
Луис

2
Если root не требуется, только сборка разработки может получить доступ к собственному журналу? И где именно этот код исполняется? с в приложении apk?
Ayyappa

2
Я пробовал тест. Я мог читать только события своего собственного процесса. Я думаю, вам нужен root, чтобы читать события других процессов.
Yeti99

Ответы:


55

Вы можете продолжать читать журналы, просто сняв флаг «-d» в приведенном выше коде.

Флаг "-d" дает команду logcat показать содержимое журнала и выйти. Если вы удалите флаг, logcat не завершит работу и продолжит посылать любую новую строку, добавленную к нему.

Просто имейте в виду, что это может заблокировать ваше приложение, если оно неправильно спроектировано.

удачи.


Если я удалю «-d», приложение зависнет, и я получаю диалоговое окно принудительного закрытия.
Дэвид

21
Как я уже сказал выше, это потребует тщательного проектирования приложения. Вам необходимо, чтобы приведенный выше код работал в отдельном потоке (чтобы избежать блокировки пользовательского интерфейса), и, поскольку вы хотите обновить текстовое представление в пользовательском интерфейсе с информацией журнала, вам необходимо использовать обработчик для отправки информации обратно в пользовательский интерфейс.
Луис

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

См. Ответ stackoverflow.com/a/59511458/1185087, он использует сопрограммы, которые не блокируют поток пользовательского интерфейса.
user1185087

8

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

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

Я только что обнаружил, что очистка журнала может занять некоторое время. Поэтому, если вы очистите журнал, а затем сразу же прочитаете его, некоторые старые записи могут еще не быть очищены. Кроме того, некоторые недавно добавленные записи журнала могут быть удалены, поскольку они добавляются до завершения очистки. Это не имеет значения, даже если вы вызываете process.waitfor ().
Том Рутчик,

6

С сопрограммами и официальными библиотеками lifecycle-livingata-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

Если вы хотите захватить все, что вы вошли через "Регистратор"

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 01

Отсутствие документации о форматах времени и локали, связанных с опцией селектора времени android-logcat, могло привести к ошибкам, которые потребовали бы модификаций интерполяции формата времени.
Hypersoft Systems 01

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

@ img.simone; Я обновил это своими исправлениями кода. Logcat в 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 ツ

Юва, просто сделай: process = Runtime.getRuntime (). exec ("logcat -c"); bufferedReader = новый BufferedReader (новый InputStreamReader (process.getInputStream ()), 1024); строка = 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 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.