Skip to main content

trace_weft/
events.rs

1//! Intra-span event recording.
2//!
3//! Events are point-in-time occurrences inside a span (a retry, a budget check,
4//! a guardrail trip, an REPL step). Build one with [`event`], attach
5//! attributes, and `.record()` it. Like spans, events auto-link to the ambient
6//! span context set by `SpanBuilder::run` / the macros, so an event recorded
7//! inside an instrumented body is parented to that span without manual IDs.
8
9use std::collections::HashMap;
10use std::sync::atomic::{AtomicU64, Ordering};
11use trace_weft_core::{EventId, EventKind, EventRecord, RunId, SpanId, TraceId};
12use uuid::Uuid;
13
14/// Process-wide monotonic ordering hint for events.
15static EVENT_SEQ: AtomicU64 = AtomicU64::new(0);
16
17pub struct EventBuilder {
18    event: EventRecord,
19}
20
21impl EventBuilder {
22    pub fn new(kind: EventKind, name: impl Into<String>) -> Self {
23        let now = std::time::SystemTime::now()
24            .duration_since(std::time::UNIX_EPOCH)
25            .unwrap_or_default()
26            .as_millis() as u64;
27
28        let mut event = EventRecord {
29            event_id: EventId(Uuid::now_v7()),
30            trace_id: TraceId(Uuid::now_v7()),
31            run_id: RunId(Uuid::now_v7()),
32            parent_span_id: None,
33            seq: 0,
34            event_kind: kind,
35            name: name.into(),
36            timestamp: now,
37            attributes: HashMap::new(),
38            schema_version: "1.0".to_string(),
39        };
40
41        // Auto-link to the current span unless overridden via `with_parent`.
42        if let Some(ctx) = crate::context::current_span_context() {
43            event.trace_id = ctx.trace_id;
44            event.run_id = ctx.run_id;
45            event.parent_span_id = Some(ctx.span_id);
46        }
47
48        Self { event }
49    }
50
51    /// Explicitly parent this event, overriding any ambient context.
52    pub fn with_parent(mut self, trace_id: TraceId, run_id: RunId, parent_id: SpanId) -> Self {
53        self.event.trace_id = trace_id;
54        self.event.run_id = run_id;
55        self.event.parent_span_id = Some(parent_id);
56        self
57    }
58
59    pub fn attribute(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
60        self.event.attributes.insert(key.into(), value);
61        self
62    }
63
64    pub fn attributes(mut self, attrs: HashMap<String, serde_json::Value>) -> Self {
65        self.event.attributes.extend(attrs);
66        self
67    }
68
69    /// Assign the ordering `seq` and send the event to the global recorder.
70    pub async fn record(mut self) {
71        self.event.seq = EVENT_SEQ.fetch_add(1, Ordering::Relaxed);
72        crate::record_event(self.event).await;
73    }
74}
75
76/// Start building an intra-span event of `kind` named `name`.
77pub fn event(kind: EventKind, name: impl Into<String>) -> EventBuilder {
78    EventBuilder::new(kind, name)
79}