Logcat을 이용한 로그 작성 및 보기

Android 스튜디오의 Logcat 창에는 시스템 메시지(예: 가비지 컬렉션 발생)와 Log 클래스를 사용하여 앱에 추가한 메시지가 표시됩니다. 메시지는 실시간으로 표시되며, 이전 메시지를 볼 수 있도록 기록이 유지됩니다.

관심 있는 정보만 표시하기 위해 필터를 만들고, 메시지에 표시되는 정보의 양을 수정하고, 우선순위 레벨을 설정하고, 앱 코드를 통해 생성된 메시지만 표시하고, 로그를 검색할 수 있습니다. 기본적으로 logcat은 가장 최근 실행한 앱과 관련된 로그 출력만 보여줍니다.

앱에서 예외가 발생하면 logcat은 메시지를 표시한 다음 코드 줄에 대한 링크를 포함한 연결된 스택 추적을 표시합니다.

Android 스튜디오 2.2부터는 Run 창에 현재 실행 중인 앱의 로그 메시지도 표시됩니다. 참고로 logcat 출력 화면은 구성할 수 있지만, Run 창은 구성할 수 없습니다.

앱 로그 보기

앱의 로그 메시지를 표시하려면 다음을 따르세요.

  1. 기기에서 앱을 빌드하고 실행합니다.
  2. View > Tool Windows > Logcat을 클릭합니다(또는 도구 창 모음에서 Logcat을 클릭).

Logcat 창은 그림 1과 같이 창의 상단에 있는 드롭다운 목록에서 선택한 대로 선택된 앱의 로그 메시지를 표시합니다.

그림 1. Logcat 창

기본적으로 logcat은 기기에서 실행 중인 앱의 로그 메시지만 표시합니다. 이 기본값을 변경하려면 logcat 메시지 필터링을 참조하세요.

Logcat 툴바는 다음과 같은 버튼을 제공합니다.

  1. Clear logcat : 클릭하여 표시된 로그를 삭제합니다.
  2. Scroll to the end : 클릭하여 로그의 맨 아래로 이동하고 최신 로그 메시지를 확인합니다. 그런 다음 로그에서 한 줄을 클릭하면 뷰는 그 지점에서 스크롤하는 것을 일시 중단합니다.
  3. Up the stack trace 및 Down the stack trace : 클릭하여 로그의 스택 추적에서 위아래로 이동하면서 출력된 예외 뒤에 표시되는 파일 이름을 선택하고 편집기에 대응하는 줄 번호를 확인합니다. 이 동작은 로그에서 파일 이름을 클릭하는 것과 같습니다.
  4. Use soft wraps : 클릭하여 줄바꿈을 사용하고 가로 방향 스크롤을 금지합니다(가로 방향 스크롤이 필요한 줄바꿈을 할 수 없는 모든 문자열 포함).
  5. Print : 클릭하여 logcat 메시지를 인쇄합니다. 표시된 대화상자에서 인쇄 설정을 선택한 후에 PDF에 저장하는 것도 선택할 수 있습니다.
  6. Restart : 클릭하여 로그를 삭제하고 logcat을 다시 시작합니다. Clear logcat 버튼과 달리 이 기능은 이전의 로그 메시지를 복구하여 표시합니다. 따라서, 이 기능은 Logcat이 응답하지 않고 로그 메시지를 잃고 싶지 않을 때 가장 유용합니다.
  7. Logcat header : 클릭하여 Configure Logcat Header 대화상자를 열고 각 Logcat 메시지의 출력 형식(예: 날짜와 시간의 표시 여부)을 사용자설정합니다.
  8. Screen capture : 클릭하여 스크린샷을 캡처합니다.
  9. Screen record : 클릭하여 기기의 화면을 동영상으로 녹화합니다(최대 3분).

로그 메시지 작성

Log 클래스를 사용하여 logcat에 표시되는 로그 메시지를 만들 수 있습니다. 일반적으로 다음과 같은 로그 메서드를 사용해야 하며, 우선순위가 가장 높은 것부터(또는 상세 수준이 가장 낮은 것부터) 순서대로 표시되어 있습니다.

전체 옵션 목록은 Log 클래스 설명을 참조하세요.

개발 단계를 제외하고 상세 로그를 앱으로 컴파일하면 안 됩니다. 디버그 로그는 런타임에 컴파일되지만 삭제되는 반면, 오류, 경고 및 정보 로그는 계속 유지됩니다.

