디버깅을 위해 로그를 사용하는 것은 컴퓨팅 자체만큼이나 오래되었다. 로그는 시스템의 내부 작동뿐 아니라, 시간소인이 표시된 로그 내에서 시간순으로 된 메시지를 통해 시스템 내부에서 이루어지는 활동의 타이밍과 관계도 이해하는 데 유용하다.
이 기사에서는 로그 정보를 구성 및 수집하는 데 사용되는 API(Application Programming Interface)를 보고 커널의 로깅부터 탐색해본다(전체
프레임워크와 컴포넌트의 개요는 그림 1 참조). 그런 다음, 커널의 로그 데이터가 사용자 공간으로 이동하는 과정을 살펴본다. 마지막으로, 커널 기반 로그 데이터의
대상을 탐색한다. 즉, rsyslog로 사용자 공간의 로그 관리 프레임워크를 살펴본다.
그림 1. 커널 로깅 에코시스템 및 주요 컴포넌트
커널 내에서의 로깅은 사용자 공간의 printf(print formatted)와 유사한 점이 많은 printk
함수를 사용하여 수행된다. printf 명령은 여러 언어에서 오랜 역사를 가지고 있는데, 가장 최근의 언어로는 C
언어에서 이 명령이 사용되지만 훨씬 더 오래 전으로 돌아가 1950년대와 1960년대에는 Fortran(PRINT 및
FORMAT 문), BCPL(writf 함수, BCPL 다음에 C가 등장함) 및
ALGOL 68 언어(printf, putf)에서도 사용되었다.
printk(print kernel)는 커널 내에서 printf 함수와 거의 똑같은 형식을 사용하여
버퍼에 포맷된 메시지를 쓰는 데 사용된다. ./linux/include/linux/kernel.h에서는 printk의 형식을, ./linux/kernel/printk.c에서는
이 형식의 구현을 찾을 수 있다.
int printk( const char * fmt, ... ); |
이 형식은 (printf와 똑같이) 텍스트와 형식을 정의하는 데 문자열이 사용됨을 나타내고, 이 형식에는
(줄임표[...]로 식별되는) 인수의 변수 세트가 수반된다.
printk를 사용할 때 볼 수 있는 첫 번째 차이점들 중 하나는 이 함수가 함수의 성질보다는 프로토콜의 성질을 더
많이 띤다는 점이다. 이런 특징에 따라, C 언어의 불명료한 측면을 이용해 메시지 레벨 또는 우선순위의 스펙을 단순화한다. 이 커널을
사용하면 각 메시지를 로그 레벨(특정 메시지의 심각도를 정의하는 여덟 가지 레벨 중 하나)로 분류할 수 있다. 이런 레벨을 통해 시스템을 사용할 없게
되었는지(비상 메시지), 중대한 조건이 발생했는지(중대 메시지) 또는 메시지가 단순히 정보를 제공하기 위한 것인지 여부를 전달한다. 중대 메시지에 대한 다음
예제에서 보여주는 바와 같이, 커널 코드는 단순히 로그 레벨을 메시지의 첫 번째 인수로 정의한다.
printk( KERN_CRIT "Error code %08x.\n", val ); |
레벨(KERN_CRIT)과 형식 문자열을 구분하는 쉼표(,)가 없으므로, 첫 번째 인수는 전혀
인수가 아니라는 점에 유의한다. KERN_CRIT는 문자열 자체에 지나지 않는다(사실, 이것은 "<2>"
문자열을 나타냄. 전체 로그 레벨 목록은 표 1 참조). C에서는 위 두 문자열을 프리프로세서의 일부로서 문자열 리터럴
연결(string literal concatenation)이라는 기능에서 자동으로 결합한다. 그 결과는 로그 레벨 및 사용자 지정 형식 문자열을 단일 문자열로 통합하는
단일 문자열이다. 호출자가 printk 내에서 로그 레벨을 제공하지 않는 경우, KERN_WARNING의 기본값이
자동으로 사용된다(즉, KERN_WARNING과 더 높은 우선순위의 로그 메시지만 로그에 기록됨).
표 1. 로그 레벨, 기호 및 사용법
| 기호 | 문자열 | 사용법 |
|---|---|---|
KERN_EMERG | <0> | 비상 메시지(파손에 앞서 발생함) |
KERN_ALERT | <1> | 즉각적인 주의를 요하는 오류 |
KERN_CRIT | <2> | 중대 오류(하드웨어 또는 소프트웨어) |
KERN_ERR | <3> | 오류 조건(드라이버의 공통 사항) |
KERN_WARNING | <4> | 경고 조건(오류로 이어질 수 있음) |
KERN_NOTICE | <5> | 오류는 아니지만 중요한 조건임 |
KERN_INFO | <6> | 정보 메시지 |
KERN_DEBUG | <7> | 디버그 메시지용으로만 사용됨 |
KERN_DEFAULT | <d> | 기본 커널 로깅 레벨 |
KERN_CONT | <c> | 로그 행의 연속(새 시간소인의 추가를 피할 것) |
커널의 임의 컨텍스트에서 printk가 호출될 수 있다. va_start를 사용하여 가변 길이 인수를
해결한 후 (같은 소스 파일에서) vprintk를 호출하는 printk 함수의 ./linux/kernel/printk.c에서 호출이
시작된다.
vprintk 함수는 많은 관리 레벨 검사(순환 찾기)를 수행한 다음, 로그 버퍼(__log_buf)에
대한 잠금을 건다. 그 다음, 수신되는 문자열에 로그 레벨 문자열이 있는지 검사하고, 로그 레벨 문자열을 찾은 경우 로그 레벨이 적절히 설정된다. 마지막으로,
vprintk는 (cpu_clock 함수를 사용하여) 현재 시간을 가져오고, sprintf(표준
라이브러리 버전이 아니라 ./linux/lib/vsprintf.c에 구현된 내부 커널 버전)를 사용하여 이를 문자열로 변환한다. 그런 다음, 링의 바운드(emit_log_char)를
관리하는 특수 함수를 사용하여 printk로 전달된 문자열이 커널 로그 버퍼로 복사된다. 이 함수의 끝에서는 콘솔 세마포어의
이유 없는 획득과 해제가 수행되어 다음 로그 메시지를 콘솔로 내보낸다(release_console_sem 내에서 수행됨). 커널 링 버퍼의 크기는
원래 4KB였지만, 최근 커널에서는 16KB(아키텍처에 따라서는 최대 1MB)가 되었다.
지금까지 로그 메시지를 커널 링 버퍼로 삽입할 때 사용되는 API를 살펴보았다. 이제, 커널에서 호스트로 데이터 마이그레이션할 때 사용되는 메소드를 살펴보자.
로그 버퍼에 대한 액세스 권한은 다목적 syslog 시스템 호출을 통해 코어에서 제공된다. 이 단일 호출에서는
모두 사용자 공간에서 수행될 수 있는 다양한 동작을 구현하지만, 루트 권한이 없는 사용자에 대해서는 한 가지 동작만 구현한다. syslog
시스템 호출을 위한 프로토타입은 ./linux/include/linux/syslog.h에서 정의되고, 그 구현은 ./linux/kernel/printk.c에 있다.
syslog 호출은 커널의 로그 메시지 링 버퍼에 대한 입력/출력(I/O) 및 제어 인터페이스의 역할을 한다. 애플리케이션은
syslog 호출에서 로그 메시지(일부, 전체 또는 새 메시지만)를 읽을 뿐 아니라, 링 버퍼의 동작을 제어(내용 지우기, 로그에 기록할
메시지의 레벨 설정, 콘솔 사용 또는 사용 안함 등)할 수 있다.
그림 2는 논의한 몇 가지 주요 컴포넌트를 이용한 로깅 스택을 도해로 나타낸 것이다.
그림 2. 주요 컴포넌트를 식별하는 커널 로깅 스택
syslog 호출(./linux/kernel/printk.c의 커널 내에서는
do_syslog라고 함)은 커널 링 버퍼를 읽고 제어하는 기능을 제공하는 비교적 작은 함수이다. glibc 2.0에서는
다양한 호출과 애플리케이션을 지칭하는 syslog라는 용어를 과다하게 사용하기 때문에 이 함수를 klogctl이라고
부른다. (사용자 공간에서) syslog 및 klogctl에 대한 프로토타입 함수는 다음으로 정의된다.
int syslog( int type, char *bufp, int len ); int klogctl( int type, char *bufp, int len ); |
type 인수는 수행할 명령을 알려주고 인수의 길이를 포함한 선택적 버퍼와 연관된다. (링 버퍼 지우기와 같은) 일부
명령에서는 bufp 및 len 인수를 무시한다. 첫 두 가지 명령 유형은 커널 내에서 아무런 작업도 수행하지
않지만, 나머지는 로그 메시지를 읽거나 로깅과 관련된 부분을 제어하는 데 사용된다. 로그 메시지를 읽을 때는 세 가지 명령이 사용된다.
SYSLOG_ACTION_READ 명령은 로그 메시지를 사용할 수 있을 때까지 메시지를 차단한 후 제공되는 버퍼에 리턴하는 데
사용된다. 이 명령은 메시지를 소비한다(이후에 이 메시지를 호출할 때 이전 메시지가 표시되지 않을 것임). SYSLOG_ACTION_READ_ALL
명령은 로그에서 마지막 n 문자를 읽는다(여기서, n은 klogctl로 전달되는 'len' 매개변수로
정의됨). SYSLOG_ACTION_READ_CLEAR 명령은 SYSLOG_ACTION_READ_ALL 작업을 수행하고, 뒤이어
SYSLOG_ACTION_CLEAR 명령이 실행되어 링 버퍼를 지운다. SYSLOG_ACTION_CONSOLE ON 및 OFF를 이용해
로그 레벨에서 콘솔에 대한 로그 메시지를 사용하거나 사용 안하도록 조작하며, 여기서 호출자는 SYSLOG_CONSOLE_LEVEL을 통해
콘솔에서 허용할 로그 메시지의 레벨을 정의할 수 있다. 마지막으로, SYSLOG_ACTION_SIZE_BUFFER는 커널 링 버퍼의 크기를 리턴하고,
SYSLOG_ACTION_SIZE_UNREAD는 커널 링 버퍼에서 현재 읽을 수 있는 문자 수를 리턴한다. SYSLOG 명령의
전체 목록은 표 2에 표시되어 있다.
표 2. syslog/klogctl 시스템 호출로 구현된 명령
| 명령/opcode | 용도 |
|---|---|
SYSLOG_ACTION_CLOSE (0) | (구현되지 않은) 로그 닫기 |
SYSLOG_ACTION_OPEN (1) | 로그 열기(구현되지 않음) |
SYSLOG_ACTION_READ (2) | 로그에서 읽기 |
SYSLOG_ACTION_READ_ALL (3) | 로그에서 모든 메시지 읽기(비소거식) |
SYSLOG_ACTION_READ_CLEAR (4) | 로그에서 모든 메시지 읽기 및 지우기 |
SYSLOG_ACTION_CLEAR (5) | 링 버퍼 지우기 |
SYSLOG_ACTION_CONSOLE_OFF (6) | 콘솔에 대해 printk 사용 안함 |
SYSLOG_ACTION_CONSOLE_ON (7) | 콘솔에 대해 printk 사용 |
SYSLOG_ACTION_CONSOLE_LEVEL (8) | 콘솔에서 수락하는 메시지의 레벨 설정 |
SYSLOG_ACTION_SIZE_UNREAD (9) | 로그에서 읽지 않은 문자 수 리턴 |
SYSLOG_ACTION_SIZE_BUFFER (10) | 커널 링 버퍼의 크기 리턴 |
syslog/klogctl 계층 위에서 구현되는 kmsg proc 파일 시스템은 (./linux/fs/proc/kmsg.c에
구현되는) I/O 경로로서, 커널 버퍼에서 로그 메시지를 읽기 위한 2진 인터페이스를 제공한다. 일반적으로 메시지를 사용하고 (구성을 바탕으로) 알맞은
로그 파일로 경로 지정하기 위해 메시지를 rsyslog로 전달하는 디먼(klogd 또는
rsyslogd)에서 이것을 읽는다.
/proc/kmsg 파일은 내부 do_syslog 연산과 동등한 소수의 파일 작업을 구현한다.
내부적으로 open 호출은 SYSLOG_ACTION_OPEN과, release
호출은 SYSLOG_ACTION_CLOSE와 관련되며, 그 각각은 NOP(No Operation Performed)로 구현된다. 폴 연산에서는 파일에 대한 작업 대기를
허용한 다음, SYSLOG_ACTION_SIZE_UNREAD를 호출하여 읽을 수 있는 문자 수를 식별한다. 마지막으로, read
연산은 SYSLOG_ACTION_READ로 맵핑하여 사용 가능한 로그 메시지를 사용한다. /proc/kmsg 파일이 사용자에게 유용한 것은 아니다.
단일 디먼에서 이 파일을 사용하여 로그 메시지를 가져와서 /var 공간의 필수 로그 파일로 이 메시지를 경로 지정한다.
사용자 공간에는 커널 로깅을 읽고 관리하기 위한 액세스 지점이 많이 있다. (/proc 파일 시스템 구성 요소와 같은) 하위 레벨 인터페이스에서 시작한 다음, 상위 레벨 애플리케이션으로 확장해보자.
/proc 파일 시스템은 로그 메시지(kmsg) 액세스를 위해 단지 2진 인터페이스 이상의 것을 내보낸다. 또한,
syslog/klogctl을 통해 논의한 것과 관련된 구성 요소와 무관한 구성 요소도 많이 제공한다.
목록 1은 이런 매개변수에 대한 내용을 나타낸 것이다.
목록 1. /proc에 있는 printk 구성 매개변수의 탐색
mtj@ubuntu:~$ cat /proc/sys/kernel/printk 4 4 1 7 mtj@ubuntu:~$ cat /proc/sys/kernel/printk_delay 0 mtj@ubuntu:~$ cat /proc/sys/kernel/printk_ratelimit 5 mtj@ubuntu:~$ cat /proc/sys/kernel/printk_ratelimit_burst 10 |
목록 1에서 첫 번째 항목은 printk API에서 현재 사용되는 로그 레벨을 정의한다. 이들 로그 레벨은 콘솔 로그 레벨,
기본 메시지 로그 레벨, 최소 콘솔 로그 레벨 및 기본 콘솔 로그 레벨을 나타낸다. (일부 시나리오에서 가독성을 더하기 위해)
printk_delay 값은 printk 메시지 간에 지연할 시간을 밀리초 단위로 나타낸다. 여기서는 이 값이 0으로
설정되어 있고, /proc를 통해 이 값을 설정할 수 없다. printk_ratelimit는 메시지 간에 허용되는 최소 시간의 길이를 정의한다(현재,
5초마다 몇 개의 커널 메시지로 정의되어 있음). 메시지 수는 printk_ratelimit_burst로 정의된다(현재는 10으로 정의되어 있음).
이는 특히 많은 정보를 담은 커널이 있지만 직렬 포트를 통하는 경우와 같이 대역폭이 제한된 콘솔 장치가 있는 경우에 유용하다. 커널 내에서
비율 제한은 호출자가 제어하고 printk 내에서 구현되지 않는다. 비율 제한을 원하는 printk 사용자가 printk_ratelimit 함수를 호출한다.
dmesg 명령을 사용하여 커널 링 버퍼를 인쇄 및 제어할 수도 있다. 이 명령에서는 klogctl
시스템 호출을 사용하여 커널 링 버퍼를 읽고 이를 표준 출력(stdout)으로 내보낸다. 이 명령을 사용하여 커널 링 버퍼를 지우고(-c 옵션
사용), 콘솔에 대한 로깅 레벨을 설정하고(-n 옵션), 커널 로그 메시지를 읽는 데 사용되는 버퍼의 크기를 정의할 수도
있다(-s 옵션). 버퍼 크기가 지정되지 않은 경우 dmesg는 klogctl에 대해
SYSLOG_ACTION_SIZE_BUFFER 연산을 사용하여 적당한 버퍼 크기를 식별한다.
마지막으로, 모든 로깅 애플리케이션의 모태가 되는 것은 Linux® 및 BSD(Berkeley Software Distribution)를 포함한 주요 운영 체제에서
구현되는 표준화된 로깅 프레임워크인 syslog이다. syslog에는 (컴포넌트를 송신자, 릴레이 및 콜렉터로
나누는) 다양한 전송 프로토콜을 통해 이벤트 알림 메시지를 전달하는 데 사용되는 고유의 프로토콜이 있다. 많은 경우, 이 세 가지 모두 단일 호스트에서
구현된다. syslog의 다양하고 흥미로운 기능 외에도, syslog는 로깅 정보의 저장 위치뿐 아니라 수집 및 필터링 방법까지 지정한다.
syslog는 수많은 변화를 거치면서 발전해왔다. 아마 syslog,
klog 또는 sysklogd에 대해 들어봤을 것이다.
더욱 최근에 발표된 Ubuntu 배포 버전에서는 안정적이고 확장된 syslogd를 가리키는
rsyslog(원래의 syslog를 기반으로 함)라는 syslog의 새 버전이 사용된다.
rsyslogd 디먼은 /etc/rsyslog.conf에 있는 구성 파일을 통해 /proc 파일 시스템 kmsg
인터페이스를 이해하고 그것을 사용하여 추출 로깅 메시지를 추출한다. 내부적으로는 모든 로그 레벨이 /proc/kmsg를 통해 작성되므로, 어떤 로그 레벨을 전송할지
정의하는 대신 해당 작업이 rsyslog 자체에 남겨진다.
그런 다음 커널 로그 메시지는 (다른 구성 파일 중에서도) /var/log/kern.log에 저장된다. /var/log에서는 일반 메시지와 시스템 관련
호출(/var/log/messages), 시스템 부트 로그(/var/log/boot.log), 인증 로그(/var/log/auth.log) 등을 포함한 수많은 로그 파일을 찾을 수 있을 것이다.
검토용으로 이들 로그를 사용할 수 있지만, 자동 감사 및 포렌식을 위해 사용할 수도 있다. 문제점 해결이나 보안 규정 준수를 위한 다양한 로그 파일 분석기가 있으며, 이들 분석기는 패턴 인식이나 상관관계 분석(여러 시스템 간의 분석까지 포함하여)과 같은 기법을 사용하여 문제점을 자동으로 찾는다.
본 기사에서는 커널에서의 커널 로그 메시지 작성에서 커널의 링 버퍼 내에 이런 메시지를 저장하거나
syslog/klogctl을 통해 메시지를 사용자 공간으로 전송하거나 rsyslog
로깅 프레임워크를 통해 /var/log 하위 트리의 최종 위치로 경로 지정하는 것까지, 다양한 커널 로깅 및 애플리케이션에 대해 두루 살펴보았다.
Linux에서는 (커널과 외부에서 모두) 로깅을 위한 다양하고 유연한 프레임워크를 제공한다.
교육
-
매뉴얼 페이지와 wiki
사이트에서
rsyslog(syslog와klog를 대체하는 새로운 시스템 로깅 프레임워크)에 대한 내용을 읽어보자. - Ubuntu에서는
rsyslog에 초점을 맞춘 로깅에 대한 유용한 페이지를 유지 관리한다. 이 백서에서는 구성 및 복잡한 다중 호스트 로깅 네트워크를 포함하여, 로깅과rsyslog에 대한 자세한 개론을 소개한다. -
syslog(2)매뉴얼 페이지에서syslog(2)와 다양한 옵션 및 구성에 대해 훌륭하게 설명한 개론을 읽어볼 수 있다. -
printk함수는 문자열 리터럴 연결이라는C언어의 기능에 의존한다. Wikipedia의C언어 페이지에 이 기법이 소개되어 있다. -
syslog프로토콜은 실제로는 Internet Engineering Task Force의 RFC(Request for Comments) 프로세스를 통해 표준화된 프로토콜이다. syslog RFC 5424에 관한 내용을 읽어보자. - 로그 파일 분석은 시스템 학습 및 모니터링 도구에서 관심의 초점이 모아진 주제이다. Wikipedia의
일반 로그 분석(general log analysis)과 SourceForge에서 Swatch라고
하는 활성 로그 파일 모니터링 도구에 대해 자세히 알아보자.
-
Linux에서는
수백 개의 기술자료 목록과 함께, Linux 개발자와 관리자를 위한
다양한 다운로드, 토론 포럼 및 다른 참고자료를 찾을 수 있다.
-
developerWorks 기술 행사 및 웹 캐스트를 통해 다양한 IBM 제품 및 IT 산업 주제에 대한 최신 정보를 얻을 수 있다.
-
무료 developerWorks Live!
briefing을 통해 최신 IBM 제품 및 도구에 대한 정보뿐만 아니라 IT 업계의 최신 경향까지도 빠르게 확인할 수 있다.
-
developerWorks on-demand demos에서는 입문자를 위한 제품 설치 및 설정부터 숙련된 개발자를 위한 고급 기능까지 망라된 다양한 데모를 제공한다.
-
Twitter의 developerWorks를 팔로우(follow)하거나
developerWorks에 대한 Linux 트윗(tweet)의 피드를 구독하자.
제품 및 기술 얻기
-
자신에게 가장한 적합한 방법으로 IBM
제품을 평가해 보자. 시험판 제품을 다운로드하거나, 온라인으로 제품을 사용해 보거나, 클라우드 환경에서 제품을 사용하거나,
SOA Sandbox에서
SOA(Service Oriented Architecture)를 효과적으로 구현하는 방법을 배울 수 있다.
토론
-
developerWorks community에 참여한다. 개발자가 운영하고 있는 블로그, 포럼, 그룹 및 위키를 살펴보면서 다른 developerWorks 사용자와 의견을 나눌 수 있다.