애플리케이션 내에서 프로그래밍 방식으로 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-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.
— 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.