Events as logs, not span events
Status: Accepted · Date: 6 Aug 2026 · Issues: #814, #808, #809, #805 Relates: ADR-0024 (dashboard UX telemetry)
Context
OpenTelemetry is deprecating the Span Events
API — Span.AddEvent,
Span.RecordException and friends. Maintaining two overlapping ways to emit a
correlated event (span events via the Tracing API, log events via the Logs API)
produced split guidance for instrumentation authors and a slower-evolving event
model. The replacement is events as logs: emitted through the Logs API,
named, and correlated to the active span through context rather than attached to
it. Semantic-convention authors are now explicitly told to document events as
log-based events.
Nothing is being removed soon. The deprecation is phased, existing data stays valid in the OTLP trace model, and compatibility layers are a stated priority. This is direction-setting, not an outage.
What rolter emits today
A grep for the deprecated API finds nothing:
$ grep -rn "add_event\|record_exception" --include='*.rs' crates/
(no matches)
That result is misleading, and reading the bridge is what the audit actually
required. rolter instruments through tracing + tracing-opentelemetry, and
that layer’s on_event turns every tracing event fired inside an active span
into an OTel span event (tracing-opentelemetry-0.33.0/src/layer.rs:1468,
span.add_event(...); the pre-export path pushes onto builder.events
identically). An ERROR-level event additionally sets the span status to error.
So rolter has zero explicit uses of the deprecated API and is, in practice, an
exclusive user of it: the ~57 tracing event macros across rolter-gateway,
rolter-proxy and rolter-core become span events whenever they fire inside one
of the pipeline spans. The migration surface is the bridge, not our call sites.
Why the ordering between #808 and #809 changes
The GenAI semantic conventions (#808) are precisely the area moving to log-based events. Implementing them against span events would be building onto a deprecated API on the day it is written.
Emitting log-based events requires exporting logs at all, and rolter does not:
there is no OTLP logs exporter in the workspace and signoz_logs is empty by
construction. #809 therefore
stops being an independent nice-to-have and becomes a prerequisite.
Decision
Log-based events are rolter’s event model. New instrumentation targets the Logs
API; span events are legacy, produced only incidentally by the tracing bridge.
Concretely:
- No new explicit span events.
add_eventandrecord_exceptionare not to be called directly. Nothing calls them today and nothing should start. - #809 (OTLP log export) sequences before #808 (GenAI conventions). The conventions #808 implements are specified as log-based events, so the log pipeline has to exist before they can be emitted correctly. #808’s acceptance criteria adopt log-based events as the target.
tracingstays. The bridge is idiomatic Rust and the ecosystem’s own integration, not a bespoke house wrapper — the same distinction drawn in the #815 wrapping audit. It is where the span-event mapping lives, so it is also the single place a future migration happens: whentracing-opentelemetryroutes events through the Logs API, rolter follows by upgrading, without touching 57 call sites.- Correlation is the acceptance bar. A log-based event is only a replacement
for a span event if it carries
trace_idandspan_id, which is already called out as most of the value in #809. An exported log that cannot be joined back to its span is a regression against what the bridge does today. - Content stays off. The GenAI opt-in content attributes
(
gen_ai.input.messages/gen_ai.output.messages) remain disabled whether they are carried as span events or as log records. Moving event model does not move the privacy line.
Consequences
- The migration is a dependency upgrade rather than a code sweep, because rolter
never adopted the deprecated API directly. This is the payoff of having gone
through
tracinginstead of calling OTel by hand. - #809 gains a second justification beyond “logs are missing”: it unblocks #808.
Its
trace_id/span_idcorrelation requirement is now load-bearing rather than a nicety, and should be treated as a blocking acceptance criterion. - Until #809 lands, error detail continues to reach backends as span events via the bridge. That is fine — the deprecation is phased and the data stays valid — but it means the GenAI work should not start early and improvise its own event channel.
- Anything that wants a new event type before #809 has no correct home. The answer is to wait rather than to add a span event that will have to be removed.
Alternatives considered
Implement #808 now against span events, migrate later. Rejected. The conventions are specified as log-based events, so this builds a second migration into work that has not shipped yet, on the exact attributes most likely to be re-specified.
Emit both span events and log events during a transition. Rejected. Duplicate
correlated events on both signals is the split-guidance problem OpenTelemetry is
deprecating span events to escape, and it doubles egress for data that already
joins on trace_id.
Drop tracing and call the OTel Logs API directly. Rejected. It is a large
rewrite of every call site to avoid a bridge that will itself be updated
upstream, and it contradicts the #815 finding that the ecosystem bridge is not
the wrapping the “don’t wrap” guidance targets.