각 로그 메서드에서 첫 번째 매개변수는 고유한 태그여야 하며 두 번째 매개변수는 메시지입니다. 시스템 로그 메시지의 태그는 메시지가 발생한 시스템 구성요소를 가리키는 짧은 문자열입니다(예: ActivityManager). 사용자가 정의한 태그는 현재 클래스의 이름과 같이 사용자가 유용하다고 생각하는 정보를 나타내는 문자열입니다.

첫 번째 매개변수에서 사용할 클래스에 TAG 상수 선언을 규칙으로 하는 것이 좋습니다. 예를 들어, 다음과 같이 정보 로그 메시지를 만들 수 있습니다.

Kotlin

    private const val TAG = "MyActivity"
    ...
    Log.i(TAG, "MyClass.getView() — get item number $position")
    

Java

    private static final String TAG = "MyActivity";
    ...
    Log.i(TAG, "MyClass.getView() — get item number " + position);
    

참고: 태그 이름이 23자를 초과하는 경우 logcat 출력에서 잘립니다.

logcat 메시지 형식

모든 Android 로그 메시지에는 태그 및 태그와 연관된 우선순위가 있습니다. 시스템 로그 메시지의 태그는 메시지가 발생한 시스템 구성요소(예: ActivityManager)를 가리키는 짧은 문자열입니다. 사용자 정의 태그는 현재 클래스의 이름과 같이 사용자가 유용하다고 생각하는 정보를 나타내는 문자열입니다(권장 태그). 예를 들어, 아래와 같이 Log 메서드 호출에서 이러한 태그를 정의합니다.

Kotlin

    Log.d(tag, message)
    

Java

    Log.d(tag, message);
    

우선순위는 다음 중 하나의 값을 갖습니다.

  • V: 상세 (가장 낮은 우선순위)
  • D: 디버그
  • I: 정보
  • W: 경고
  • E: 오류
  • A: 강제 종료

로그 메시지 형식은 다음과 같습니다.

    date time PID-TID/package priority/tag: message
    

예를 들어, 다음 로그 메시지의 우선순위는 V이고 태그는 AuthZen입니다.

    12-10 13:02:50.071 1901-4229/com.google.android.gms V/AuthZen: Handling delegate intent.
    

PID는 프로세스 식별자(process identifier), TID는 스레드 식별자(thread identifier)를 나타내며, 스레드가 하나만 있을 때는 두 ID가 같을 수 있습니다.

로그 레벨 설정

로그 레벨을 설정하여 logcat에 표시할 메시지 수를 제어할 수 있습니다. 모든 메시지를 표시하거나 가장 심각한 조건을 나타내는 메시지만 표시할 수 있습니다.

로그 레벨 설정과 관계없이 logcat은 계속 모든 메시지를 수집합니다. 이 설정은 단순히 logcat이 무엇을 표시할지 결정합니다.

Log level 메뉴에서 다음 값 중 하나를 선택합니다.

  • Verbose: 모든 로그 메시지를 표시합니다(기본 설정).
  • Debug: 개발 단계에서만 유용한 디버그 로그 메시지뿐 아니라 그보다 낮은 레벨의 메시지도 이 목록에 표시합니다.
  • Info: 일반적인 사용을 위해 예상할 수 있는 로그 메시지뿐 아니라 그보다 낮은 레벨의 메시지도 이 목록에 표시합니다.
  • Warn: 아직 오류는 아니지만 발생할 수 있는 문제뿐 아니라 그보다 낮은 레벨의 메시지도 이 목록에 표시합니다.
  • Error: 오류를 일으킨 문제뿐 아니라 그보다 낮은 레벨의 메시지도 이 목록에 표시합니다.
  • Assert: 개발자가 발생해서는 안 된다고 생각하는 문제를 표시합니다.

logcat 메시지 검색

현재 logcat에 표시된 메시지를 검색하려면 다음을 따르세요.

  1. 정규 표현식 검색 패턴을 사용하고 싶으면 Regex를 선택합니다(선택 사항).
  2. 검색 입력란 에 문자 시퀀스를 입력합니다.

    검색 결과에 따라 logcat 출력 화면이 변경됩니다.

  3. Enter 키를 눌러 이 세션 동안 메뉴에 검색 문자열을 저장합니다.
  4. 검색을 반복하려면 검색 메뉴에서 해당 검색을 선택합니다. 필요에 따라 Regex를 선택하거나 선택 해제합니다(이 설정이 저장되지는 않음).

