Đọc logcat theo chương trình trong ứng dụng


116

Tôi muốn đọc và phản ứng với nhật ký logcat trong ứng dụng của tôi.

Tôi tìm thấy đoạn mã sau:

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

Mã này thực sự trả về nhật ký logcat được thực hiện cho đến khi ứng dụng được khởi động -

Nhưng nó có thể liên tục nghe các bản ghi logcat mới không?


1
Tùy chọn -d sẽ kết xuất nhật ký và thoát. Chỉ cần loại bỏ tùy chọn -d và logcat sẽ không thoát.
Frohnzie

1
Làm thế nào tôi có thể xóa nó? - Tôi cần tìm một dòng cụ thể liên quan đến ứng dụng của mình
David

1
Không cần root.
Luis

2
Nếu không cần root, chỉ có bản dựng phát triển mới có thể truy cập vào nhật ký của chính nó? Và chính xác mã này được thực thi ở đâu? với ứng dụng apk nào?
Ayyappa

2
Tôi đã thử một bài kiểm tra. Tôi chỉ có thể đọc các sự kiện của quá trình của riêng tôi. Tôi nghĩ rằng bạn cần root để đọc các sự kiện quy trình khác.
Yetti99

Câu trả lời:


55

Bạn có thể tiếp tục đọc nhật ký, chỉ bằng cách xóa cờ "-d" trong mã của bạn ở trên.

Cờ "-d" hướng dẫn logcat để hiển thị nội dung nhật ký và thoát. Nếu bạn xóa cờ, logcat sẽ không chấm dứt và tiếp tục gửi bất kỳ dòng mới nào được thêm vào nó.

Chỉ cần lưu ý rằng điều này có thể chặn ứng dụng của bạn nếu không được thiết kế chính xác.

chúc may mắn.


Nếu tôi xóa "-d", ứng dụng bị kẹt và tôi sẽ nhận được hộp thoại đóng.
David

21
Như tôi đã nói ở trên, nó sẽ đòi hỏi một thiết kế ứng dụng cẩn thận. Bạn cần chạy đoạn mã trên trong một luồng riêng biệt (để tránh chặn UI) và khi bạn muốn cập nhật textview trong UI với thông tin nhật ký, bạn cần sử dụng Handler để đăng thông tin trở lại UI.
Luis

2
Xin chào Luis, xin vui lòng bạn có thể gửi một mã ví dụ của một chủ đề riêng biệt?
img.simone

Xem câu trả lời stackoverflow.com/a/59511458/1185087 bằng cách sử dụng coroutines không chặn luồng UI.
dùng1185087

8

Bạn có thể xóa logcat của mình bằng phương thức này tôi đang sử dụng để xóa sau khi ghi logcat vào một tệp để tránh các dòng trùng lặp:

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

Tôi chỉ phát hiện ra rằng việc xóa nhật ký có thể mất một thời gian. Vì vậy, nếu bạn xóa nhật ký và sau đó đọc nhật ký ngay lập tức, một số mục cũ có thể chưa bị xóa. Ngoài ra, một số mục nhật ký mới được thêm vào có thể bị xóa vì chúng được thêm vào trước khi hoàn thành xóa. Nó không tạo ra sự khác biệt ngay cả khi bạn gọi process.waitfor ().
Tom Rutchik

6

Với coroutines và chính thức thư viện vòng đời -ktxvòng đời-viewmodel-ktx, nó đơn giản như thế:

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

Sử dụng

val logCatViewModel by viewModels<LogCatViewModel>()

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

Đề nghị tuyệt vời. Tôi tự hỏi nếu có một cách để làm cho công việc này WebViewthay vì TextView.
Fatih

4

Dưới đây là một tập hợp / thả nhanh chóng có thể được sử dụng để chụp tất cả các mục nhật ký hiện tại hoặc tất cả các mục nhật ký mới (kể từ khi có yêu cầu cuối cùng).

Bạn nên sửa đổi / mở rộng điều này, bởi vì bạn có thể muốn trả về một luồng liên tục thay vì LogCapture.

