[new] OpenTelemetry handler: support spans created outside Telemere
Some checks are pending
Clj tests / tests (17, ubuntu-latest) (push) Waiting to run
Clj tests / tests (19, ubuntu-latest) (push) Waiting to run
Clj tests / tests (21, ubuntu-latest) (push) Waiting to run
Cljs tests / tests (21, ubuntu-latest) (push) Waiting to run
Graal tests / tests (17, macOS-latest) (push) Waiting to run
Graal tests / tests (17, ubuntu-latest) (push) Waiting to run
Graal tests / tests (17, windows-latest) (push) Waiting to run

BEFORE this commit:

  Telemere captured OpenTelemetry context only when generating
  tracing signals (`trace!`, `spy!`, etc.).

AFTER this commit:

  Telemere always captures OpenTelemetry context when present.

Motivation for the change:

  Telemere users may have spans automatically created by the
  OpenTelemetry Java Agent, or manually created by other libs
  like clj-otel.

  By always capturing the OpenTelemetry context when present,
  this lets even non-tracing Telemere signals (like `log!`)
  include the relevant trace and span IDs for external tooling.

Thanks to @devurandom for suggesting this, and for helping
debug/identify the necessary changes 🙏
This commit is contained in:
Peter Taoussanis 2025-12-04 11:22:25 +01:00
parent 13d16d9378
commit e95937401d
3 changed files with 13 additions and 10 deletions

View file

@ -26,7 +26,7 @@ All options are available for all signal creator calls:
`:elidable?` --- Should signal be subject to compile-time elision? (default true)
`:allow?` ------ Custom override for usual runtime filtering (true => ALWAYS create signal)
`:trace?` ------ Should tracing be enabled for `:run` form?
`:trace?` ------ Should tracing be enabled for this signal? (default true iff `:run` provided or OpenTelemetry tracing enabled)
`:sample` ------ Sample ?rate ∈ℝ[0,1] for random signal sampling (0.75 => allow 75% of signals, nil => allow all)
`:when` -------- Arb ?form; when present, form must return truthy to allow signal

View file

@ -569,7 +569,10 @@
id-form :id
level-form :level} opts
trace? (get opts :trace? run-form?)
otel? (get opts :otel? (and clj? present:otel?)) ; Undocumented
otel-tracing? (and otel? enabled:otel-tracing?)
trace? (get opts :trace? (or run-form? otel-tracing?))
_
(when-not (contains? #{true false nil} trace?)
(truss/ex-info!
@ -612,9 +615,9 @@
(dissoc opts
:elidable? :coords :inst :uid :xfn :xfn+ :kvs+,
:sample :ns :kind :id :level :filter :when #_:limit #_:limit-by,
:ctx :ctx+ :parent #_:trace?, :do :let :data :msg :error,
:ctx :ctx+ :parent :trace?, :do :let :data :msg :error,
:run :run-form :run-val, :elide? :allow? #_:callsite-id,
:host :thread :otel/context))]
:host :thread :otel? :otel/context))]
(if-let [kvs+ (get opts :kvs+)] ; Undocumented
(if base
@ -696,7 +699,7 @@
(catch :all t# (RunResult. nil t# (- (enc/now-nano*) t0#))))))]
;; Trace without OpenTelemetry
(or cljs? (not enabled:otel-tracing?))
(or cljs? (not otel-tracing?))
`[~'__otel-context1 nil
~'__uid ~(auto-> uid-form `(taoensso.telemere/*uid-fn* (if ~'__root0 false true)))
~'__root1 (or ~'__root0 ~(when trace? `{:id ~'__id, :uid ~'__uid}))
@ -710,9 +713,9 @@
(catch :all t# (RunResult. nil t# (- (enc/now-nano*) t0#)))))))]
;; Trace with OpenTelemetry
(and clj? enabled:otel-tracing?)
(and clj? otel-tracing?)
`[~'__otel-context0 ~(get opts :otel/context `(otel-context)) ; Context
~'__otel-context1 ~(if run-form? `(otel-context+span ~'__id ~'__inst ~'__otel-context0 ~(get opts :otel/span-kind)) ~'__otel-context0)
~'__otel-context1 ~(if run-form? `(otel-context+span ~'__id ~'__inst ~'__otel-context0 ~(get opts :otel/span-kind)) `~'__otel-context0)
~'__uid ~(auto-> uid-form `(or (otel-span-id ~'__otel-context1) (com.taoensso.encore.Ids/genHexId16)))
~'__root1
(or ~'__root0

View file

@ -447,10 +447,10 @@
(let [sv (with-sig (sig! {:level :info, :parent {:uid :uid1, :foo :bar}}))] (is (sm? sv {:parent {:id :submap/nx :uid :uid1, :foo :bar}}) "Manual `:parent/uid`"))
(let [sv (with-sig (sig! {:level :info, :root {:id :id1, :foo :bar}}))] (is (sm? sv {:root {:id :id1 :uid :submap/nx, :foo :bar}}) "Manual `:root/id`"))
(let [sv (with-sig (sig! {:level :info, :root {:uid :uid1, :foo :bar}}))] (is (sm? sv {:root {:id :submap/nx :uid :uid1, :foo :bar}}) "Manual `:root/uid`"))
(let [sv (with-sig (sig! {:level :info})) ] (is (sm? sv {:uid nil})))
(let [sv (with-sig (sig! {:level :info, :otel? false})) ] (is (sm? sv {:uid nil})))
(let [sv (with-sig (sig! {:level :info, :uid :auto})) ] (is (sm? sv {:uid string?})))
(let [sv (binding [tel/*uid-fn* (fn [_] "my-uid")]
(with-sig (sig! {:level :info, :uid :auto}))) ] (is (sm? sv {:uid "my-uid"})))])
(with-sig (sig! {:level :info, :uid :auto, :otel? false}))) ] (is (sm? sv {:uid "my-uid"})))])
(testing "Auto parent/root"
[(testing "Tracing disabled"
@ -882,7 +882,7 @@
(is (= ((tel/pr-signal-fn {:pr-fn (fn [_] "str")}) sig) (str "str" utils/newline))))]))
(testing "format-signal-fn"
(let [sig (with-sig :raw :trap (tel/event! ::ev-id {:inst t1, :msg ["a" "b"]}))]
(let [sig (with-sig :raw :trap (tel/event! ::ev-id {:inst t1, :msg ["a" "b"], :otel? false}))]
[(is (enc/str-starts-with? ((tel/format-signal-fn) sig ) "2024-01-01T01:01:01.110Z INFO EVENT"))
(is (enc/str-ends-with? ((tel/format-signal-fn) (dissoc sig :host)) "::ev-id a b\n"))]))])])