logcat 메시지 필터링

로그 출력을 관리 가능한 수준으로 줄이는 한 가지 방법은 필터를 사용해 로그 출력을 제한하는 것입니다.

참고: 필터는 logcat에 현재 표시되고 있는 메시지뿐만 아니라 전체 logcat 기록에 적용됩니다. 검사하려는 필터 출력을 볼 수 있도록 다른 표시 옵션이 알맞게 설정되어 있는지 확인하세요.

필터를 정의하고 적용하는 방법은 다음과 같습니다.

  1. 필터 메뉴에서 필터 옵션을 선택합니다.
    • Show only selected application: 앱 코드에서 생성된 메시지만 표시합니다(기본 설정). Logcat이 활성 상태인 앱의 PID를 사용하여 로그 메시지를 필터링합니다.
    • No Filters: 필터를 적용하지 않습니다. Logcat이 개발자가 선택한 프로세스와 관계없이 기기에서 수신되는 모든 로그 메시지를 표시합니다.
    • Edit Filter Configuration: 사용자설정 필터를 만들거나 수정합니다. 예를 들어, 두 앱의 로그 메시지를 동시에 볼 수 있는 필터를 만들 수 있습니다.

    필터를 정의한 후 메뉴에서 이러한 필터를 선택할 수도 있습니다. 필터를 메뉴에서 삭제하려면 필터를 삭제하세요.

  2. Edit Filter Configuration을 선택한 경우 다음 순서를 따라 필터를 만들거나 수정합니다.
    1. Create New Logcat Filter 대화상자에서 필터 매개변수를 다음과 같이 지정합니다.
      • Filter Name: 정의하려는 필터의 이름을 입력하거나 왼쪽 창에서 기존 필터를 선택하여 수정합니다. 이름에는 소문자, 밑줄, 숫자만 사용할 수 있습니다.
      • Log Tag: 태그를 지정합니다(선택사항). 자세한 정보는 logcat 메시지 형식을 참조하세요.
      • Log Message: 로그 메시지 텍스트를 지정합니다(선택사항). 자세한 정보는 logcat 메시지 형식을 참조하세요.
      • Package Name: 패키지 이름을 지정합니다(선택사항). 자세한 정보는 logcat 메시지 형식을 참조하세요.
      • PID: 프로세스 ID를 지정합니다(선택사항). 자세한 정보는 logcat 메시지 형식을 참조하세요.
      • Log Level: 로그 레벨을 선택합니다(선택사항). 자세한 정보는 로그 레벨 설정을 참조하세요.
      • Regex: 특정 매개변수에 정규 표현식 구문을 사용하려면 이 옵션을 선택합니다.
    2. 왼쪽 창에 필터 정의를 추가하려면 +를 클릭하세요.

      필터를 삭제하려면 왼쪽 창에서 삭제할 필터를 선택한 후 -를 클릭하세요.

    3. 위 작업을 완료하면 OK를 클릭합니다.

원하는 로그 메시지가 표시되지 않으면 No filters를 선택하고 해당 로그 메시지를 검색해 보세요.

가비지 컬렉션 메시지 읽기

가비지 컬렉션 이벤트가 발생하면 logcat에 출력됩니다.

앱 메모리에 관한 자세한 정보는 메모리 프로파일러를 사용하세요.

Dalvik 로그 메시지

Dalvik(ART 아님)에서는 모든 GC가 logcat에 다음 정보를 출력합니다.

    D/dalvikvm(PID): GC_Reason Amount_freed, Heap_stats, External_memory_stats, Pause_time
    

예:

    D/dalvikvm( 9050): GC_CONCURRENT freed 2049K, 65% free 3571K/9991K, external 4703K/5261K, paused 2ms+2ms
    
