veecle_telemetry/protocol/base.rs
1//! Core protocol type definitions and storage family trait.
2//!
3//! This module defines the core data structures used for telemetry message exchange.
4//! It includes message types for logging, tracing, and time synchronization, as well
5//! as supporting types for execution tracking and attribute management.
6//!
7//! # Message Types
8//!
9//! The protocol supports several categories of telemetry messages:
10//!
11//! - **Log Messages** - Structured logging with severity levels and attributes
12//! - **Tracing Messages** - Distributed tracing with spans, events, and links
13//! - **Time Sync Messages** - Time synchronization between systems
14//!
15//! # Thread Tracking
16//!
17//! Each message is associated with a [`ThreadId`] that uniquely identifies the thread it came from
18//! (globally unique across all processes).
19//! This allows telemetry data from multiple threads to be correlated and analyzed separately.
20
21use serde::{Deserialize, Serialize};
22
23pub use crate::SpanContext;
24pub use crate::id::{ProcessId, SpanId, ThreadId};
25
26pub(crate) trait Sealed {}
27
28#[expect(private_bounds, reason = "sealed trait")]
29/// A trait defining how data is stored in different contexts.
30///
31/// This trait allows the same protocol messages to be used with different storage strategies, see
32/// the [main protocol module][super] docs for more details on the strategies provided.
33///
34/// # Examples
35///
36/// ```rust
37/// use veecle_telemetry::protocol::base::StorageFamily;
38/// use veecle_telemetry::protocol::transient::Transient;
39///
40/// // The Transient family uses references
41/// let message: <Transient as StorageFamily>::String<'_> = "hello";
42/// ```
43pub trait StorageFamily: Clone + core::fmt::Debug + Sealed {
44 /// The string type for this storage family.
45 type String<'a>: AsRef<str> + Clone + core::fmt::Debug + serde::Serialize
46 where
47 Self: 'a;
48
49 /// The list type for this storage family.
50 type List<'a, T: Clone + core::fmt::Debug + serde::Serialize + 'a>: AsRef<[T]>
51 + Clone
52 + core::fmt::Debug
53 + serde::Serialize
54 where
55 Self: 'a;
56
57 /// The value type for this storage family.
58 type Value<'a>: Clone + core::fmt::Debug + serde::Serialize
59 where
60 Self: 'a;
61}
62
63/// A key-value attribute pair used in telemetry data.
64///
65/// Key-value pairs provide additional context for spans, events, and log messages.
66///
67/// # Examples
68///
69/// ```rust
70/// use veecle_telemetry::protocol::transient::KeyValue;
71///
72/// // Create attributes with different value types
73/// let user_id = KeyValue::new("user_id", 123);
74/// let username = KeyValue::new("username", "alice");
75/// let is_active = KeyValue::new("is_active", true);
76/// let score = KeyValue::new("score", 95.5);
77/// ```
78#[derive(Clone, Debug, Serialize, Deserialize)]
79#[serde(bound(
80 deserialize = "F::String<'a>: serde::de::DeserializeOwned, F::Value<'a>: serde::de::DeserializeOwned"
81))]
82pub struct KeyValue<'a, F>
83where
84 F: StorageFamily + 'a,
85{
86 /// The attribute key (name).
87 pub key: F::String<'a>,
88
89 /// The attribute value.
90 pub value: F::Value<'a>,
91}
92
93impl<'a, F> KeyValue<'a, F>
94where
95 F: StorageFamily + 'a,
96{
97 /// Creates a new key-value attribute pair.
98 ///
99 /// # Arguments
100 ///
101 /// * `key` - The attribute key (name)
102 /// * `value` - The attribute value
103 ///
104 /// # Examples
105 ///
106 /// ```rust
107 /// use veecle_telemetry::protocol::transient::KeyValue;
108 ///
109 /// let user_id = KeyValue::new("user_id", 123);
110 /// let username = KeyValue::new("username", "alice");
111 /// ```
112 pub fn new<K, V>(key: K, value: V) -> Self
113 where
114 K: Into<F::String<'a>>,
115 V: Into<F::Value<'a>>,
116 {
117 Self {
118 key: key.into(),
119 value: value.into(),
120 }
121 }
122}
123
124impl<'a, F> core::fmt::Display for KeyValue<'a, F>
125where
126 F: StorageFamily + 'a,
127 F::Value<'a>: core::fmt::Display,
128{
129 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
130 write!(f, "{}: {}", self.key.as_ref(), self.value)
131 }
132}
133/// A telemetry message associated with a specific execution thread.
134///
135/// This structure wraps a telemetry message with its execution context,
136/// allowing messages from different executions to be properly correlated.
137#[derive(Clone, Debug, Serialize, Deserialize)]
138#[serde(bound(deserialize = "TelemetryMessage<'a, F>: serde::de::DeserializeOwned"))]
139pub struct InstanceMessage<'a, F>
140where
141 F: StorageFamily + 'a,
142{
143 /// The thread this message belongs to.
144 pub thread_id: ThreadId,
145
146 /// The telemetry message content.
147 pub message: TelemetryMessage<'a, F>,
148}
149
150/// An enumeration of all possible telemetry message types.
151///
152/// This enum represents the different categories of telemetry data that can be
153/// collected and exported by the system.
154#[derive(Clone, Debug, Serialize, Deserialize)]
155#[serde(bound(
156 deserialize = "LogMessage<'a, F>: serde::de::DeserializeOwned, TracingMessage<'a, F>: serde::de::DeserializeOwned"
157))]
158pub enum TelemetryMessage<'a, F>
159where
160 F: StorageFamily + 'a,
161{
162 /// A structured log message with severity and attributes.
163 Log(LogMessage<'a, F>),
164
165 /// A time synchronization message for clock coordination.
166 TimeSync(TimeSyncMessage),
167
168 /// A distributed tracing message (spans, events, links).
169 Tracing(TracingMessage<'a, F>),
170}
171
172/// Log message severity levels.
173///
174/// These levels follow standard logging conventions, ordered from most verbose
175/// to most critical.
176#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
177pub enum Severity {
178 /// The "trace" level.
179 ///
180 /// Designates very low priority, often extremely verbose, information.
181 Trace,
182
183 /// The "debug" level.
184 ///
185 /// Designates lower priority information.
186 Debug,
187
188 /// The "info" level.
189 ///
190 /// Designates useful information.
191 Info,
192
193 /// The "warn" level.
194 ///
195 /// Designates hazardous situations.
196 Warn,
197
198 /// The "error" level.
199 ///
200 /// Designates very serious errors.
201 Error,
202
203 /// The "fatal" level.
204 ///
205 /// Designates critical failures that might crash the program.
206 Fatal,
207}
208
209/// A structured log message with severity, timestamp, and attributes.
210///
211/// Log messages can be optionally correlated with traces by including trace and span IDs when available.
212#[derive(Clone, Debug, Serialize, Deserialize)]
213#[serde(bound(
214 deserialize = "F::String<'a>: serde::de::DeserializeOwned, F::List<'a, KeyValue<'a, F>>: serde::de::DeserializeOwned"
215))]
216pub struct LogMessage<'a, F>
217where
218 F: StorageFamily + 'a,
219{
220 /// Timestamp in nanoseconds since Unix epoch (or system start)
221 pub time_unix_nano: u64,
222
223 /// The severity level of this log message
224 pub severity: Severity,
225
226 /// The message body
227 pub body: F::String<'a>,
228
229 /// Key-value attributes providing additional context
230 pub attributes: F::List<'a, KeyValue<'a, F>>,
231}
232
233/// A time synchronization message for coordinating clocks between systems.
234///
235/// This message contains both local time and absolute time information,
236/// allowing downstream systems to correlate local timestamps with real-world time.
237#[derive(Clone, Debug, Serialize, Deserialize)]
238pub struct TimeSyncMessage {
239 /// Local timestamp in system-specific units.
240 pub local_timestamp: u64,
241
242 /// Time since Unix epoch in nanoseconds.
243 pub since_epoch: u64,
244}
245
246/// Messages related to distributed tracing operations.
247///
248/// This enum encompasses all the different types of tracing messages that can be
249/// generated during span lifecycle management and tracing operations.
250#[derive(Clone, Debug, Serialize, Deserialize)]
251#[serde(bound(
252 deserialize = "SpanCreateMessage<'a, F>: serde::de::DeserializeOwned, SpanAddEventMessage<'a, F>: serde::de::DeserializeOwned, SpanSetAttributeMessage<'a, F>: serde::de::DeserializeOwned"
253))]
254pub enum TracingMessage<'a, F>
255where
256 F: StorageFamily + 'a,
257{
258 /// A new span has been created.
259 CreateSpan(SpanCreateMessage<'a, F>),
260
261 /// A span has been entered (made current).
262 EnterSpan(SpanEnterMessage),
263
264 /// A span has been exited (no longer current).
265 ExitSpan(SpanExitMessage),
266
267 /// A span has been closed (completed).
268 CloseSpan(SpanCloseMessage),
269
270 /// An event has been added to a span.
271 AddEvent(SpanAddEventMessage<'a, F>),
272
273 /// A link has been added to a span.
274 AddLink(SpanAddLinkMessage),
275
276 /// An attribute has been set on a span.
277 SetAttribute(SpanSetAttributeMessage<'a, F>),
278}
279
280/// Message indicating the creation of a new span.
281///
282/// This message provides all the information needed to create a new span
283/// in the trace, including its identity, timing, and initial attributes.
284#[derive(Clone, Debug, Serialize, Deserialize)]
285#[serde(bound(
286 deserialize = "F::String<'a>: serde::de::DeserializeOwned, F::List<'a, KeyValue<'a, F>>: serde::de::DeserializeOwned"
287))]
288pub struct SpanCreateMessage<'a, F>
289where
290 F: StorageFamily + 'a,
291{
292 /// The unique identifier (within the associated process) for this span.
293 pub span_id: SpanId,
294
295 /// The name of the span.
296 pub name: F::String<'a>,
297
298 /// Timestamp when the span was started.
299 pub start_time_unix_nano: u64,
300
301 /// Initial attributes attached to the span.
302 pub attributes: F::List<'a, KeyValue<'a, F>>,
303}
304
305/// Message indicating a span has been entered.
306#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
307pub struct SpanEnterMessage {
308 /// The span being entered.
309 pub span_id: SpanId,
310
311 /// Timestamp when the span was entered.
312 pub time_unix_nano: u64,
313}
314
315/// Message indicating a span has been exited.
316#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
317pub struct SpanExitMessage {
318 /// The span being exited.
319 pub span_id: SpanId,
320
321 /// Timestamp when the span was exited.
322 pub time_unix_nano: u64,
323}
324
325/// Message indicating a span has been closed (completed).
326#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
327pub struct SpanCloseMessage {
328 /// The span being closed.
329 pub span_id: SpanId,
330
331 /// Timestamp when the span was closed.
332 pub end_time_unix_nano: u64,
333}
334
335/// Message indicating an attribute has been set on a span.
336#[derive(Clone, Debug, Serialize, Deserialize)]
337#[serde(bound(deserialize = "KeyValue<'a, F>: serde::de::DeserializeOwned"))]
338pub struct SpanSetAttributeMessage<'a, F>
339where
340 F: StorageFamily + 'a,
341{
342 /// The span the attribute is being set on, if [`None`] then this applies to the "current span"
343 /// as determined by tracking [`SpanEnterMessage`] and [`SpanExitMessage`] pairs.
344 pub span_id: Option<SpanId>,
345
346 /// The attribute being set.
347 pub attribute: KeyValue<'a, F>,
348}
349
350/// Message indicating an event has been added to a span.
351#[derive(Clone, Debug, Serialize, Deserialize)]
352#[serde(bound(
353 deserialize = "F::String<'a>: serde::de::DeserializeOwned, F::List<'a, KeyValue<'a, F>>: serde::de::DeserializeOwned"
354))]
355pub struct SpanAddEventMessage<'a, F>
356where
357 F: StorageFamily + 'a,
358{
359 /// The span the event is being added to, if [`None`] then this applies to the "current span"
360 /// as determined by tracking [`SpanEnterMessage`] and [`SpanExitMessage`] pairs.
361 pub span_id: Option<SpanId>,
362
363 /// The name of the event.
364 pub name: F::String<'a>,
365
366 /// Timestamp when the event occurred.
367 pub time_unix_nano: u64,
368
369 /// Attributes providing additional context for the event.
370 pub attributes: F::List<'a, KeyValue<'a, F>>,
371}
372
373/// Message indicating a link has been added to a span.
374///
375/// Links connect spans across different traces, representing relationships
376/// that are not parent-child hierarchies.
377#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
378pub struct SpanAddLinkMessage {
379 /// The span the link is being added to, if [`None`] then this applies to the "current span"
380 /// as determined by tracking [`SpanEnterMessage`] and [`SpanExitMessage`] pairs.
381 pub span_id: Option<SpanId>,
382
383 /// The span context being linked to.
384 pub link: SpanContext,
385}