Baca logcat secara terprogram dalam aplikasi

116

Saya ingin membaca dan bereaksi terhadap log logcat dalam aplikasi saya.

Saya menemukan kode berikut:

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

Kode ini memang mengembalikan log logcat yang dibuat hingga aplikasi dimulai -

Tetapi apakah mungkin untuk terus mendengarkan log logcat yang baru?

David
sumber
1
Opsi -d akan membuang log dan keluar. Hapus saja opsi -d dan logcat tidak akan keluar.
Frohnzie
1
Bagaimana saya bisa menghapusnya? - Saya perlu menemukan baris khusus terkait lamaran saya
David
1
Tidak diperlukan root.
Luis
2
Jika root tidak diperlukan, hanya build pengembangan yang dapat mengakses lognya sendiri? Dan di mana tepatnya kode ini dijalankan? dengan dalam aplikasi apk?
Ayyappa
2
Saya mencoba tes. Saya hanya bisa membaca peristiwa proses saya sendiri. Saya pikir Anda perlu root untuk membaca acara proses lainnya.
Yetti99

Jawaban:

55

Anda dapat terus membaca log, hanya dengan menghapus tanda "-d" pada kode Anda di atas.

Bendera "-d" memerintahkan logcat untuk menampilkan konten log dan keluar. Jika Anda menghapus bendera, logcat tidak akan menghentikan dan terus mengirimkan baris baru yang ditambahkan ke dalamnya.

Perlu diingat bahwa ini dapat memblokir aplikasi Anda jika tidak dirancang dengan benar.

semoga berhasil.

Luis
sumber
Jika saya menghapus "-d" aplikasi macet dan saya mendapatkan dialog paksa ditutup.
David
21
Seperti yang saya katakan di atas, diperlukan desain aplikasi yang cermat. Anda harus menjalankan kode di atas dalam utas terpisah (untuk menghindari pemblokiran UI), dan karena Anda ingin memperbarui textview di UI dengan informasi log, Anda perlu menggunakan Handler untuk memposting informasi kembali ke UI.
Luis
2
Hai Luis, bisakah Anda memposting kode contoh utas terpisah?
img.simone
Lihat jawaban stackoverflow.com/a/59511458/1185087 menggunakan coroutine yang tidak memblokir utas UI.
user1185087
8

Anda dapat menghapus logcat Anda dengan metode ini yang saya gunakan untuk menghapus setelah menulis logcat ke file untuk menghindari baris duplikat:

public void clearLog(){
     try {
         Process process = new ProcessBuilder()
         .command("logcat", "-c")
         .redirectErrorStream(true)
         .start();
    } catch (IOException e) {
    }
}
Jan Ziesse
sumber
Saya baru tahu bahwa membersihkan log bisa memakan waktu lama. Jadi jika Anda mengosongkan log dan kemudian langsung membaca log tersebut, beberapa entri lama mungkin belum dihapus. Selain itu, beberapa entri log yang baru ditambahkan dapat dihapus karena ditambahkan sebelum pembersihan selesai. Tidak ada bedanya bahkan jika Anda memanggil process.waitfor ().
Tom Rutchik
6

Dengan coroutine dan pustaka lifecycle-Liveata-ktx dan lifecycle-viewmodel-ktx yang resmi :

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

Pemakaian

val logCatViewModel by viewModels<LogCatViewModel>()

logCatViewModel.logCatOutput().observe(this, Observer{ logMessage ->
    logMessageTextView.append("$logMessage\n")
})
pengguna1185087
sumber
Saran bagus. Saya ingin tahu apakah ada cara untuk membuat ini berhasil, WebViewbukan TextView.
Fatih
4

Berikut ini adalah penyatuan / drop-in cepat yang dapat digunakan untuk menangkap semua item log saat ini, atau semua baru (sejak permintaan terakhir).

Anda harus mengubah / memperluas ini, karena Anda mungkin ingin mengembalikan aliran berkelanjutan daripada LogCapture.

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

}

Dalam proyek Anda yang melakukan pencatatan aktivitas:

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

Ketika Anda ingin menangkap semua yang Anda masuki "Logger"

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

Saat Anda lapar dan ingin mengemil lebih banyak batang kayu

LogCapture nextCapture = capture.getNextCapture();

Anda bisa mendapatkan tangkapan sebagai string

String captureString = capture.toString();

Atau Anda bisa mendapatkan item log penangkapan dengan

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

Tidak ada metode statis yang tepat untuk menangkap kunci log asing tetapi ada caranya

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

Menggunakan cara di atas juga akan memungkinkan Anda untuk memanggil Logger.this.nextCapturepenangkap asing.

Sistem Hypersoft
sumber
Ini umumnya merupakan metode terbaik untuk melakukan logging dan analisis karena strategi overhead [dalam pemrosesan] yang rendah. Mungkin ada atau mungkin tidak ada bug dalam kode ini. perlakuan logcat pemilihan waktu harus lebih besar dari waktu yang diberikan untuk kecocokan yang benar. Kurangnya algoritme pemilihan waktu yang tepat akan membuat entri log duplikat di elemen pertama nextCapture.
Sistem Hypersoft
Kurangnya dokumentasi tentang format waktu dan lokal yang terkait dengan opsi pemilih waktu android-logcat mungkin telah menciptakan bug yang akan memerlukan modifikasi waktu-format-interpolasi.
Hypersoft Systems
Hai .. Saya menggunakan kode Anda tetapi hasilnya selalu kosong. Apakah masih valid?
img.simone
@ img.indonesia; Saya telah memperbarui ini dengan revisi kode saya. Logcat Android rusak dalam beberapa cara. Yang pertama, adalah bahwa keluaran format tanggal dari logcat tidak memiliki komponen tahun, yang membuat perbandingan tanggal mundur ke tahun 1970; kedua, opsi -t dan -T dengan tanggal tidak benar-benar mulai mengeluarkan log pada tanggal yang ditentukan, jadi kita harus mengurai tanggal dan membandingkannya dengan tanggal numerik yang disinkronkan ke tahun 1970. Saya tidak dapat menguji ini memperbarui untuk Anda, tetapi seharusnya berfungsi; karena kode berasal dari repositori yang berfungsi dengan amandemen khusus untuk konteks ini.
Hypersoft Systems
1
Anda dapat menemukan kode yang berfungsi di sini di bawah git.hsusa.core.log.controller.AndroidLogController.java; Anda mungkin ingin menggunakan pustaka hscore saya daripada solusi "cepat & kotor" ini. Untuk melakukan logging dengan hscore Anda akan menggunakan: public final static SmartLogContext log = SmartLog.getContextFor("MyLogContext");untuk memulai. Cara kerjanya hampir sama dengan API yang lebih baik. Anda dapat menggunakan pelacak masalah git hub saya jika Anda memerlukan bantuan APA PUN dengan itu.
Hypersoft Systems
3

Bendera "-c" membersihkan buffer.

-c Menghapus (membersihkan) seluruh log dan keluar.

Allen
sumber
cara menggunakan -c dalam kode di atas. jika menemukan sesuatu di log, saya ingin menghapusnya
yuva ツ
yuva, lakukan saja: process = Runtime.getRuntime (). exec ("logcat -c"); bufferedReader = BufferedReader baru (InputStreamReader baru (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"))
              {
              }
            }

Saya menggunakan ini di aplikasi saya dan berhasil. Dan Anda dapat menggunakan kode di atas dalam Tugas Timer sehingga tidak akan menghentikan utas utama Anda

        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);
      }
Reetpreet Brar
sumber