GC Reason(GC 이유)
GC를 트리거한 이유와 가비지 컬렉션의 종류에 대한 설명입니다. 나타날 수 있는 이유는 다음과 같습니다.
GC_CONCURRENT
힙이 가득 차기 시작하면 메모리를 해제하는 동시 GC입니다.
GC_FOR_MALLOC
힙이 이미 가득 찼을 때 앱이 메모리 할당을 시도했기 때문에 GC가 발생한 것이므로, 시스템이 앱을 중지하고 메모리를 회수해야 했습니다.
GC_HPROF_DUMP_HEAP
힙을 분석하기 위해 HPROF 파일 생성을 요청할 때 발생하는 GC입니다.
GC_EXPLICIT
gc()를 호출하는 것과 같은 명시적인 GC입니다(필요할 때 GC가 실행될 것을 신뢰하고 이러한 함수 호출을 피해야 함).
GC_EXTERNAL_ALLOC
API 레벨 10 이하에서만 발생하는 GC입니다(그 이상의 버전에서는 Dalvik 힙에서 모든 메모리를 할당함). 외부에서 할당된 메모리를 위한 GC입니다(예: 네이티브 메모리 또는 NIO 바이트 버퍼에 저장되는 픽셀 데이터).
Amount freed(회수한 메모리 크기)
이 GC를 통해 회수한 메모리의 크기입니다.
Heap stats(힙 통계)
힙에서 회수한 비율로, (라이브 객체 수)/(총 힙 크기)로 계산됩니다.
External memory stats(외부 메모리 통계)
API 레벨 10 이하에서 외부 할당된 메모리로, (할당된 메모리의 크기)/(수집이 발생하게 되는 한계)입니다.
Pause time(일시중지 횟수)
힙이 클수록 일시중지 횟수가 많아집니다. 동시 일시중지 횟수는 두 번의 일시중지를 표시하는데, 하나는 수집의 시작 시점이고 다른 하나는 종료 시점 근처입니다.

이러한 로그 메시지가 누적되는 동안 힙 통계에서 증가하는 부분이 있는지 찾아보세요(위 예에서는 3571K/9991K 값). 이 값이 계속 증가한다면 메모리 누수가 있을 수 있습니다.

ART 로그 메시지

Dalvik과 달리, ART는 GC를 명시적으로 요청하지 않으면 GC에 관한 메시지를 로그에 기록하지 않습니다. GC는 GC가 느리다고 판단될 때만 출력됩니다. 정확히 말하자면, GC 일시중지가 5ms를 초과하거나 GC 시간이 100ms를 초과하는 경우입니다. 앱이 눈에 띌 정도로 일시중지된 상태가 아니라면(예: 앱이 백그라운드에서 실행 중이거나 사용자가 GC 일시중지 상태를 인식하지 못하는 경우) 앱의 GC는 느리다고 판단되지 않습니다. 명시적 GC는 항상 로그에 기록됩니다.

ART는 가비지 컬렉션 로그 메시지에 다음 정보를 포함합니다.

    I/art: GC_Reason GC_Name Objects_freed(Size_freed) AllocSpace Objects,
        Large_objects_freed(Large_object_size_freed) Heap_stats LOS objects, Pause_time(s)
    

예:

    I/art : Explicit concurrent mark sweep GC freed 104710(7MB) AllocSpace objects,
        21(416KB) LOS objects, 33% free, 25MB/38MB, paused 1.230ms total 67.216ms
    
GC Reason(GC 이유)
GC를 트리거한 이유와 가비지 컬렉션의 종류에 대한 설명입니다. 나타날 수 있는 이유는 다음과 같습니다.
Concurrent
앱 스레드를 일시정지하지 않는 동시 GC입니다. 이 GC는 백그라운드 스레드에서 실행되며 메모리 할당을 막지 않습니다.
Alloc
힙이 이미 가득 찼을 때 앱이 메모리 할당을 시도했기 때문에 이 GC가 시작되었습니다. 이 경우에는 가비지 컬렉션이 할당 스레드에서 발생합니다.
Explicit
앱이 가비지 컬렉션을 명시적으로 요청했습니다(예: gc() 또는 gc() 호출). Dalvik과 마찬가지로, ART에서는 가급적 GC를 신뢰하고 GC를 명시적으로 요청하지 않는 것이 권장사항입니다. 명시적 GC는 할당 스레드를 차단하고 불필요하게 CPU 사이클을 낭비하므로 사용하지 않는 것을 권장합니다. 명시적인 GC로 인해 다른 스레드가 선점될 경우에는 이상 현상(앱의 버벅거림, 끊김 또는 중단)이 발생할 수도 있습니다.
NativeAlloc
Bitmap 또는 RenderScript 할당 객체와 같은 네이티브 할당에 따른 네이티브 메모리 압력으로 인해 발생한 가비지 컬렉션입니다.
CollectorTransition
이 가비지 컬렉션은 힙 전환에 의해 발생하며 힙 전환은 런타임에 GC 전략을 변경하여 발생합니다(예: 눈에 띄는 일시중지 상태 사이에 앱이 변경되는 경우). 가비지 컬렉터 전환은 사용 가능 목록으로 지정된 공간에서 범프 포인터 공간으로(또는 그 반대로) 모든 객체를 복사하도록 구성됩니다.

