I realize it's not ideal Racket, but I've gotten a lot of mileage out of the following:
;;----------------------------------------------------------------------
(define prefix-for-say (make-parameter ""))
(define-syntax (say stx)
(syntax-case stx ()
[(say a ...)
#'(displayln (~a (prefix-for-say) a ...))]
))
(define-syntax (__WHERE:__ stx) ; Note the ':'
(with-syntax ((fpath (syntax-source stx))
(line (syntax-line stx)))
(syntax-case stx ()
[_ #'(~a "file:" fpath " (line:" line "): ")])))
;;----------------------------------------------------------------------
I then sprinkle my code with:
(say __WHERE:__ "the thing was: " thing ", and it had " num-owners " owners)
which gets me output like:
file:/Users/dstorrs/some/path/to/file.rkt (line:131): the thing was: <thing>, and it had 7 owners
For threading, I have this:
(define/contract (threaded thnk)
(-> (-> any) any)
(define current-prefix (prefix-for-say))
(define thread-label
(cond [(empty-string? current-prefix) (~a (rand-val "thread") ": ")]
[else (~a (rand-val "thread") " / " current-prefix)]))
(parameterize ([prefix-for-say thread-label])
(thread (thunk (begin0 (thnk) (sleep 0)))))) ; sleep 0 suggests that the thread run now
(define (foo x) (threaded (thunk (bar x))))
(define (bar x) (threaded (thunk (say x))))
(foo "hello, world")
And I get output like this:
thread-261855 / thread-861893: hello, world
There are improvements that I could make to this -- for example, make 'threaded' a macro so you don't need to pass a thunk, then special-case it so that thunks were executed so that existing code continues to work. Also, 'say' could have an option that lets you use println instead of displayln, or automatically map ~v over arguments. And obviously this system doesn't take advantage of Racket's hierarchical logging infrastructure. That said, it's simple and it works really well for me.