1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
//! Tracer capability trait for distributed-tracing exporters.
//!
//! A `Tracer` plugin turns span events produced by the bext runtime (and
//! other plugins) into whatever wire format its backend expects — OTLP,
//! Datadog, Honeycomb, line-oriented stdout for development, and so on.
//! See `plan/ecosystem/02-capabilities.md` for the design rationale and
//! the list of reference implementations landing in E1.
//!
//! # Design rules
//!
//! - **No `opentelemetry` crate types in the trait.** Trace IDs, span IDs,
//! attribute values, kinds, and status codes are all plain data. A non-otel
//! implementation (like `@bext/tracer-stdout`) must be able to satisfy
//! the trait without pulling in `opentelemetry` as a transitive dep. An
//! OTLP-based implementation maps these types 1:1 into the otel SDK.
//! - **Aligned with OpenTelemetry semantic conventions.** IDs are the same
//! widths as the OTel wire format (16 bytes trace id, 8 bytes span id),
//! status codes match OTel's `Unset`/`Ok`/`Error`, span kinds match
//! OTel's `Internal`/`Server`/`Client`/`Producer`/`Consumer`, and
//! attribute value types cover the OTel semantic-convention allowed set
//! (string / bool / int / float / arrays). Plugin authors are expected
//! to use the OTel semconv attribute keys (e.g. `http.request.method`,
//! `http.response.status_code`); the trait does not enforce this.
//! - **Explicit parent context, not thread-local.** A span is started with
//! an explicit `Option<SpanHandle>` parent, so callers control propagation
//! and the trait stays async-runtime-agnostic. Thread-local propagation,
//! if wanted, is a concern for a helper layer above this trait, not the
//! trait itself.
//! - **Infallible recording, fallible flush.** `start_span` / `set_attribute`
//! / `set_status` / `add_event` / `end_span` never return errors — a
//! tracing failure must never break the caller. Only `flush` (which does
//! real I/O) returns `Result`. Implementations that hit transient errors
//! record them internally and surface them at flush time or via their
//! own metrics.
//! - **Sync and object-safe.** Matches the rest of the plugin API: all
//! methods are synchronous, there are no generic parameters on trait
//! methods, and `SpanHandle` is a plain `Copy` value — so `dyn Tracer`
//! works across the WASM ABI and the in-process host-function table
//! alike.
/// An opaque handle returned by [`TracerPlugin::start_span`]. Passing a
/// `SpanHandle` back to the same tracer is how the runtime attaches
/// attributes, events, and status to an active span. Handles are cheap
/// `Copy` values — they are just the id pair the tracer already emits
/// on the wire.
///
/// A handle is only valid for the tracer that created it. Passing a
/// handle produced by one tracer to another tracer is a programmer
/// error; implementations should treat unknown handles as a no-op
/// rather than panicking.
/// OpenTelemetry-aligned span kind. Mirrors `opentelemetry::trace::SpanKind`
/// without depending on it. Used by backends to colour spans in the UI
/// and to apply kind-specific semantic conventions.
/// OpenTelemetry-aligned span status. Mirrors `opentelemetry::trace::Status`
/// without depending on it.
/// Attribute value for spans and events. Matches the value types allowed
/// by the OpenTelemetry semantic-convention specification: string, bool,
/// signed 64-bit int, 64-bit float, and homogeneous arrays of each.
///
/// Kept as a plain enum (no `opentelemetry::Value` indirection) so that
/// non-otel backends can encode it directly without a mapping layer.
/// Arguments for [`TracerPlugin::start_span`]. A struct rather than a long
/// positional parameter list so the trait stays forward-compatible: new
/// optional fields can be added without breaking existing implementations
/// that use `..Default::default()`.
/// A point-in-time event recorded inside a span. Events do not have
/// durations; they are the OTel equivalent of a structured log line
/// scoped to a span.
/// A distributed-tracing exporter plugin.
///
/// Bext's observability hooks (and any plugin that wants to emit custom
/// spans) call into the active `TracerPlugin` through host functions.
/// There is at most one active `TracerPlugin` per site; multiple backends
/// are achieved by chaining (a fan-out tracer that forwards to several
/// children) rather than by running several implementations side-by-side.
///
/// Implementations must be cheap on the hot path. `start_span`,
/// `set_attribute`, `set_status`, `add_event`, and `end_span` run inline
/// with the operation they describe; they should buffer rather than
/// perform network I/O. Actual export happens during [`TracerPlugin::flush`],
/// which the runtime calls on an interval and at shutdown.