+(: timeline-download (-> Integer (Listof Feed) Void))
+(define (timeline-download num-workers feeds)
+ ; TODO No need for map - can just iter
+ (void (concurrent-filter-map num-workers feed-download feeds)))
+
+; TODO timeline contract : time-sorted list of messages
+(: timeline-read (-> Timeline-Order (Listof Feed) (Listof Msg)))
+(define (timeline-read order feeds)
+ (define cmp (match order
+ ['old->new <]
+ ['new->old >]))
+ (sort (append* (filter-map feed->msgs feeds))
+ (λ (a b) (cmp (msg-ts-epoch a) (msg-ts-epoch b)))))
+
+(: log-writer-stop (-> Thread Void))
+(define (log-writer-stop log-writer)
+ (log-message (current-logger) 'fatal 'stop "Exiting." #f)
+ (thread-wait log-writer))
+
+(: logger-start (-> Log-Level Thread))
+(define (logger-start level)
+ (let* ([logger
+ (make-logger #f #f level #f)]
+ [log-receiver
+ (make-log-receiver logger level)]
+ [log-writer
+ (thread
+ (λ ()
+ (parameterize
+ ([date-display-format 'iso-8601])
+ (let loop ()
+ (match-define (vector level msg _ topic) (sync log-receiver))
+ (unless (equal? topic 'stop)
+ (eprintf "~a [~a] ~a~n" (date->string (current-date) #t) level msg)
+ (loop))))))])
+ (current-logger logger)
+ log-writer))
+
+(module+ main