diff --git a/collects/unstable/logging.rkt b/collects/unstable/logging.rkt index 04d1e070..37793d61 100644 --- a/collects/unstable/logging.rkt +++ b/collects/unstable/logging.rkt @@ -2,6 +2,64 @@ (require racket/contract) +(define level/c (or/c 'fatal 'error 'warning 'info 'debug)) +(define log-message/c (vector/c level/c string? any/c)) + +;; helper used below +(define (receiver-thread receiver stop-chan intercept) + (thread + (lambda () + (define (clear-events) + (let ([l (sync/timeout 0 receiver)]) + (when l ; still something to read + (intercept l) ; interceptor gets the whole vector + (clear-events)))) + (let loop () + (let ([l (sync receiver stop-chan)]) + (cond [(eq? l 'stop) + ;; we received all the events we were supposed + ;; to get, read them all (w/o waiting), then + ;; stop + (clear-events)] + [else ; keep going + (intercept l) + (loop)])))))) + +(struct listener (stop-chan + ;; ugly, but the thread and the listener need to know each + ;; other + [thread #:mutable] + [rev-messages #:mutable] + [done? #:mutable])) + +;; [level] -> listener +(define (start-recording #:level [level 'debug]) + (let* ([receiver (make-log-receiver (current-logger) level)] + [stop-chan (make-channel)] + [cur-listener (listener stop-chan #f '() #f)] + [t (receiver-thread + receiver stop-chan + (lambda (l) + (set-listener-rev-messages! + cur-listener + (cons l (listener-rev-messages cur-listener)))))]) + (set-listener-thread! cur-listener t) + cur-listener)) + +;; listener -> listof messages +(define (stop-recording cur-listener) + (unless (listener-done? cur-listener) + (channel-put (listener-stop-chan cur-listener) + 'stop) ; stop the receiver thread + (thread-wait (listener-thread cur-listener)) + (set-listener-done?! cur-listener #t)) + (reverse (listener-rev-messages cur-listener))) + +(provide/contract + [start-recording (->* () (#:level level/c) listener?)] + [stop-recording (-> listener? (listof log-message/c))]) + + (define (with-intercepted-logging interceptor proc #:level [level 'debug]) (let* ([orig-logger (current-logger)] ;; the new logger is unrelated to the original, to avoid getting @@ -9,30 +67,16 @@ [logger (make-logger)] [receiver (make-log-receiver logger level)] [stop-chan (make-channel)] - [t (thread (lambda () - (define (intercept l) - ;; we want to send l to the original logger, so that - ;; the rest of the system can see it too. - (log-message orig-logger - (vector-ref l 0) ; level - (vector-ref l 1) ; message - (vector-ref l 2)) ; data - (interceptor l)) - (define (clear-events) - (let ([l (sync/timeout 0 receiver)]) - (when l ; still something to read - (intercept l) ; interceptor get the whole vector - (clear-events)))) - (let loop () - (let ([l (sync receiver stop-chan)]) - (cond [(eq? l 'stop) - ;; we received all the events we were supposed - ;; to get, read them all (w/o waiting), then - ;; stop - (clear-events)] - [else ; keep going - (intercept l) - (loop)])))))]) + [t (receiver-thread + receiver stop-chan + (lambda (l) + ;; we want to send l to the original logger, so that + ;; the rest of the system can see it too. + (log-message orig-logger + (vector-ref l 0) ; level + (vector-ref l 1) ; message + (vector-ref l 2)) ; data + (interceptor l)))]) (begin0 (parameterize ([current-logger logger]) (proc)) @@ -45,10 +89,8 @@ port)) proc #:level level)) -(define level/c (or/c 'fatal 'error 'warning 'info 'debug)) - (provide/contract [with-intercepted-logging - (->* ((-> (vector/c level/c string? any/c) any) + (->* ((-> log-message/c any) (-> any)) (#:level level/c) any)]