애플리케이션 내에서 프로그래밍 방식으로 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
어떻게 지울 수 있습니까? -내 지원서와 관련된 특정 라인을 찾아야합니다
David

1
루트가 필요하지 않습니다.
Luis

2
루트가 필요하지 않은 경우 개발 빌드 만 자체 로그에 액세스 할 수 있습니까? 이 코드는 정확히 어디에서 실행됩니까? apk 응용 프로그램에서?
Ayyappa

2
나는 테스트를 시도했다. 내 프로세스의 이벤트 만 읽을 수있었습니다. 다른 프로세스 이벤트를 읽으려면 루트가 필요하다고 생각합니다.
Yetti99

답변:


55

위 코드에서 "-d"플래그를 제거하면 로그를 계속 읽을 수 있습니다.

"-d"플래그는 logcat에 로그 내용을 표시하고 종료하도록 지시합니다. 플래그를 제거하면 logcat이 종료되지 않고 추가 된 새 행을 계속 보냅니다.

올바르게 설계되지 않은 경우 애플리케이션이 차단 될 수 있음을 명심하십시오.

행운을 빕니다.


"-d"를 제거하면 응용 프로그램이 중단되고 강제 종료 대화 상자가 나타납니다.
David

21
위에서 말했듯이 신중한 애플리케이션 설계가 필요했습니다. UI 차단을 피하기 위해 위의 코드를 별도의 스레드에서 실행해야하며, UI의 textview를 로그 정보로 업데이트하려면 Handler를 사용하여 정보를 UI에 다시 게시해야합니다.
Luis

2
안녕하세요 Luis, 분리 된 스레드의 예제 코드를 게시 할 수 있습니까?
img.simone

답변 stackoverflow.com/a/59511458/1185087 참조 UI 스레드를 차단하지 않는 코 루틴을 사용하고 있습니다.
user1185087 dec.

8

중복 줄을 피하기 위해 logcat을 파일에 쓴 후 지우는 데 사용하는이 방법으로 logcat을 지울 수 있습니다.

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

방금 로그를 지우는 데 시간이 걸릴 수 있음을 발견했습니다. 따라서 로그를 지운 다음 즉시 로그를 읽으면 일부 이전 항목이 아직 지워지지 않을 수 있습니다. 또한 새로 추가 된 일부 로그 항목은 삭제가 완료되기 전에 추가되기 때문에 삭제할 수 있습니다. process.waitfor ()를 호출하더라도 차이가 없습니다.
Tom Rutchik

6

코 루틴과 공식 lifecycle-livedata-ktxlifecycle-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.
Fatih

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은 몇 가지 방법으로 손상됩니다. 첫 번째는 logcat의 날짜 형식 출력에 연도 구성 요소가 없기 때문에 날짜 비교를 1970 년으로 대체합니다. 두 번째로 날짜가있는 -t 및 -T 옵션은 지정된 날짜에 실제로 로그를 뱉어 내기 시작하지 않으므로 날짜를 구문 분석하여 1970 년과 동기화 된 숫자 날짜와 비교해야합니다. 나는 이것을 테스트 할 수 없습니다. 당신을 위해 업데이트하지만 확실히 작동합니다. 코드는이 컨텍스트에 특정한 수정 사항이있는 작업 저장소에서 제공되기 때문입니다.
Hypersoft Systems 2017

1
당신은 작업 코드를 찾을 수 있습니다 여기 아래를 git.hsusa.core.log.controller.AndroidLogController.java; 이 "빠르고 더러운"솔루션 대신 내 hscore 라이브러리를 사용할 수 있습니다. hscore로 로깅을 수행하려면 다음을 사용하십시오 public final static SmartLogContext log = SmartLog.getContextFor("MyLogContext");. 더 나은 API를 사용하면 거의 동일한 방식으로 작동합니다. 도움이 필요하면 내 git 허브 문제 추적기를 사용할 수 있습니다.
Hypersoft Systems 2017

3

"-c"플래그는 버퍼를 지 웁니다.

-c 전체 로그를 지우고 (플러시) 종료합니다.


위의 code.if에서 -c를 사용하는 방법 로그에서 뭔가를 발견하면 지우고 싶습니다
yuva ツ

yuva, 그냥하세요 : process = Runtime.getRuntime (). exec ( "logcat -c"); bufferedReader = new BufferedReader (new 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 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);
      }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.