otel P3: auto-instrument handlers at the make-app seam
otel/instrument-routes wraps each flattened Dream route's handler in a timed
span named METHOD /route with {:http.method :http.route :http.status} attrs;
host/make-app applies it so every matched request becomes a trace. Refactored
with-span onto a shared otel/-timed core that takes a finalize fn for
result-derived attrs (the http.status only known post-handler).
This commit is contained in:
@@ -73,13 +73,15 @@
|
||||
(set! otel/-stack (list))
|
||||
nil)))
|
||||
|
||||
;; ── with-span: the timed-effect combinator ───────────────────────────
|
||||
;; ── -timed: the shared timed-effect core ─────────────────────────────
|
||||
;; Records a span around (thunk): a fresh span id, the trace inherited from the
|
||||
;; enclosing span (or a new trace at the root), the enclosing span as :parent, and
|
||||
;; t0/t1 straddling the call. Pushes its context so nested with-spans see it as
|
||||
;; parent, pops after, then records the finished span. Returns the thunk's value.
|
||||
(define otel/with-span
|
||||
(fn (name attrs thunk)
|
||||
;; t0/t1 straddling the call. Pushes its context so nested spans see it as parent,
|
||||
;; pops after, then records the finished span. `finalize` maps the thunk's result
|
||||
;; to EXTRA attrs merged onto `attrs` (e.g. an http.status only known post-call).
|
||||
;; Returns the thunk's value unchanged.
|
||||
(define otel/-timed
|
||||
(fn (name attrs finalize thunk)
|
||||
(let ((parent-ctx (otel/-top)))
|
||||
(let ((trace (if parent-ctx (get parent-ctx :trace) (otel/gen-trace-id)))
|
||||
(span (otel/gen-span-id))
|
||||
@@ -91,5 +93,36 @@
|
||||
(otel/-pop!)
|
||||
(otel/record!
|
||||
{:trace trace :span span :parent parent :name name
|
||||
:t0 t0 :t1 t1 :attrs attrs :events (list)})
|
||||
:t0 t0 :t1 t1
|
||||
:attrs (merge attrs (finalize result))
|
||||
:events (list)})
|
||||
result))))))))
|
||||
|
||||
;; with-span — a plain timed span; no result-derived attrs.
|
||||
(define otel/with-span
|
||||
(fn (name attrs thunk)
|
||||
(otel/-timed name attrs (fn (r) {}) thunk)))
|
||||
|
||||
;; ── auto-instrument HTTP handlers ─────────────────────────────────────
|
||||
;; A trace is a composition; a request handler is a timed effect. Wrap one Dream
|
||||
;; route's handler so dispatching it records a root span "METHOD /route" with
|
||||
;; {:http.method :http.route :http.status}. The status is only known after the
|
||||
;; handler runs, so it's a finalize attr; the result is coerced the same way the
|
||||
;; router coerces it, so a bare-string handler still reports 200. The raw handler
|
||||
;; result is returned untouched so dr/run-route's own coercion is unchanged.
|
||||
(define otel/instrument-route
|
||||
(fn (r)
|
||||
(let ((method (dream-route-method r))
|
||||
(path (dream-route-path r))
|
||||
(handler (dream-route-handler r)))
|
||||
(dream-route method path
|
||||
(fn (req)
|
||||
(otel/-timed
|
||||
(str method " " path)
|
||||
{:http.method method :http.route path}
|
||||
(fn (resp) {:http.status (dream-status (dream-coerce-response resp))})
|
||||
(fn () (handler req))))))))
|
||||
|
||||
;; Flatten nested route groups and instrument each route — the host/make-app seam.
|
||||
(define otel/instrument-routes
|
||||
(fn (routes) (map otel/instrument-route (dr/flatten-routes routes))))
|
||||
|
||||
Reference in New Issue
Block a user