ANDROID 16 · LIBLOG · LOGDW · LOGDR

Log.i에서 logcat까지: 기록을 요청했다고 logd가 받은 것은 아닙니다

Java 호출, liblog 송신, logd 버퍼와 logcat 읽기 경로를 Android 16의 같은 태그로 나누어 봅니다.

같은 태그와 같은 오버로드부터 고정합니다

여기서는 android-16.0.0_r1의 기본 logd-backed 구성을 기준으로 Log.i(tag, msg) 경로를 읽습니다. 이 오버로드는 LOG_ID_MAIN과 INFO를 println_native에 넘기며 JNI는 __android_log_buf_write를 호출합니다. Throwable을 받는 다른 오버로드나 모든 종류의 Android 로그를 이 호출열 하나로 설명하지 않습니다.

liblog에는 교체 가능한 logger 함수가 있고, 이 태그의 logd logger에도 ro.log.file_logger.path에 따른 파일 출력 분기가 있습니다. 따라서 “liblog 호출은 언제나 logdw로 간다”는 표현은 적용 범위를 지나치게 넓힙니다.

Java Log 오버로드

JNI의 liblog 호출

양수가 반환됐다고 logd가 받았다는 뜻은 아닙니다

__android_log_buf_write는 로그 허용 여부를 검사한 뒤 void 함수인 __android_log_write_log_message를 호출하고 1을 반환합니다. 이 값은 logd가 저장했고 logcat이 읽었다는 왕복 확인이 아닙니다. logger 내부의 송신 실패를 이 반환값만으로 알 수 있다고 설명하면 안 됩니다.

기본 송신 경로의 LogdWrite는 logdw 소켓으로 헤더와 payload를 보냅니다. 구현에는 payload 상한과 EAGAIN에 따른 드롭 처리가 있으므로 큰 메시지가 모두 보존되거나 부하 중에도 빠짐없이 기록된다는 보장은 없습니다.

liblog의 필터·반환·logger 선택

logdw 송신과 드롭 처리

logdw는 쓰기, logdr는 읽기 경로입니다

logd의 LogListener는 수신 메시지와 함께 전달된 송신자 credentials에서 UID·PID를 확인하고 로그 ID 등의 조건을 검사해 버퍼에 넘깁니다. 이 UID·PID를 logcat 독자의 신원과 혼동하지 않습니다. logcat 쪽의 liblog reader는 별도의 logdr 소켓으로 로그 선택과 읽기를 요청합니다.

분기와 관측 지점을 나눈 자체 개념도
  1. Log.i → JNI → liblog logger → logdw 송신

  2. logd LogListener → 송신자·로그 조건 검사 → 버퍼

  3. logcat → liblog reader → logdr 읽기 요청

logd main에는 선택 가능한 버퍼 구현이 있으며 reader는 요청한 로그 마스크와 접근 권한을 따로 처리합니다. 모든 버전이 하나의 고정 자료구조를 사용하거나 일반 앱이 모든 버퍼를 읽을 수 있다고 일반화하지 않습니다.

LogListener의 송신자 확인

LogReader의 읽기 조건

logdr 연결과 읽기 요청

출력이 없다고 실행되지 않았다고 볼 수는 없습니다

태그·우선순위 필터, 선택한 버퍼, reader 권한, 메시지 크기와 기록 당시 부하를 먼저 확인합니다. 로그 버퍼를 영구 보관소로 취급하지 않습니다. 토큰·개인정보를 추가 출력해 증거를 만들려는 방식도 피합니다.

재현 기록에는 빌드 태그, 사용한 API와 오버로드, PID·TID와 시각, reader 옵션과 권한을 적습니다. 이 글의 개념도는 고정 소스에서 재구성했으며 특정 기기에서 로그를 실행·측정한 결과나 원본 이미지 검증 결과가 아닙니다.

logd의 버퍼·리스너 구성

로그 접근과 개인정보 보호