Android 8.0 이전의 RAM 사양이 낮은 기기에서 앱이 포그라운드에서 실행 중인데도 사용자가 GC 일시중지를 인식할 수 있을 정도로 눈에 띄는 일시중지 상태에서 인식할 수 없는 일시중지 상태로(혹은 그 반대) 프로세스 상태를 변경할 때 가비지 컬렉터 전환이 발생합니다.

HomogeneousSpaceCompact
같은 종류의 공간 압축은 사용 가능 목록 공간 간의 압축으로, 보통 앱이 일시중지를 인식할 수 없는 프로세스 상태로 이동할 때 발생합니다. 이 작업을 하는 주된 이유는 RAM 사용량을 줄이고 힙 조각 모음을 하기 위해서 입니다.
DisableMovingGc
실제 GC 이유는 아니지만, 동시 힙 압축이 이루어지는 동안 GetPrimitiveArrayCritical을 사용했기 때문에 가비지 컬렉션이 차단되었다는 점에 주의해야 합니다. 일반적으로, GetPrimitiveArrayCritical은 가비지 컬렉터 이동을 제한하므로 사용하지 않을 것을 적극적으로 권장합니다.
HeapTrim
GC 이유는 아니지만, 힙 트림을 마칠 때까지 가비지 컬렉션이 차단된다는 점에 주의해야 합니다.
GC Name(GC 이름)
ART에서는 여러 가지 다양한 GC를 실행할 수 있습니다.
Concurrent mark sweep (CMS)
이미지 공간을 제외한 모든 공간을 회수하고 수집하는 완전한 힙 컬렉터입니다.
Concurrent partial mark sweep
이미지 및 zygote 공간을 제외한 모든 공간을 수집하는 거의 완전한 힙 컬렉터입니다.
Concurrent sticky mark sweep
마지막 GC 이후로 할당된 객체만 회수할 수 있는 세대 간 컬렉터입니다. 이 가비지 컬렉션이 더 빠르고 일시중지 횟수도 적으므로 전체 또는 부분 마크 스윕보다 더 자주 실행됩니다.
Marksweep + semispace
(힙 조각 모음을 위한) 같은 종류의 공간 압축뿐 아니라 힙 전환에도 사용되는 비동시 방식의 복사 GC입니다.
Objects freed(회수한 객체 수)
크지 않은 객체 공간에서 이 GC를 통해 회수한 객체의 수입니다.
Size freed(회수한 공간의 크기)
크지 않은 객체 공간에서 이 GC를 통해 회수한 바이트 수입니다.
Large objects freed(회수한 큰 객체 수)
이 가비지 컬렉션으로 회수한 큰 객체 공간에 있는 객체 수입니다.
Large object size freed(회수한 큰 객체의 크기)
이 가비지 컬렉션으로 회수한 큰 객체 공간에 있는 바이트 수입니다.
Heap stats(힙 통계)
회수한 비율로, (라이브 객체 수)/(총 힙 크기)로 계산됩니다.
Pause times(일시중지 횟수)
일반적으로 일시중지 횟수는 GC 실행 중에 수정한 객체 참조의 개수에 비례합니다. 현재 ART CMS GC는 GC 종료 시점 근처에서 한 번만 일시중지됩니다. 이동 GC에는 GC 지속 시간 중 대부분의 시간 동안 지속하는 긴 일시중지가 있습니다.

Logcat에 다량의 GC가 있는 경우 힙 통계에서 증가하는 부분을 찾아보세요(위 예에서는 25MB/38MB 값). 이 값이 계속 증가하고 있고 작아질 것으로 보이지 않는다면 메모리 누수가 있을 수 있습니다. 또는 'Alloc'으로 인한 GC가 있는 경우 이미 힙 용량에 거의 도달해 있는 것이므로 곧 OOM 예외가 발생할 것을 예상할 수 있습니다.