1use std::{collections::HashMap, sync::Arc};
2
3use anyhow::{Result, anyhow};
4use parking_lot::RwLock;
5use uuid::Uuid;
6use zelos_trace_types::{
7 Value,
8 ipc::{
9 IpcMessage, IpcMessageWithId, Sender, TraceEvent, TraceEventFieldMetadata,
10 TraceEventFieldNamedValues, TraceEventSchema, TraceSegmentEnd, TraceSegmentStart,
11 },
12};
13
14use crate::time::now_time_ns;
15
16#[derive(Debug)]
18pub struct TraceSourceEvent {
19 id: Uuid,
20 source_name: String,
21 sender: Sender,
22 pub name: String,
23 pub schema: Vec<TraceEventFieldMetadata>,
24}
25
26impl TraceSourceEvent {
27 pub fn emit(&self, time_ns: i64, fields: impl Iterator<Item = (String, Value)>) -> Result<()> {
28 let evt = TraceEvent {
29 time_ns,
30 name: self.name.clone(),
31 fields: fields.collect(),
32 };
33
34 self.sender.send(IpcMessageWithId {
35 segment_id: self.id,
36 source_name: self.source_name.clone(),
37 msg: IpcMessage::TraceEvent(evt),
38 })?;
39
40 Ok(())
41 }
42
43 pub async fn emit_async(
44 &self,
45 time_ns: i64,
46 fields: impl Iterator<Item = (String, Value)>,
47 ) -> Result<()> {
48 let evt = TraceEvent {
49 time_ns,
50 name: self.name.clone(),
51 fields: fields.collect(),
52 };
53
54 self.sender
55 .send_async(IpcMessageWithId {
56 segment_id: self.id,
57 source_name: self.source_name.clone(),
58 msg: IpcMessage::TraceEvent(evt),
59 })
60 .await?;
61
62 Ok(())
63 }
64
65 pub fn build(&self) -> builder::EventBuilder<'_> {
66 builder::EventBuilder::new(self)
67 }
68}
69
70#[derive(Debug)]
73pub struct TraceSource {
74 pub id: Uuid,
75 pub source_name: String,
76 sender: Sender,
77 events: RwLock<HashMap<String, Arc<TraceSourceEvent>>>,
78}
79
80impl TraceSource {
81 pub fn new(source_name: &str, sender: Sender) -> Self {
82 let id = Uuid::now_v7();
83 let src = TraceSource {
84 id,
85 source_name: source_name.to_string(),
86 sender,
87 events: RwLock::new(HashMap::new()),
88 };
89
90 tracing::debug!(?id, ?source_name, "TraceSource::new");
91
92 if let Err(e) = src.emit_start() {
93 tracing::error!("Error emitting trace segment start: {}", e);
94 }
95
96 src
97 }
98
99 fn emit(&self, msg: IpcMessage) -> Result<()> {
100 self.sender.send(IpcMessageWithId {
101 segment_id: self.id,
102 source_name: self.source_name.clone(),
103 msg,
104 })?;
105
106 Ok(())
107 }
108
109 async fn emit_async(&self, msg: IpcMessage) -> Result<()> {
110 self.sender
111 .send_async(IpcMessageWithId {
112 segment_id: self.id,
113 source_name: self.source_name.clone(),
114 msg,
115 })
116 .await?;
117
118 Ok(())
119 }
120
121 pub fn emit_start(&self) -> Result<()> {
122 self.emit(IpcMessage::TraceSegmentStart(TraceSegmentStart {
123 time_ns: now_time_ns(),
124 source_name: self.source_name.clone(),
125 }))
126 }
127
128 pub fn emit_end(&self) -> Result<()> {
129 self.emit(IpcMessage::TraceSegmentEnd(TraceSegmentEnd {
130 time_ns: now_time_ns(),
131 }))
132 }
133
134 pub fn add_value_table(
135 &self,
136 name: &str,
137 field_name: &str,
138 values: impl Iterator<Item = (Value, String)>,
139 ) -> Result<()> {
140 self.emit(IpcMessage::TraceEventFieldNamedValues(
141 TraceEventFieldNamedValues {
142 event_name: name.to_string(),
143 field_name: field_name.to_string(),
144 values: values.collect(),
145 },
146 ))
147 }
148
149 pub fn add_event(
150 &self,
151 name: &str,
152 schema: impl Iterator<Item = TraceEventFieldMetadata>,
153 ) -> Result<Arc<TraceSourceEvent>> {
154 if self.events.read().contains_key(name) {
155 return Err(anyhow!("Event={} already exists", name));
156 }
157
158 let msg = Arc::new(TraceSourceEvent {
159 id: self.id,
160 source_name: self.source_name.clone(),
161 sender: self.sender.clone(),
162 name: name.to_string(),
163 schema: schema.collect(),
164 });
165
166 self.emit(IpcMessage::TraceEventSchema(TraceEventSchema {
168 name: name.to_string(),
169 fields: msg.schema.clone(),
170 }))?;
171
172 self.events.write().insert(name.to_string(), msg.clone());
174
175 Ok(msg)
176 }
177
178 pub async fn add_event_async(
179 &self,
180 name: &str,
181 schema: impl Iterator<Item = TraceEventFieldMetadata>,
182 ) -> Result<Arc<TraceSourceEvent>> {
183 if self.events.read().contains_key(name) {
184 return Err(anyhow!("Event={} already exists", name));
185 }
186
187 let msg = Arc::new(TraceSourceEvent {
188 id: self.id,
189 source_name: self.source_name.clone(),
190 sender: self.sender.clone(),
191 name: name.to_string(),
192 schema: schema.collect(),
193 });
194
195 self.emit_async(IpcMessage::TraceEventSchema(TraceEventSchema {
197 name: name.to_string(),
198 fields: msg.schema.clone(),
199 }))
200 .await?;
201
202 self.events.write().insert(name.to_string(), msg.clone());
204
205 Ok(msg)
206 }
207
208 pub fn get_event(&self, name: &str) -> Result<Arc<TraceSourceEvent>> {
209 self.events
210 .read()
211 .get(name)
212 .cloned()
213 .ok_or_else(|| anyhow!("Event not found"))
214 }
215
216 pub fn build_event<'a>(&'a self, name: &'a str) -> builder::TraceSourceEventBuilder<'a> {
217 builder::TraceSourceEventBuilder::new(self, name)
218 }
219}
220
221impl Drop for TraceSource {
222 fn drop(&mut self) {
223 if let Err(e) = self.emit_end() {
224 tracing::debug!("Error emitting trace segment end: {}", e);
225 }
226 }
227}
228
229pub mod builder {
230 use zelos_trace_types::DataType;
231
232 use super::*;
233
234 #[must_use]
235 pub struct EventBuilder<'a> {
236 parent: &'a TraceSourceEvent,
237 data: HashMap<String, Value>,
238 }
239
240 impl<'a> EventBuilder<'a> {
241 pub(crate) fn new(parent: &'a TraceSourceEvent) -> Self {
242 EventBuilder {
243 parent,
244 data: HashMap::new(),
245 }
246 }
247
248 pub fn emit(mut self) -> Result<()> {
250 self.parent.emit(now_time_ns(), self.data.drain())
251 }
252
253 pub fn emit_at(mut self, time_ns: i64) -> Result<()> {
255 self.parent.emit(time_ns, self.data.drain())
256 }
257
258 pub async fn emit_async(mut self) -> Result<()> {
260 self.parent
261 .emit_async(now_time_ns(), self.data.drain())
262 .await
263 }
264
265 pub async fn emit_at_async(mut self, time_ns: i64) -> Result<()> {
267 self.parent.emit_async(time_ns, self.data.drain()).await
268 }
269
270 pub fn try_insert(&mut self, name: &str, value: Value) -> Result<()> {
272 let field = self
274 .parent
275 .schema
276 .iter()
277 .find(|field| field.name == name)
278 .ok_or_else(|| anyhow!("Field '{}' not found in schema", name))?;
279
280 if field.data_type != value.data_type() {
282 return Err(anyhow!(
283 "Type mismatch for field '{}': expected {:?}, found {:?}",
284 name,
285 field.data_type,
286 value.data_type()
287 ));
288 }
289
290 self.data.insert(name.to_string(), value);
292 Ok(())
293 }
294
295 pub fn try_insert_i8(mut self, name: &str, value: i8) -> Result<Self> {
296 self.try_insert(name, Value::Int8(value))?;
297 Ok(self)
298 }
299
300 pub fn try_insert_i16(mut self, name: &str, value: i16) -> Result<Self> {
301 self.try_insert(name, Value::Int16(value))?;
302 Ok(self)
303 }
304
305 pub fn try_insert_i32(mut self, name: &str, value: i32) -> Result<Self> {
306 self.try_insert(name, Value::Int32(value))?;
307 Ok(self)
308 }
309
310 pub fn try_insert_i64(mut self, name: &str, value: i64) -> Result<Self> {
311 self.try_insert(name, Value::Int64(value))?;
312 Ok(self)
313 }
314
315 pub fn try_insert_u8(mut self, name: &str, value: u8) -> Result<Self> {
316 self.try_insert(name, Value::UInt8(value))?;
317 Ok(self)
318 }
319
320 pub fn try_insert_u16(mut self, name: &str, value: u16) -> Result<Self> {
321 self.try_insert(name, Value::UInt16(value))?;
322 Ok(self)
323 }
324
325 pub fn try_insert_u32(mut self, name: &str, value: u32) -> Result<Self> {
326 self.try_insert(name, Value::UInt32(value))?;
327 Ok(self)
328 }
329
330 pub fn try_insert_u64(mut self, name: &str, value: u64) -> Result<Self> {
331 self.try_insert(name, Value::UInt64(value))?;
332 Ok(self)
333 }
334
335 pub fn try_insert_f32(mut self, name: &str, value: f32) -> Result<Self> {
336 self.try_insert(name, Value::Float32(value))?;
337 Ok(self)
338 }
339
340 pub fn try_insert_f64(mut self, name: &str, value: f64) -> Result<Self> {
341 self.try_insert(name, Value::Float64(value))?;
342 Ok(self)
343 }
344
345 pub fn try_insert_timestamp_ns(mut self, name: &str, value: i64) -> Result<Self> {
346 self.try_insert(name, Value::TimestampNs(value))?;
347 Ok(self)
348 }
349
350 pub fn try_insert_binary(mut self, name: &str, value: Vec<u8>) -> Result<Self> {
351 self.try_insert(name, Value::Binary(value))?;
352 Ok(self)
353 }
354
355 pub fn try_insert_string(mut self, name: &str, value: String) -> Result<Self> {
356 self.try_insert(name, Value::String(value))?;
357 Ok(self)
358 }
359
360 pub fn try_insert_bool(mut self, name: &str, value: bool) -> Result<Self> {
361 self.try_insert(name, Value::Boolean(value))?;
362 Ok(self)
363 }
364 }
365
366 #[must_use]
367 pub struct TraceSourceEventBuilder<'a> {
368 source: &'a TraceSource,
369 name: &'a str,
370 schema: HashMap<String, TraceEventFieldMetadata>,
371 }
372
373 impl<'a> TraceSourceEventBuilder<'a> {
374 pub(crate) fn new(source: &'a TraceSource, name: &'a str) -> Self {
375 TraceSourceEventBuilder {
376 source,
377 name,
378 schema: HashMap::new(),
379 }
380 }
381
382 pub fn build(self) -> Result<Arc<TraceSourceEvent>> {
384 self.source.add_event(self.name, self.schema.into_values())
385 }
386
387 pub async fn build_async(self) -> Result<Arc<TraceSourceEvent>> {
389 self.source
390 .add_event_async(self.name, self.schema.into_values())
391 .await
392 }
393
394 pub fn add_field(mut self, name: &str, data_type: DataType, unit: Option<String>) -> Self {
395 self.schema.insert(
396 name.to_string(),
397 TraceEventFieldMetadata {
398 name: name.to_string(),
399 data_type,
400 unit,
401 },
402 );
403 self
404 }
405
406 pub fn add_i8_field(self, name: &str, unit: Option<String>) -> Self {
407 self.add_field(name, DataType::Int8, unit)
408 }
409
410 pub fn add_i16_field(self, name: &str, unit: Option<String>) -> Self {
411 self.add_field(name, DataType::Int16, unit)
412 }
413
414 pub fn add_i32_field(self, name: &str, unit: Option<String>) -> Self {
415 self.add_field(name, DataType::Int32, unit)
416 }
417
418 pub fn add_i64_field(self, name: &str, unit: Option<String>) -> Self {
419 self.add_field(name, DataType::Int64, unit)
420 }
421
422 pub fn add_u8_field(self, name: &str, unit: Option<String>) -> Self {
423 self.add_field(name, DataType::UInt8, unit)
424 }
425
426 pub fn add_u16_field(self, name: &str, unit: Option<String>) -> Self {
427 self.add_field(name, DataType::UInt16, unit)
428 }
429
430 pub fn add_u32_field(self, name: &str, unit: Option<String>) -> Self {
431 self.add_field(name, DataType::UInt32, unit)
432 }
433
434 pub fn add_u64_field(self, name: &str, unit: Option<String>) -> Self {
435 self.add_field(name, DataType::UInt64, unit)
436 }
437
438 pub fn add_f32_field(self, name: &str, unit: Option<String>) -> Self {
439 self.add_field(name, DataType::Float32, unit)
440 }
441
442 pub fn add_f64_field(self, name: &str, unit: Option<String>) -> Self {
443 self.add_field(name, DataType::Float64, unit)
444 }
445
446 pub fn add_timestamp_ns_field(self, name: &str, unit: Option<String>) -> Self {
447 self.add_field(name, DataType::TimestampNs, unit)
448 }
449
450 pub fn add_binary_field(self, name: &str, unit: Option<String>) -> Self {
451 self.add_field(name, DataType::Binary, unit)
452 }
453
454 pub fn add_string_field(self, name: &str, unit: Option<String>) -> Self {
455 self.add_field(name, DataType::String, unit)
456 }
457
458 pub fn add_bool_field(self, name: &str, unit: Option<String>) -> Self {
459 self.add_field(name, DataType::Boolean, unit)
460 }
461 }
462}
463
464#[cfg(test)]
465mod test {
466 use zelos_trace_types::DataType;
467
468 use super::*;
469
470 #[test]
471 fn test_source_basic() -> Result<()> {
472 let (sender, receiver) = flume::unbounded::<IpcMessageWithId>();
474 let src = TraceSource::new("src", sender.clone());
476 let id = src.id.clone();
477
478 {
480 let m = receiver.recv()?;
481 assert_eq!(m.segment_id, id);
482 assert!(matches!(m.msg, IpcMessage::TraceSegmentStart(_)));
483 }
484
485 let evt = src
487 .build_event("hello")
488 .add_i32_field("sig", None)
489 .build()?;
490
491 {
493 let m = receiver.recv()?;
494 assert_eq!(m.segment_id, id);
495 if let IpcMessage::TraceEventSchema(schema) = &m.msg {
496 assert_eq!(schema.name, "hello");
497 assert_eq!(schema.fields.len(), 1);
498 assert_eq!(schema.fields[0].name, "sig");
499 assert_eq!(schema.fields[0].data_type, DataType::Int32);
500 } else {
501 panic!("Expected TraceEventSchema");
502 }
503 }
504
505 evt.build().try_insert_i32("sig", 10)?.emit()?;
507
508 {
510 let m = receiver.recv()?;
511 assert_eq!(m.segment_id, id);
512 if let IpcMessage::TraceEvent(event) = &m.msg {
513 assert_eq!(event.name, "hello");
514 assert_eq!(event.fields.len(), 1);
515 let data = event.fields.iter().collect::<Vec<_>>();
516 assert_eq!(data[0].0, "sig");
517 assert_eq!(*data[0].1, Value::Int32(10));
518 } else {
519 panic!("Expected TraceEvent");
520 }
521 }
522
523 src.get_event("hello")?
525 .build()
526 .try_insert_i32("sig", 20)?
527 .emit()?;
528
529 {
531 let m = receiver.recv()?;
532 assert_eq!(m.segment_id, id);
533 if let IpcMessage::TraceEvent(event) = &m.msg {
534 assert_eq!(event.name, "hello");
535 assert_eq!(event.fields.len(), 1);
536 let data = event.fields.iter().collect::<Vec<_>>();
537 assert_eq!(data[0].0, "sig");
538 assert_eq!(*data[0].1, Value::Int32(20));
539 } else {
540 panic!("Expected TraceEvent");
541 }
542 }
543
544 drop(src);
546
547 {
549 let m = receiver.recv()?;
550 assert_eq!(m.segment_id, id);
551 assert!(matches!(m.msg, IpcMessage::TraceSegmentEnd(_)));
552 }
553
554 Ok(())
555 }
556}