"Hướng dẫn" 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);
  }

}

Trong dự án của bạn thực hiện ghi nhật ký hoạt động:

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

Khi bạn muốn chụp mọi thứ bạn đã đăng nhập thông qua "Logger"

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

Khi bạn đói và bạn muốn ăn nhẹ vào nhiều khúc gỗ hơn

LogCapture nextCapture = capture.getNextCapture();

Bạn có thể lấy bản chụp dưới dạng chuỗi với

String captureString = capture.toString();

Hoặc bạn có thể lấy các mục nhật ký của bản chụp với

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

Không có phương pháp tĩnh chính xác để chụp các khóa nhật ký nước ngoài nhưng có một cách không kém

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

Sử dụng ở trên cũng sẽ cho phép bạn kêu gọi Logger.this.nextCapturebắt giữ nước ngoài.


Đây thường là phương pháp tốt nhất để thực hiện ghi nhật ký và phân tích do chiến lược [xử lý] chi phí thấp. Có thể có hoặc không có lỗi trong mã này. điều trị logcat của lựa chọn thời gian phải lớn hơn thời gian cho một kết hợp chính xác. Thiếu một thuật toán lựa chọn thời gian chính xác sẽ tạo ra một mục nhật ký trùng lặp ở phần tử đầu tiên của nextCapture.
Hệ thống Hypersoft

Việc thiếu tài liệu về các định dạng thời gian và địa phương liên quan đến tùy chọn bộ chọn thời gian của android-logcat có thể đã tạo ra các lỗi sẽ yêu cầu sửa đổi nội suy định dạng thời gian-định dạng.
Hệ thống Hypersoft

Xin chào .. Tôi đã sử dụng mã của bạn nhưng kết quả luôn trống. Vẫn còn hợp lệ?
img.simone

@ img.simone; Tôi đã cập nhật điều này với sửa đổi mã của tôi. Logcat của Android bị hỏng theo một số cách. Đầu tiên, là đầu ra định dạng ngày của logcat không có thành phần năm, điều này làm cho sự so sánh ngày trở thành năm 1970; thứ hai là tùy chọn -t và -T với một ngày không thực sự bắt đầu phun ra các bản ghi vào ngày được chỉ định, vì vậy chúng tôi phải phân tích ngày và so sánh nó với một ngày số được đồng bộ hóa với năm 1970. Tôi không thể kiểm tra điều này cập nhật cho bạn, nhưng nó chắc chắn sẽ hoạt động; vì mã đến từ một kho lưu trữ làm việc với các sửa đổi cụ thể cho bối cảnh này.
Hệ thống Hypersoft

1
Bạn có thể tìm thấy mã làm việc ở đây dưới git.hsusa.core.log.controller.AndroidLogController.java; Bạn có thể muốn sử dụng thư viện hscore của tôi thay vì giải pháp "nhanh & bẩn" này. Để đăng nhập với hscore bạn sẽ sử dụng: public final static SmartLogContext log = SmartLog.getContextFor("MyLogContext");để bắt đầu. Nó hoạt động khá giống với một API tốt hơn. Bạn có thể sử dụng trình theo dõi vấn đề trung tâm git của tôi nếu bạn cần BẤT K help sự giúp đỡ nào.
Hệ thống Hypersoft

3

Cờ "-c" xóa bộ đệm.

-c Xóa (xóa) toàn bộ nhật ký và thoát.


Làm thế nào để sử dụng -c trong đoạn mã trên. Nếu tìm thấy một cái gì đó trong nhật ký tôi muốn xóa nó
yuva

yuva, chỉ cần làm: process = Runtime.getR nb (). exec ("logcat -c"); bufferedReader = new BufferedReader (new InputStreamReader (process.getInputStream ()), 1024); dòng = 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"))
              {
              }
            }

Tôi đang sử dụng điều này trong ứng dụng của tôi và nó hoạt động. Và bạn có thể sử dụng mã ở trên trong Nhiệm vụ hẹn giờ để nó không dừng luồng chính của bạn

        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);
      }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.