로깅

로깅 (Logging)

Racket의 로깅 시스템은 *로거(logger)*와 *로그 수신자(log receiver)*라는 두 주체로 동작해요. 로거가 이벤트를 받아 관심 있는 쪽에 전달하고, 수신자가 그 이벤트를 비동기로 받습니다. 프로그램의 상태를 추적하거나 디버깅 신호를 남길 때 씁니다.

출처: Racket Reference - Logging

본문

*로거(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 이전에는 PLTSTDERRPLTSYSLOG의 파싱이 매우 엄격했어요. 앞뒤 공백이 금지되었고, 두 명세를 구분하는 공백 하나가 정확히 하나여야만 했죠.

버전 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-levelpropagate-topic 인자는 새 로거에서 parent(가 #f가 아닐 때)로 전파되는 이벤트를 제약하는데, make-log-receiver에서 로그 수신자에 대해 이벤트를 설명하는 것과 같은 방식이에요. 기본적으로 모든 이벤트가 parent로 전파됩니다.

버전 6.1.1.3 (패키지 base)에서 변경: 알림 콜백을 지정하는 선택적 인자가 제거되고, 전파할 이벤트의 propagate-levelpropagate-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-debuglog-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을 기준으로 이벤트를 필터링할 수 있어요. 게다가 topicprefix-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-debugdefine-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?와 비슷하지만, loggertopic에 대한 log-level?#t를 반환하는, 로깅의 최대 상세 수준을 보고해요. loggertopic에 대한 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입니다.

leveltopic의 여러 쌍을 제공해서 서로 다른 topic마다 서로 다른 구체적인 level을 나타낼 수 있어요(topic은 마지막으로 주어진 level에 대해서만 #f로 기본 설정됩니다). #f topic에 대한 level은 다른 제공된 topic과 일치하지 않는 주제를 가진 이벤트에만 적용됩니다. 같은 topic이 여러 번 제공되면 인자 리스트에서 마지막 인스턴스와 함께 제공된 level이 우선합니다.

추가 로깅 함수 (Additional Logging Functions)

( require racket/logging ) — 패키지: base

이 절에서 문서화한 바인딩은 racket/logging 라이브러리가 제공하지, racket/baseracket이 제공하지 않아요.

프로시저

(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 인자 추가됨.

더 알아보기