#lang scheme/base
(require scheme/async-channel
srfi/13
srfi/19
"base.ss"
"number.ss"
"parameter.ss"
"time.ss")
(define (default-log-formatter level timestamp args)
(string-join (list* (format-log-level level)
(format-log-timestamp timestamp)
(map (cut format "~s" <>) args))
","))
(define (format-log-level level)
(case level
[(fatal) "F"]
[(error) "E"]
[(warning) "W"]
[(info) "I"]
[(debug) "D"]))
(define (format-log-timestamp timestamp)
(cond [(time-utc? timestamp) (date->string (time-utc->date timestamp) "~Y-~m-~d ~H:~M:~S")]
[(time-tai? timestamp) (date->string (time-utc->date timestamp) "~Y-~m-~d ~H:~M:~S")]
[else (raise-type-error 'format-log-timestamp "(U time-utc time-tai)" timestamp)]))
(define (default-log-handler level message marks)
(display message (current-output-port))
(newline))
(define current-application-logger
(make-parameter
(make-logger)
(make-guard logger? "logger")))
(define current-log-formatter
(make-parameter default-log-formatter (make-guard procedure? "(any ... -> string)")))
(define (start-log-output level [handler default-log-handler])
(define receive-evt
(make-log-receiver (current-application-logger) level))
(define stop-evt
(make-async-channel))
(define (print-message)
(match (sync receive-evt stop-evt)
[(vector level message marks)
(handler level message marks)
(print-message)]
[#f (void)]))
(thread print-message)
(cut async-channel-put stop-evt #f))
(define-syntax-rule (define-log-form id level)
(define-syntax-rule (id arg (... ...))
(let ([timestamp (current-time time-utc)]
[logger (current-application-logger)])
(when (log-level? logger level)
(log-message logger
level
((current-log-formatter) level timestamp (list arg (... ...)))
(current-continuation-marks)))
timestamp)))
(define-log-form log-fatal* 'fatal)
(define-log-form log-error* 'error)
(define-log-form log-warning* 'warning)
(define-log-form log-info* 'info)
(define-log-form log-debug* 'debug)
(define log-level/c
(one-of/c 'fatal 'error 'warning 'info 'debug))
(provide log-fatal*
log-error*
log-warning*
log-info*
log-debug*)
(provide/contract
[current-application-logger (parameter/c logger?)]
[current-log-formatter (parameter/c (-> log-level/c (or/c time-utc? time-tai?) list? string?))]
[format-log-level (-> log-level/c string?)]
[format-log-timestamp (-> (or/c time-utc? time-tai?) string?)]
[start-log-output (->* (log-level/c) ((-> log-level/c string? continuation-mark-set? void?)) (-> void?))])