로깅
로깅 (Logging)
Racket의 로깅 시스템은 *로거(logger)*와 *로그 수신자(log receiver)*라는 두 주체로 동작해요. 로거가 이벤트를 받아 관심 있는 쪽에 전달하고, 수신자가 그 이벤트를 비동기로 받습니다. 프로그램의 상태를 추적하거나 디버깅 신호를 남길 때 씁니다.
본문
*로거(logger)*는 관심 있는 쪽에 기록할 정보가 담긴 이벤트를 받아들여요. *로그 수신자(log receiver)*는 기록된 이벤트를 비동기로 받는 관심 있는 쪽을 나타내죠. 각 이벤트는 주제(topic)와 상세 수준(level of detail)을 가지며, 로그 수신자는 특정 주제 또는 모든 주제에 대해 일정 상세 수준(그보다 낮은 수준 포함)의 로깅 이벤트를 구독합니다. 상세 수준은 낮은 것부터 높은 것 순으로 'none, 'fatal, 'error, 'warning, 'info, 'debug예요. 'none 수준은 수신자를 지정할 때 쓰기 위한 것이며, 그 수준으로 기록된 메시지는 구독자에게 절대 보내지지 않습니다.
기록된 이벤트를 조직하기 위해, 로거는 기본 주제(default topic)와/또는 부모 로거를 가질 수 있어요. 로거에 보고된 모든 이벤트는 그 부모(있으면)로 전파되고, 이벤트 메시지에 주제가 이미 없으면 메시지 앞에 로거의 주제(있으면)가 붙습니다. 게다가 로거에서 부모로 전파되는 이벤트는 수준과 주제로 필터링될 수 있어요.
시작할 때 Racket은 핵심 런타임 시스템의 이벤트를 기록하는 초기 로거를 만듭니다. 예를 들어 가비지 컬렉션이 일어날 때마다 'debug 이벤트가 보고돼요(Garbage Collection 참조). 이 초기 로거에는 두 개의 로그 수신자도 만들어집니다. 하나는 프로세스의 원래 오류 출력 포트에 이벤트를 쓰고, 다른 하나는 시스템 로그에 이벤트를 씁니다. 각 경우에 쓰여지는 이벤트의 수준은 시스템마다 다르며, 기본값은 명령줄 플래그(Command Line 참조)나 환경 변수로 바꿀 수 있습니다.
PLTSTDERR환경 변수가 정의되어 있고 명령줄 플래그로 덮어쓰이지 않았다면, 원래 오류 포트로 이벤트를 전파하는 로그 수신자의 수준을 결정해요. 환경 변수의 값은 ‹level›:none,fatal,error,warning,info, 또는debug(낮은 상세부터 높은 상세 순)일 수 있으며, 해당 상세 수준 이하의 모든 이벤트가 인쇄됩니다. 초기 ‹level› 뒤에 값에는 공백으로 구분된 ‹level›@‹topic› 형태의 명세가 올 수 있는데, 이는 주제가 ‹topic›과 일치하는 이벤트를 주어진 ‹level› 이상에서만 인쇄합니다(‹topic›은 공백이나 @가 아닌 어떤 문자든 담을 수 있어요). 앞뒤 공백은 무시됩니다. 예를 들어"error debug@GC"값은'error수준 이상의 모든 이벤트를 인쇄하지만, 주제'GC의 이벤트는'debug수준 이상(모든 수준 포함)에서 인쇄합니다. 기본값은"error"입니다.PLTSTDOUT환경 변수가 정의되어 있고 명령줄 플래그로 덮어쓰이지 않았다면, 원래 출력 포트로 이벤트를 전파하는 로그 수신자의 수준을 결정해요. 가능한 값은PLTSTDERR와 같습니다. 기본값은"none"이에요.PLTSYSLOG환경 변수가 정의되어 있고 명령줄 플래그로 덮어쓰이지 않았다면, 시스템 로그로 이벤트를 전파하는 로그 수신자의 수준을 결정해요. 가능한 값은PLTSTDERR와 같습니다. 기본값은 Unix에서는"none", Windows와 Mac OS에서는"error"예요.
current-logger 파라미터는 log-warning 같은 형식이 사용하는 *현재 로거(current logger)*를 결정합니다. 시작할 때 이 파라미터의 초기값은 초기 로거예요. 런타임 시스템은 때때로 현재 로거로 이벤트를 보고합니다. 예를 들어 바이트코드 컴파일러는 평가하면 런타임 오류를 만들어 낼 표현식을 감지하면 'warning 이벤트를 보고하기도 합니다.
버전 6.6.0.2 (패키지 base)에서 변경: 6.6.0.2 이전에는 PLTSTDERR와 PLTSYSLOG의 파싱이 매우 엄격했어요. 앞뒤 공백이 금지되었고, 두 명세를 구분하는 공백 하나가 정확히 하나여야만 했죠.
버전 6.90.0.17에서 변경: PLTSTDOUT 추가됨.
로거 만들기 (Creating Loggers)
프로시저
(logger? v) → boolean?
v : any/c
v가 로거이면 #t, 그렇지 않으면 #f를 반환해요.
프로시저
(make-logger [topic parent propagate-level propagate-topic ...] ...) → logger?
topic : (or/c symbol? #f) = #f
parent : (or/c logger? #f) = #f
propagate-level : log-level/c = 'debug
propagate-topic : (or/c #f symbol?) = #f
선택적 주제와 부모를 가진 새 로거를 만들어요.
선택적 propagate-level과 propagate-topic 인자는 새 로거에서 parent(가 #f가 아닐 때)로 전파되는 이벤트를 제약하는데, make-log-receiver에서 로그 수신자에 대해 이벤트를 설명하는 것과 같은 방식이에요. 기본적으로 모든 이벤트가 parent로 전파됩니다.
버전 6.1.1.3 (패키지 base)에서 변경: 알림 콜백을 지정하는 선택적 인자가 제거되고, 전파할 이벤트의 propagate-level과 propagate-topic 제약이 추가됨.
프로시저
(logger-name logger) → (or/c symbol? #f)
logger : logger?
로거의 기본 주제(있으면)를 보고해요.
파라미터
(current-logger) → logger?
(current-logger logger) → void?
logger : logger?
현재 로거를 결정하는 파라미터예요.
문법
(define-logger id maybe-parent)
maybe-parent = | #:parent parent-expr
parent-expr : (or/c logger? #f)
log-id-fatal, log-id-error, log-id-warning, log-id-info, log-id-debug를 log-fatal, log-error, log-warning, log-info, log-debug 같은 형식으로 정의해요. define-logger 형식은 또한 id-logger도 정의하는데, 이것은 기본 주제가 'id인 로거로, parent-expr의 결과(가 #f를 만들지 않으면)의 자식이거나 parent-expr가 제공되지 않으면 (current-logger)의 자식이에요. log-id-fatal 등의 형식은 이 새 로거를 사용합니다. 새 로거는 define-logger가 평가될 때 만들어집니다.
버전 7.1.0.9 (패키지 base)에서 변경: #:parent 옵션 추가됨.
로깅 이벤트 (Logging Events)
프로시저
(log-message logger level [topic] message [data prefix-message?]) → void?
logger : logger?
level : log-level/c
topic : (or/c symbol? #f) = (logger-name logger)
message : string?
data : any/c = #f
prefix-message? : any/c = #t
logger에 이벤트를 보고해요. 그러면 로거가 level 이상의 이벤트에 관심 있는, logger나 그 조상에 붙어 있는 로그 수신자들에게 그 정보를 배포합니다. level이 'none이면 기록된 메시지는 어떤 수신자에게도 보내지지 않습니다.
로그 수신자는 topic을 기준으로 이벤트를 필터링할 수 있어요. 게다가 topic과 prefix-message?가 #f가 아니면, 수신자에게 보내기 전에 message 앞에 주제와 ": "가 붙습니다.
버전 6.0.1.10 (패키지 base)에서 변경: prefix-message? 인자 추가됨.
버전 7.2.0.7에서 변경: data 인자가 선택적이 됨.
버전 8.10.0.5에서 변경: 'none 처리를 일관되게 메시지를 억제하도록 변경.
프로시저
(log-level? logger level [topic]) → boolean?
logger : logger?
level : log-level/c
topic : (or/c symbol? #f) = #f
logger 또는 그 조상 중 하나에 붙은 어떤 로그 수신자가 topic에 대해 level 이벤트(또는 잠재적으로 더 낮은)에 관심이 있는지 보고해요. topic이 #f이면, 어떤 주제에 대해서든 level 이벤트에 관심 있는 로그 수신자가 있는지를 나타냅니다. level이 'none이면 결과는 항상 #f예요.
이 함수는 수신자가 그 정보에 관심이 없을 때 log-message용 이벤트를 만드는 작업을 피하는 데 써요. 다만 이 지름길은 log-fatal, log-error, log-warning, log-info, log-debug와 define-logger가 바인딩한 형식에 이미 내장되어 있으므로, 그 형식들과 함께 쓰면 안 됩니다.
가비지 컬렉션이 로그 수신자가 더 이상 접근 불가능하다고 판단하면(따라서 수신자가 받는 어떤 이벤트 정보도 결코 접근 가능해지지 않는다면) 이 함수의 결과가 바뀔 수 있어요.
버전 6.1.1.3 (패키지 base)에서 변경: topic 인자 추가됨.
버전 8.10.0.5에서 변경: 'none에 대한 결과가 일관되게 #f가 되도록 변경.
프로시저
(log-max-level logger [topic]) → (or/c log-level/c #f)
logger : logger?
topic : (or/c symbol? #f) = #f
log-level?와 비슷하지만, logger와 topic에 대한 log-level?이 #t를 반환하는, 로깅의 최대 상세 수준을 보고해요. logger와 topic에 대한 log-level?이 모든 수준에서 현재 #f를 반환하면 결과는 #f입니다.
버전 6.1.1.3 (패키지 base)에서 변경: topic 인자 추가됨.
프로시저
(log-all-levels logger) → (list/c (or/c #f log-level/c) (or/c #f symbol?) ... ...)
logger : logger?
가능한 모든 인턴(intern) 심볼에 대한 log-max-level의 가능한 결과들을 요약해요. 결과 리스트는 심볼과 #f의 수열을 담는데, 첫 번째, 세 번째 등 리스트 요소는 수준에 대응하고 두 번째, 네 번째 등 리스트 요소는 해당 주제를 나타내요. 수준은 log-max-level이 그 주제에 대해 만들어 낼 결과이며, #f 주제(결과 리스트에 항상 존재)에 대한 수준은 리스트에 나타나지 않는 어떤 인턴 심볼 주제에 대한 결과를 나타냅니다.
이 결과는 make-log-receiver(로거 인자 뒤에)에 인자 수열로 제공하기 적합하며, logger에서 현재 수신자를 가진 이벤트에 대한 새 수신자를 만들 수 있어요.
버전 6.1.1.4 (패키지 base)에서 추가됨.
프로시저
(log-level-evt logger) → evt?
logger : logger?
log-level?, log-max-level, 또는 log-all-levels의 결과가 log-level-evt를 호출하기 전과 달라질 수 있을 때 동기화 준비가 되는, 동기화 가능한 이벤트를 만들어요. 이벤트의 동기화 결과는 이벤트 자신입니다.
이벤트가 보고하는 조건은 보수적인 근사예요. log-level?, log-max-level, log-all-levels의 결과가 변하지 않아도 이벤트는 동기화 준비 상태가 될 수 있습니다. 그럼에도 log-level-evt가 만든 이벤트는 로그 수신자 생성에 의해 촉발되므로 좀처럼 준비 상태가 되지 않을 것이라고 기대합니다.
버전 6.1.1.4 (패키지 base)에서 추가됨.
문법
(log-fatal string-expr)
(log-fatal format-string-expr v ...)
(log-error string-expr)
(log-error format-string-expr v ...)
(log-warning string-expr)
(log-warning format-string-expr v ...)
(log-info string-expr)
(log-info format-string-expr v ...)
(log-debug string-expr)
(log-debug format-string-expr v ...)
현재 로거로 이벤트를 기록해요. string-expr 또는 (format format-string-expr v ...)는 로거에 그 이벤트에 관심 있는 수신자가 있을 때만 평가합니다. 게다가 현재 연속의 연속 마크(continuation marks)가 메시지 문자열과 함께 로거로 보내집니다.
이 형식들은 현재 로거를 쓰기에 편리하지만, 라이브러리는 일반적으로 특정 주제용 로거를 써야 해요. 보통 define-logger가 만든 비슷한 편의 형식을 통해서 말이죠.
각 log-level에 대해,
(log-level string-expr)
은 다음과 동등합니다:
(let ([l (current-logger)])
(when (log-level? l 'level)
(log-message l 'level string-expr
(current-continuation-marks))))
그리고
(log-level format-string-expr v ...)
은 다음과 동등합니다:
(log-level (format format-string-expr v ...))
기록된 이벤트 받기 (Receiving Logged Events)
프로시저
(log-receiver? v) → boolean?
v : any/c
v가 로그 수신자이면 #t, 그렇지 않으면 #f를 반환해요.
프로시저
(make-log-receiver logger level [topic ...] ...) → log-receiver?
logger : logger?
level : log-level/c
topic : (or/c #f symbol?) = #f
logger와 그 자손에 보고된 level 이하 상세의 이벤트를 받는 로그 수신자를 만들어요. 단 topic이 #f이거나 이벤트의 주제가 topic과 일치해야 합니다.
로그 수신자는 동기화 가능한 이벤트예요. 로깅 이벤트를 받으면 동기화 준비 상태가 되므로, sync로 기록된 이벤트를 받습니다. 로그 수신자의 동기화 결과는 네 값을 담은 불변 벡터예요. 이벤트의 수준(심볼), 이벤트 메시지용 불변 문자열, 이벤트가 기록될 때 log-message의 마지막 인자로 제공된 임의 값, 그리고 이벤트 주제용 심볼 또는 #f입니다.
level과 topic의 여러 쌍을 제공해서 서로 다른 topic마다 서로 다른 구체적인 level을 나타낼 수 있어요(topic은 마지막으로 주어진 level에 대해서만 #f로 기본 설정됩니다). #f topic에 대한 level은 다른 제공된 topic과 일치하지 않는 주제를 가진 이벤트에만 적용됩니다. 같은 topic이 여러 번 제공되면 인자 리스트에서 마지막 인스턴스와 함께 제공된 level이 우선합니다.
추가 로깅 함수 (Additional Logging Functions)
( require racket/logging ) — 패키지: base
이 절에서 문서화한 바인딩은 racket/logging 라이브러리가 제공하지, racket/base나 racket이 제공하지 않아요.
프로시저
(log-level/c v) → boolean?
v : any/c
v가 유효한 로깅 수준('none, 'fatal, 'error, 'warning, 'info, 또는 'debug)이면 #t, 그렇지 않으면 #f를 반환해요.
버전 6.3 (패키지 base)에서 추가됨.
프로시저
(with-intercepted-logging interceptor proc [#:logger logger] level [topic ...] ...) → any
interceptor : (-> (vector/c log-level/c string? any/c (or/c symbol? #f)) any)
proc : (-> any)
logger : logger? = #f
level : log-level/c
topic : (or/c #f symbol?) = #f
proc를 실행하면서, proc의 실행이 current-logger로 주어진 수준과 주제에서 방출하는 어떤 로그 이벤트에 대해 interceptor를 호출해요. #:logger가 지정되면 그 로거로 보내진 이벤트를 가로채고, 그렇지 않으면 현재 로거의 새 자식 로거를 사용합니다. proc가 반환하는 것을 그대로 반환해요.
예시:
(let ([warning-counter 0])
(with-intercepted-logging
(lambda (l)
(when (eq? (vector-ref l 0)
'warning)
(set! warning-counter (add1 warning-counter))))
(lambda ()
(log-warning "Warning!")
(log-warning "Warning again!")
(+ 2 2))
'warning)
warning-counter)
→ 2
버전 6.3 (패키지 base)에서 추가됨.
버전 6.7.0.1에서 변경: #:logger 인자 추가됨.
프로시저
(with-logging-to-port port proc [#:logger logger] level [topic ...] ...) → any
port : output-port?
proc : (-> any)
logger : logger? = #f
level : log-level/c
topic : (or/c #f symbol?) = #f
proc를 실행하면서, proc의 실행이 current-logger로 주어진 수준과 주제에서 방출하는 어떤 로깅을 port로 출력해요. #:logger가 지정되면 그 로거로 보내진 이벤트를 가로채고, 그렇지 않으면 현재 로거의 새 자식 로거를 사용합니다. proc가 반환하는 것을 그대로 반환해요.
예시:
(let ([my-log (open-output-string)])
(with-logging-to-port my-log
(lambda ()
(log-warning "Warning World!")
(+ 2 2))
'warning)
(get-output-string my-log))
→ "Warning World!\n"
버전 6.3 (패키지 base)에서 추가됨.
버전 6.7.0.1에서 변경: #:logger 인자 추가됨.