1use crate::dyn_deps::merge_properties;
3pub use crate::errors::{Error, Result};
4use crate::images::{ImageBlock, ImageEvent, ImageStream};
5use crate::logs::{TaggedLogInteropEvent, TaggedLogStaticStrEvent, TaggedLogString};
6use crate::metrics::{TaggedFloatMetricEvent, TaggedIntegerMetricEvent};
7use crate::prelude::*;
8use crate::property_set::{DynProperty, PropertySet, PropertySetId};
9use crate::{
10 event::{EventBlock, EventSink, EventStream, ExtractDeps, NullEventSink, TracingBlock},
11 info,
12 logs::{
13 LogMetadata, LogStaticStrEvent, LogStaticStrInteropEventV2, LogStream, LogStringEvent,
14 LogStringInteropEvent,
15 },
16 metrics::{FloatMetricEvent, IntegerMetricEvent, MetricsStream, StaticMetricMetadata},
17 spans::{
18 BeginAsyncDynNamedSpanEvent, BeginAsyncNamedSpanEvent, BeginAsyncSpanEvent,
19 BeginThreadDynNamedSpanEvent, BeginThreadNamedSpanEvent, BeginThreadSpanEvent,
20 EndAsyncDynNamedSpanEvent, EndAsyncNamedSpanEvent, EndAsyncSpanEvent,
21 EndThreadDynNamedSpanEvent, EndThreadNamedSpanEvent, EndThreadSpanEvent, SpanLocation,
22 SpanMetadata, ThreadBlock, ThreadEventQueueTypeIndex, ThreadStream,
23 },
24 string_id::DynStringId,
25 warn,
26};
27
28const IMAGE_BUFFER_SIZE: usize = 1024 * 1024;
29use chrono::Utc;
30use std::cell::OnceCell;
31use std::cell::UnsafeCell;
32use std::collections::HashMap;
33use std::fmt;
34use std::sync::RwLock;
35use std::{
36 cell::Cell,
37 sync::{Arc, Mutex, MutexGuard},
38};
39
40pub fn init_event_dispatch(
41 logs_buffer_size: usize,
42 metrics_buffer_size: usize,
43 threads_buffer_size: usize,
44 sink: Arc<dyn EventSink>,
45 process_properties: HashMap<String, String>,
46 cpu_tracing_enabled: bool,
47) -> Result<()> {
48 lazy_static::lazy_static! {
49 static ref INIT_MUTEX: Mutex<()> = Mutex::new(());
50 }
51 let _guard = INIT_MUTEX.lock().unwrap();
52 let dispatch_ref = &G_DISPATCH.inner;
53 unsafe {
54 if (*dispatch_ref.get()).get().is_none() {
55 (*dispatch_ref.get())
56 .set(Dispatch::new(
57 logs_buffer_size,
58 metrics_buffer_size,
59 threads_buffer_size,
60 sink,
61 process_properties,
62 cpu_tracing_enabled,
63 ))
64 .map_err(|_| Error::AlreadyInitialized())
65 } else {
66 info!("event dispatch already initialized");
67 Err(Error::AlreadyInitialized())
68 }
69 }
70}
71
72#[inline]
73pub fn process_id() -> Option<uuid::Uuid> {
74 G_DISPATCH.get().map(Dispatch::get_process_id)
75}
76
77#[inline]
78pub fn cpu_tracing_enabled() -> Option<bool> {
79 G_DISPATCH.get().map(Dispatch::get_cpu_tracing_enabled)
80}
81
82pub fn get_sink() -> Option<Arc<dyn EventSink>> {
83 G_DISPATCH.get().map(Dispatch::get_sink)
84}
85
86pub fn shutdown_dispatch() {
87 G_DISPATCH.get().map(Dispatch::shutdown);
88}
89
90#[inline(always)]
91pub fn int_metric(metric_desc: &'static StaticMetricMetadata, value: u64) {
92 if let Some(d) = G_DISPATCH.get() {
93 d.int_metric(metric_desc, value);
94 }
95}
96
97#[inline(always)]
98pub fn float_metric(metric_desc: &'static StaticMetricMetadata, value: f64) {
99 if let Some(d) = G_DISPATCH.get() {
100 d.float_metric(metric_desc, value);
101 }
102}
103
104#[inline(always)]
105pub fn tagged_float_metric(
106 desc: &'static StaticMetricMetadata,
107 properties: &'static PropertySet,
108 value: f64,
109) {
110 if let Some(d) = G_DISPATCH.get() {
111 d.tagged_float_metric(desc, properties, value);
112 }
113}
114
115#[inline(always)]
116pub fn tagged_integer_metric(
117 desc: &'static StaticMetricMetadata,
118 properties: &'static PropertySet,
119 value: u64,
120) {
121 if let Some(d) = G_DISPATCH.get() {
122 d.tagged_integer_metric(desc, properties, value);
123 }
124}
125
126#[inline(always)]
127pub fn tagged_float_metric_dyn(
128 desc: &'static StaticMetricMetadata,
129 properties: &'static PropertySet,
130 dyn_properties: &[DynProperty<'_>],
131 value: f64,
132) {
133 if let Some(d) = G_DISPATCH.get() {
134 d.tagged_float_metric_dyn(desc, properties, dyn_properties, value);
135 }
136}
137
138#[inline(always)]
139pub fn tagged_integer_metric_dyn(
140 desc: &'static StaticMetricMetadata,
141 properties: &'static PropertySet,
142 dyn_properties: &[DynProperty<'_>],
143 value: u64,
144) {
145 if let Some(d) = G_DISPATCH.get() {
146 d.tagged_integer_metric_dyn(desc, properties, dyn_properties, value);
147 }
148}
149
150#[inline(always)]
152pub fn log_dyn_tagged(
153 desc: &'static LogMetadata,
154 properties: &'static PropertySet,
155 dyn_properties: &[DynProperty<'_>],
156 args: fmt::Arguments<'_>,
157) {
158 if let Some(d) = G_DISPATCH.get() {
159 d.log_dyn_tagged(desc, properties, dyn_properties, args);
160 }
161}
162
163#[doc(hidden)]
165pub struct EnabledLogSink {
166 dispatch: &'static Dispatch,
167 sink: Arc<dyn EventSink>,
168}
169
170#[doc(hidden)]
173#[inline(always)]
174pub fn enabled_log_sink(desc: &LogMetadata) -> Option<EnabledLogSink> {
175 let dispatch = G_DISPATCH.get()?;
176 let sink = dispatch.get_sink();
177 if sink.on_log_enabled(desc) {
178 Some(EnabledLogSink { dispatch, sink })
179 } else {
180 None
181 }
182}
183
184#[doc(hidden)]
185#[inline(always)]
186pub fn log_dyn_tagged_enabled(
187 sink: EnabledLogSink,
188 desc: &'static LogMetadata,
189 properties: &'static PropertySet,
190 dyn_properties: &[DynProperty<'_>],
191 args: fmt::Arguments<'_>,
192) {
193 sink.dispatch
194 .log_dyn_tagged_to_sink(&*sink.sink, desc, properties, dyn_properties, args);
195}
196
197#[inline(always)]
198pub fn log_interop_dyn_tagged(
199 metadata: &LogMetadata,
200 dyn_properties: &[DynProperty<'_>],
201 args: fmt::Arguments<'_>,
202) {
203 if let Some(d) = G_DISPATCH.get() {
204 d.log_interop_dyn_tagged(metadata, dyn_properties, args);
205 }
206}
207
208#[inline(always)]
209pub fn log(desc: &'static LogMetadata, args: fmt::Arguments<'_>) {
210 if let Some(d) = G_DISPATCH.get() {
211 d.log(desc, args);
212 }
213}
214
215#[inline(always)]
217pub fn log_tagged(
218 desc: &'static LogMetadata,
219 properties: &'static PropertySet,
220 args: fmt::Arguments<'_>,
221) {
222 if let Some(d) = G_DISPATCH.get() {
223 d.log_tagged(desc, properties, args);
224 }
225}
226
227#[inline(always)]
228pub fn log_interop(metadata: &LogMetadata, args: fmt::Arguments<'_>) {
229 if let Some(d) = G_DISPATCH.get() {
230 d.log_interop(metadata, args);
231 }
232}
233
234#[inline(always)]
235pub fn log_enabled(metadata: &LogMetadata) -> bool {
236 if let Some(d) = G_DISPATCH.get() {
237 d.log_enabled(metadata)
238 } else {
239 false
240 }
241}
242
243#[inline(always)]
244pub fn flush_log_buffer() {
245 if let Some(d) = G_DISPATCH.get() {
246 d.flush_log_buffer();
247 }
248}
249
250pub fn send_image(name: &str, format: &str, data: Vec<u8>) {
251 if let Some(d) = G_DISPATCH.get() {
252 d.send_image(name, format, data);
253 }
254}
255
256#[inline(always)]
257pub fn flush_image_buffer() {
258 if let Some(d) = G_DISPATCH.get() {
259 d.flush_image_buffer();
260 }
261}
262
263#[inline(always)]
264pub fn flush_metrics_buffer() {
265 if let Some(d) = G_DISPATCH.get() {
266 d.flush_metrics_buffer();
267 }
268}
269
270#[inline(always)]
273pub fn init_thread_stream() {
274 LOCAL_THREAD_STREAM.with(|cell| unsafe {
275 if (*cell.as_ptr()).is_some() {
276 return;
277 }
278 #[allow(static_mut_refs)]
279 if let Some(d) = G_DISPATCH.get() {
280 if !d.cpu_tracing_enabled {
282 return;
283 }
284 d.init_thread_stream(cell);
285 } else {
286 warn!("dispatch not initialized, cannot init thread stream, events will be lost for this thread");
287 }
288 });
289}
290
291pub fn for_each_thread_stream(fun: &mut dyn FnMut(*mut ThreadStream)) {
292 if let Some(d) = G_DISPATCH.get() {
293 d.for_each_thread_stream(fun);
294 }
295}
296
297#[inline(always)]
298pub fn flush_thread_buffer() {
299 LOCAL_THREAD_STREAM.with(|cell| unsafe {
300 let opt_stream = &mut *cell.as_ptr();
301 if let Some(stream) = opt_stream {
302 #[allow(static_mut_refs)]
303 match G_DISPATCH.get() {
304 Some(d) => {
305 d.flush_thread_buffer(stream);
306 }
307 None => {
308 panic!("threads are recording but there is no event dispatch");
309 }
310 }
311 }
312 });
313}
314
315#[inline(always)]
318pub fn unregister_thread_stream() {
319 LOCAL_THREAD_STREAM.with(|cell| unsafe {
320 let opt_stream = &mut *cell.as_ptr();
321 if let Some(stream) = opt_stream {
322 #[allow(static_mut_refs)]
323 match G_DISPATCH.get() {
324 Some(d) => {
325 d.flush_thread_buffer(stream);
327 d.unregister_thread_stream(stream);
329 *opt_stream = None;
331 }
332 None => {
333 *opt_stream = None;
335 }
336 }
337 }
338 });
339}
340
341#[inline(always)]
342pub fn on_begin_scope(scope: &'static SpanMetadata) {
343 on_thread_event(BeginThreadSpanEvent {
344 time: now(),
345 thread_span_desc: scope,
346 });
347}
348
349#[inline(always)]
350pub fn on_end_scope(scope: &'static SpanMetadata) {
351 on_thread_event(EndThreadSpanEvent {
352 time: now(),
353 thread_span_desc: scope,
354 });
355}
356
357#[inline(always)]
358pub fn on_begin_named_scope(thread_span_location: &'static SpanLocation, name: &'static str) {
359 on_thread_event(BeginThreadNamedSpanEvent {
360 thread_span_location,
361 name: name.into(),
362 time: now(),
363 });
364}
365
366#[inline(always)]
367pub fn on_end_named_scope(thread_span_location: &'static SpanLocation, name: &'static str) {
368 on_thread_event(EndThreadNamedSpanEvent {
369 thread_span_location,
370 name: name.into(),
371 time: now(),
372 });
373}
374
375#[inline(always)]
376pub fn on_begin_async_scope(scope: &'static SpanMetadata, parent_span_id: u64, depth: u32) -> u64 {
377 let id = G_ASYNC_SPAN_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
378 on_thread_event(BeginAsyncSpanEvent {
379 span_desc: scope,
380 span_id: id as u64,
381 parent_span_id,
382 depth,
383 time: now(),
384 });
385 id as u64
386}
387
388#[inline(always)]
389pub fn on_end_async_scope(
390 span_id: u64,
391 parent_span_id: u64,
392 scope: &'static SpanMetadata,
393 depth: u32,
394) {
395 on_thread_event(EndAsyncSpanEvent {
396 span_desc: scope,
397 span_id,
398 parent_span_id,
399 depth,
400 time: now(),
401 });
402}
403
404#[inline(always)]
405pub fn on_begin_async_named_scope(
406 span_location: &'static SpanLocation,
407 name: &'static str,
408 parent_span_id: u64,
409 depth: u32,
410) -> u64 {
411 let id = G_ASYNC_SPAN_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
412 on_thread_event(BeginAsyncNamedSpanEvent {
413 span_location,
414 name: name.into(),
415 span_id: id as u64,
416 parent_span_id,
417 depth,
418 time: now(),
419 });
420 id as u64
421}
422
423#[inline(always)]
424pub fn on_end_async_named_scope(
425 span_id: u64,
426 parent_span_id: u64,
427 span_location: &'static SpanLocation,
428 name: &'static str,
429 depth: u32,
430) {
431 on_thread_event(EndAsyncNamedSpanEvent {
432 span_location,
433 name: name.into(),
434 span_id,
435 parent_span_id,
436 depth,
437 time: now(),
438 });
439}
440
441#[inline(always)]
444pub fn on_begin_named_scope_dyn(thread_span_location: &'static SpanLocation, name: &str) {
445 on_thread_dyn_named_event(name, |name| BeginThreadDynNamedSpanEvent {
446 thread_span_location,
447 name,
448 time: now(),
449 });
450}
451
452#[inline(always)]
455pub fn on_end_named_scope_dyn(thread_span_location: &'static SpanLocation, name: &str) {
456 let time = now();
457 on_thread_dyn_named_event(name, |name| EndThreadDynNamedSpanEvent {
458 thread_span_location,
459 name,
460 time,
461 });
462}
463
464#[inline(always)]
467pub fn on_begin_async_named_scope_dyn(
468 span_location: &'static SpanLocation,
469 name: &str,
470 parent_span_id: u64,
471 depth: u32,
472) -> u64 {
473 let id = G_ASYNC_SPAN_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
474 on_thread_dyn_named_event(name, |name| BeginAsyncDynNamedSpanEvent {
475 span_location,
476 name,
477 span_id: id as u64,
478 parent_span_id,
479 depth,
480 time: now(),
481 });
482 id as u64
483}
484
485#[inline(always)]
489pub fn on_end_async_named_scope_dyn(
490 span_id: u64,
491 parent_span_id: u64,
492 span_location: &'static SpanLocation,
493 name: &str,
494 depth: u32,
495) {
496 let time = now();
497 on_thread_dyn_named_event(name, |name| EndAsyncDynNamedSpanEvent {
498 span_location,
499 name,
500 span_id,
501 parent_span_id,
502 depth,
503 time,
504 });
505}
506
507pub struct DispatchCell {
508 inner: UnsafeCell<OnceCell<Dispatch>>,
510}
511
512impl DispatchCell {
513 const fn new() -> Self {
514 Self {
515 inner: UnsafeCell::new(OnceCell::new()),
516 }
517 }
518
519 fn get(&self) -> Option<&Dispatch> {
520 unsafe { (*self.inner.get()).get() }
521 }
522
523 unsafe fn take(&self) -> Option<Dispatch> {
524 unsafe { (*self.inner.get()).take() }
525 }
526}
527
528unsafe impl Sync for DispatchCell {}
530
531static G_DISPATCH: DispatchCell = DispatchCell::new();
532static G_ASYNC_SPAN_COUNTER: std::sync::atomic::AtomicUsize =
533 std::sync::atomic::AtomicUsize::new(1);
534
535pub unsafe fn force_uninit() {
539 unsafe {
540 G_DISPATCH.take();
541 }
542}
543
544thread_local! {
545 static LOCAL_THREAD_STREAM: Cell<Option<ThreadStream>> = const { Cell::new(None) };
546}
547
548#[inline(always)]
549fn on_thread_event<T>(event: T)
550where
551 T: micromegas_transit::InProcSerialize + ThreadEventQueueTypeIndex,
552{
553 LOCAL_THREAD_STREAM.with(|cell| unsafe {
554 let opt_stream = &mut *cell.as_ptr();
555 if let Some(stream) = opt_stream {
556 stream.get_events_mut().push(event);
557 if stream.is_full() {
558 flush_thread_buffer();
559 }
560 }
561 });
562}
563
564#[inline(always)]
565fn on_thread_dyn_named_event<T>(name: &str, make: impl FnOnce(DynStringId) -> T)
566where
567 T: micromegas_transit::InProcSerialize + ThreadEventQueueTypeIndex,
568{
569 LOCAL_THREAD_STREAM.with(|cell| unsafe {
570 let opt_stream = &mut *cell.as_ptr();
571 if let Some(stream) = opt_stream {
572 stream.push_dyn_named(name, make);
573 if stream.is_full() {
574 flush_thread_buffer();
575 }
576 }
577 });
578}
579
580struct Dispatch {
581 process_id: uuid::Uuid,
582 logs_buffer_size: usize,
583 metrics_buffer_size: usize,
584 threads_buffer_size: usize,
585 cpu_tracing_enabled: bool,
586 image_stream: Mutex<ImageStream>,
587 log_stream: Mutex<LogStream>,
588 metrics_stream: Mutex<MetricsStream>,
589 thread_streams: Mutex<Vec<*mut ThreadStream>>, sink: RwLock<Arc<dyn EventSink>>,
591}
592
593#[inline(always)]
595fn is_static_msg(desc: &LogMetadata, args: &fmt::Arguments<'_>) -> bool {
596 args.as_str() == Some(desc.fmt_str)
597}
598
599impl Dispatch {
600 pub fn new(
601 logs_buffer_size: usize,
602 metrics_buffer_size: usize,
603 threads_buffer_size: usize,
604 sink: Arc<dyn EventSink>,
605 process_properties: HashMap<String, String>,
606 cpu_tracing_enabled: bool,
607 ) -> Self {
608 let process_id = uuid::Uuid::new_v4();
609 let obj = Self {
610 process_id,
611 logs_buffer_size,
612 metrics_buffer_size,
613 threads_buffer_size,
614 cpu_tracing_enabled,
615 image_stream: Mutex::new(ImageStream::new(
616 IMAGE_BUFFER_SIZE,
617 process_id,
618 &[String::from("image")],
619 HashMap::new(),
620 )),
621 log_stream: Mutex::new(LogStream::new(
622 logs_buffer_size,
623 process_id,
624 &[String::from("log")],
625 HashMap::new(),
626 )),
627 metrics_stream: Mutex::new(MetricsStream::new(
628 metrics_buffer_size,
629 process_id,
630 &[String::from("metrics")],
631 HashMap::new(),
632 )),
633 thread_streams: Mutex::new(vec![]),
634 sink: RwLock::new(sink),
635 };
636 obj.startup(process_properties);
637 obj.init_image_stream();
638 obj.init_log_stream();
639 obj.init_metrics_stream();
640 obj
641 }
642
643 pub fn get_process_id(&self) -> uuid::Uuid {
644 self.process_id
645 }
646
647 pub fn get_cpu_tracing_enabled(&self) -> bool {
648 self.cpu_tracing_enabled
649 }
650
651 pub fn get_sink(&self) -> Arc<dyn EventSink> {
652 if let Ok(guard) = self.sink.try_read() {
653 (*guard).clone()
654 } else {
655 Arc::new(NullEventSink {})
656 }
657 }
658
659 fn shutdown(&self) {
660 let old_sink = self.get_sink();
661 let null_sink = Arc::new(NullEventSink {});
662 if let Ok(mut guard) = self.sink.write() {
663 *guard = null_sink;
664 drop(guard)
665 }
666 old_sink.on_shutdown();
667 }
668
669 fn startup(&self, process_properties: HashMap<String, String>) {
670 let mut parent_process = None;
671
672 if let Ok(parent_process_guid) = std::env::var("MICROMEGAS_TELEMETRY_PARENT_PROCESS")
673 && let Ok(parent_process_id) = uuid::Uuid::try_parse(&parent_process_guid)
674 {
675 parent_process = Some(parent_process_id);
676 }
677
678 unsafe {
679 std::env::set_var(
680 "MICROMEGAS_TELEMETRY_PARENT_PROCESS",
681 self.process_id.to_string(),
682 );
683 }
684
685 let process_info = Arc::new(make_process_info(
686 self.process_id,
687 parent_process,
688 process_properties,
689 ));
690
691 self.get_sink().on_startup(process_info);
692 }
693
694 fn init_log_stream(&self) {
695 let log_stream = self.log_stream.lock().unwrap();
696 self.get_sink().on_init_log_stream(&log_stream);
697 }
698
699 fn init_image_stream(&self) {
700 let image_stream = self.image_stream.lock().expect("image_stream lock");
701 self.get_sink().on_init_image_stream(&image_stream);
702 }
703
704 fn init_metrics_stream(&self) {
705 let metrics_stream = self.metrics_stream.lock().unwrap();
706 self.get_sink().on_init_metrics_stream(&metrics_stream);
707 }
708
709 pub fn send_image(&self, name: &str, format: &str, data: Vec<u8>) {
710 let time = now();
711 let mut image_stream = self.image_stream.lock().expect("image_stream lock");
712 image_stream.get_events_mut().push(ImageEvent {
713 time,
714 name: micromegas_transit::DynString(name.to_owned()),
715 format: micromegas_transit::DynString(format.to_owned()),
716 data: micromegas_transit::DynBlob(data),
717 });
718 if image_stream.is_full() {
719 drop(image_stream);
720 self.flush_image_buffer();
721 }
722 }
723
724 fn flush_image_buffer(&self) {
725 let mut image_stream = self.image_stream.lock().expect("image_stream lock");
726 if image_stream.is_empty() {
727 return;
728 }
729 let stream_id = image_stream.stream_id();
730 let next_offset = image_stream.get_block_ref().object_offset()
731 + image_stream.get_block_ref().nb_objects();
732 let new_block = Arc::new(ImageBlock::new(
733 IMAGE_BUFFER_SIZE,
734 self.process_id,
735 stream_id,
736 next_offset,
737 ));
738 let begin = new_block.begin;
739 let mut old_event_block = image_stream.replace_block(new_block);
740 assert!(!image_stream.is_full());
741 Arc::get_mut(&mut old_event_block)
742 .expect("image block exclusive ref")
743 .close_at(begin);
744 drop(image_stream);
745 self.get_sink().on_process_image_block(old_event_block);
746 }
747
748 fn init_thread_stream(&self, cell: &Cell<Option<ThreadStream>>) {
749 if !self.cpu_tracing_enabled {
751 return;
752 }
753
754 let mut properties = HashMap::new();
755 properties.insert(String::from("thread-id"), thread_id::get().to_string());
756 if let Some(name) = std::thread::current().name() {
757 properties.insert("thread-name".to_owned(), name.to_owned());
758 }
759 let thread_stream = ThreadStream::new(
760 self.threads_buffer_size,
761 self.process_id,
762 &["cpu".to_owned()],
763 properties,
764 );
765 unsafe {
766 let opt_ref = &mut *cell.as_ptr();
767 self.get_sink().on_init_thread_stream(&thread_stream);
768 *opt_ref = Some(thread_stream);
769 let mut vec_guard = self.thread_streams.lock().unwrap();
770 vec_guard.push(opt_ref.as_mut().unwrap());
771 }
772 }
773
774 fn for_each_thread_stream(&self, fun: &mut dyn FnMut(*mut ThreadStream)) {
775 let mut vec_guard = self.thread_streams.lock().unwrap();
776 for stream in &mut *vec_guard {
777 fun(*stream);
778 }
779 }
780
781 fn unregister_thread_stream(&self, stream_to_remove: &mut ThreadStream) {
782 let mut vec_guard = self.thread_streams.lock().unwrap();
783 let stream_ptr = stream_to_remove as *mut ThreadStream;
784
785 if let Some(pos) = vec_guard.iter().position(|&ptr| ptr == stream_ptr) {
787 vec_guard.remove(pos);
788 }
789 }
790
791 #[inline]
792 fn int_metric(&self, desc: &'static StaticMetricMetadata, value: u64) {
793 let time = now();
794 let mut metrics_stream = self.metrics_stream.lock().unwrap();
795 metrics_stream
796 .get_events_mut()
797 .push(IntegerMetricEvent { desc, value, time });
798 self.on_metrics_pushed(metrics_stream);
799 }
800
801 #[inline]
802 fn float_metric(&self, desc: &'static StaticMetricMetadata, value: f64) {
803 let time = now();
804 let mut metrics_stream = self.metrics_stream.lock().unwrap();
805 metrics_stream
806 .get_events_mut()
807 .push(FloatMetricEvent { desc, value, time });
808 self.on_metrics_pushed(metrics_stream);
809 }
810
811 #[inline]
812 fn tagged_float_metric(
813 &self,
814 desc: &'static StaticMetricMetadata,
815 properties: &'static PropertySet,
816 value: f64,
817 ) {
818 let time = now();
819 let mut metrics_stream = self.metrics_stream.lock().unwrap();
820 metrics_stream
821 .get_events_mut()
822 .push(TaggedFloatMetricEvent {
823 desc,
824 properties: PropertySetId::interned(properties),
825 value,
826 time,
827 });
828 self.on_metrics_pushed(metrics_stream);
829 }
830
831 #[inline]
832 fn tagged_integer_metric(
833 &self,
834 desc: &'static StaticMetricMetadata,
835 properties: &'static PropertySet,
836 value: u64,
837 ) {
838 let time = now();
839 let mut metrics_stream = self.metrics_stream.lock().unwrap();
840 metrics_stream
841 .get_events_mut()
842 .push(TaggedIntegerMetricEvent {
843 desc,
844 properties: PropertySetId::interned(properties),
845 value,
846 time,
847 });
848 self.on_metrics_pushed(metrics_stream);
849 }
850
851 #[inline]
852 fn tagged_float_metric_dyn(
853 &self,
854 desc: &'static StaticMetricMetadata,
855 properties: &'static PropertySet,
856 dyn_properties: &[DynProperty<'_>],
857 value: f64,
858 ) {
859 if dyn_properties.is_empty() {
860 return self.tagged_float_metric(desc, properties, value);
861 }
862 let time = now();
863 let merged = merge_properties(properties, dyn_properties);
864 let mut metrics_stream = self.metrics_stream.lock().unwrap();
865 let (events, dyn_deps) = metrics_stream.get_block_mut_parts();
866 let properties = dyn_deps.intern_set(&merged);
867 events.push(TaggedFloatMetricEvent {
868 desc,
869 properties,
870 value,
871 time,
872 });
873 self.on_metrics_pushed(metrics_stream);
874 }
875
876 #[inline]
877 fn tagged_integer_metric_dyn(
878 &self,
879 desc: &'static StaticMetricMetadata,
880 properties: &'static PropertySet,
881 dyn_properties: &[DynProperty<'_>],
882 value: u64,
883 ) {
884 if dyn_properties.is_empty() {
885 return self.tagged_integer_metric(desc, properties, value);
886 }
887 let time = now();
888 let merged = merge_properties(properties, dyn_properties);
889 let mut metrics_stream = self.metrics_stream.lock().unwrap();
890 let (events, dyn_deps) = metrics_stream.get_block_mut_parts();
891 let properties = dyn_deps.intern_set(&merged);
892 events.push(TaggedIntegerMetricEvent {
893 desc,
894 properties,
895 value,
896 time,
897 });
898 self.on_metrics_pushed(metrics_stream);
899 }
900
901 #[inline]
902 fn on_metrics_pushed(&self, mut metrics_stream: MutexGuard<'_, MetricsStream>) {
903 if !metrics_stream.is_full() {
904 return;
905 }
906 let old_block = swap_block(
909 &mut metrics_stream,
910 self.metrics_buffer_size,
911 self.process_id,
912 );
913 drop(metrics_stream);
914 self.get_sink().on_process_metrics_block(old_block);
915 }
916
917 #[inline]
918 fn flush_metrics_buffer(&self) {
919 let mut metrics_stream = self.metrics_stream.lock().unwrap();
920 if metrics_stream.is_empty() {
921 return;
922 }
923 let old_block = swap_block(
924 &mut metrics_stream,
925 self.metrics_buffer_size,
926 self.process_id,
927 );
928 drop(metrics_stream);
929 self.get_sink().on_process_metrics_block(old_block);
930 }
931
932 fn log_enabled(&self, metadata: &LogMetadata) -> bool {
933 self.get_sink().on_log_enabled(metadata)
934 }
935
936 #[inline]
937 fn log(&self, metadata: &'static LogMetadata, args: fmt::Arguments<'_>) {
938 let sink = self.get_sink();
939 if !sink.on_log_enabled(metadata) {
940 return;
941 }
942 let time = now();
943 sink.on_log(metadata, &[], time, args);
944 let msg = if is_static_msg(metadata, &args) {
945 None
946 } else {
947 Some(micromegas_transit::DynString(args.to_string()))
948 };
949 let mut log_stream = self.log_stream.lock().unwrap();
950 match msg {
951 None => log_stream.get_events_mut().push(LogStaticStrEvent {
952 desc: metadata,
953 time,
954 }),
955 Some(msg) => log_stream.get_events_mut().push(LogStringEvent {
956 desc: metadata,
957 time,
958 msg,
959 }),
960 }
961 self.on_log_pushed(log_stream);
962 }
963
964 #[inline]
965 fn log_tagged(
966 &self,
967 desc: &'static LogMetadata,
968 properties: &'static PropertySet,
969 args: fmt::Arguments<'_>,
970 ) {
971 let sink = self.get_sink();
972 if !sink.on_log_enabled(desc) {
973 return;
974 }
975 self.log_tagged_to_sink(&*sink, desc, properties, args);
976 }
977
978 #[inline]
979 fn log_tagged_to_sink(
980 &self,
981 sink: &dyn EventSink,
982 desc: &'static LogMetadata,
983 properties: &'static PropertySet,
984 args: fmt::Arguments<'_>,
985 ) {
986 let time = now();
987 sink.on_log(desc, properties.dyn_properties(), time, args);
988 let properties = PropertySetId::interned(properties);
989 let msg = if is_static_msg(desc, &args) {
990 None
991 } else {
992 Some(micromegas_transit::DynString(args.to_string()))
993 };
994 let mut log_stream = self.log_stream.lock().unwrap();
995 match msg {
996 None => log_stream.get_events_mut().push(TaggedLogStaticStrEvent {
997 desc,
998 properties,
999 time,
1000 }),
1001 Some(msg) => log_stream.get_events_mut().push(TaggedLogString {
1002 desc,
1003 properties,
1004 time,
1005 msg,
1006 }),
1007 }
1008 self.on_log_pushed(log_stream);
1009 }
1010
1011 #[inline]
1012 fn log_interop(&self, desc: &LogMetadata, args: fmt::Arguments<'_>) {
1013 let time = now();
1014 self.get_sink().on_log(desc, &[], time, args);
1015 let msg = args
1016 .as_str()
1017 .ok_or_else(|| micromegas_transit::DynString(args.to_string()));
1018 let mut log_stream = self.log_stream.lock().unwrap();
1019 let (events, dyn_deps) = log_stream.get_block_mut_parts();
1020 match msg {
1021 Ok(static_msg) => {
1022 let id = dyn_deps.intern_str(desc.target);
1023 events.push(LogStaticStrInteropEventV2 {
1024 time,
1025 level: desc.level as u32,
1026 target: DynStringId { id },
1027 msg: static_msg.into(),
1028 });
1029 }
1030 Err(msg) => {
1031 let target = dyn_deps.intern_str_ref(desc.target);
1032 events.push(LogStringInteropEvent {
1033 time,
1034 level: desc.level as u8,
1035 target,
1036 msg,
1037 });
1038 }
1039 }
1040 self.on_log_pushed(log_stream);
1041 }
1042
1043 #[inline]
1044 fn log_dyn_tagged(
1045 &self,
1046 desc: &'static LogMetadata,
1047 properties: &'static PropertySet,
1048 dyn_properties: &[DynProperty<'_>],
1049 args: fmt::Arguments<'_>,
1050 ) {
1051 let sink = self.get_sink();
1052 if !sink.on_log_enabled(desc) {
1053 return;
1054 }
1055 self.log_dyn_tagged_to_sink(&*sink, desc, properties, dyn_properties, args);
1056 }
1057
1058 #[inline]
1059 fn log_dyn_tagged_to_sink(
1060 &self,
1061 sink: &dyn EventSink,
1062 desc: &'static LogMetadata,
1063 properties: &'static PropertySet,
1064 dyn_properties: &[DynProperty<'_>],
1065 args: fmt::Arguments<'_>,
1066 ) {
1067 if dyn_properties.is_empty() {
1068 return self.log_tagged_to_sink(sink, desc, properties, args);
1069 }
1070 let time = now();
1071 let merged = merge_properties(properties, dyn_properties);
1072 sink.on_log(desc, &merged.properties, time, args);
1074 let msg = if is_static_msg(desc, &args) {
1075 None
1076 } else {
1077 Some(micromegas_transit::DynString(args.to_string()))
1078 };
1079 let mut log_stream = self.log_stream.lock().unwrap();
1080 let (events, dyn_deps) = log_stream.get_block_mut_parts();
1081 let properties = dyn_deps.intern_set(&merged);
1082 match msg {
1083 None => events.push(TaggedLogStaticStrEvent {
1084 desc,
1085 properties,
1086 time,
1087 }),
1088 Some(msg) => events.push(TaggedLogString {
1089 desc,
1090 properties,
1091 time,
1092 msg,
1093 }),
1094 }
1095 self.on_log_pushed(log_stream);
1096 }
1097
1098 #[inline]
1099 fn log_interop_dyn_tagged(
1100 &self,
1101 desc: &LogMetadata,
1102 dyn_properties: &[DynProperty<'_>],
1103 args: fmt::Arguments<'_>,
1104 ) {
1105 let time = now();
1106 let merged = merge_properties(PropertySet::empty(), dyn_properties);
1107 let is_override = |p: &DynProperty<'_>| {
1109 p.name().eq_ignore_ascii_case("target") || p.name().eq_ignore_ascii_case("level")
1110 };
1111 if merged.properties.iter().any(is_override) {
1112 let filtered: Vec<DynProperty<'_>> = merged
1113 .properties
1114 .iter()
1115 .copied()
1116 .filter(|p| !is_override(p))
1117 .collect();
1118 self.get_sink().on_log(desc, &filtered, time, args);
1119 } else {
1120 self.get_sink().on_log(desc, &merged.properties, time, args);
1121 }
1122 let msg = micromegas_transit::DynString(args.to_string());
1123 let mut log_stream = self.log_stream.lock().unwrap();
1124 let (events, dyn_deps) = log_stream.get_block_mut_parts();
1125 let target = dyn_deps.intern_str_ref(desc.target);
1126 let properties = dyn_deps.intern_set(&merged);
1127 events.push(TaggedLogInteropEvent {
1128 time,
1129 level: desc.level as u8,
1130 target,
1131 properties,
1132 msg,
1133 });
1134 self.on_log_pushed(log_stream);
1135 }
1136
1137 #[inline]
1138 fn on_log_pushed(&self, mut log_stream: MutexGuard<'_, LogStream>) {
1139 if !log_stream.is_full() {
1140 return;
1141 }
1142 let old_block = swap_block(&mut log_stream, self.logs_buffer_size, self.process_id);
1143 drop(log_stream);
1144 self.get_sink().on_process_log_block(old_block);
1145 }
1146
1147 #[inline]
1148 fn flush_log_buffer(&self) {
1149 let mut log_stream = self.log_stream.lock().unwrap();
1150 if log_stream.is_empty() {
1151 return;
1152 }
1153 let old_block = swap_block(&mut log_stream, self.logs_buffer_size, self.process_id);
1154 drop(log_stream);
1155 self.get_sink().on_process_log_block(old_block);
1156 }
1157
1158 #[inline]
1159 fn flush_thread_buffer(&self, stream: &mut ThreadStream) {
1160 if stream.is_empty() {
1161 return;
1162 }
1163 let next_offset =
1164 stream.get_block_ref().object_offset() + stream.get_block_ref().nb_objects();
1165 let new_block = Arc::new(ThreadBlock::new(
1166 self.threads_buffer_size,
1167 self.process_id,
1168 stream.stream_id(),
1169 next_offset,
1170 ));
1171 let begin = new_block.begin;
1172 let mut old_block = stream.replace_block(new_block);
1173 assert!(!stream.is_full());
1174 Arc::get_mut(&mut old_block).unwrap().close_at(begin);
1175 self.get_sink().on_process_thread_block(old_block);
1176 }
1177}
1178
1179fn swap_block<Q>(
1184 stream: &mut EventStream<EventBlock<Q>>,
1185 buffer_size: usize,
1186 process_id: uuid::Uuid,
1187) -> Arc<EventBlock<Q>>
1188where
1189 Q: micromegas_transit::HeterogeneousQueue + ExtractDeps,
1190 EventBlock<Q>: TracingBlock,
1191{
1192 let stream_id = stream.stream_id();
1193 let next_offset = stream.get_block_ref().object_offset() + stream.get_block_ref().nb_objects();
1194 let new_block = Arc::new(EventBlock::<Q>::new(
1195 buffer_size,
1196 process_id,
1197 stream_id,
1198 next_offset,
1199 ));
1200 let begin = new_block.begin;
1201 let mut old_block = stream.replace_block(new_block);
1202 assert!(!stream.is_full());
1203 Arc::get_mut(&mut old_block).unwrap().close_at(begin);
1204 old_block
1205}
1206
1207fn get_cpu_brand() -> String {
1208 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1209 return raw_cpuid::CpuId::new()
1210 .get_processor_brand_string()
1211 .map_or_else(|| "unknown".to_owned(), |b| b.as_str().to_owned());
1212 #[cfg(target_arch = "aarch64")]
1213 return String::from("aarch64");
1214}
1215
1216pub fn make_process_info(
1217 process_id: uuid::Uuid,
1218 parent_process_id: Option<uuid::Uuid>,
1219 properties: HashMap<String, String>,
1220) -> ProcessInfo {
1221 let start_ticks = now();
1222 let start_time = Utc::now();
1223 let cpu_brand = get_cpu_brand();
1224 ProcessInfo {
1225 process_id,
1226 username: whoami::username(),
1227 realname: whoami::realname(),
1228 exe: std::env::current_exe()
1229 .unwrap_or_default()
1230 .to_string_lossy()
1231 .into_owned(),
1232 computer: whoami::devicename(),
1233 distro: whoami::distro(),
1234 cpu_brand,
1235 tsc_frequency: frequency(),
1236 start_time,
1237 start_ticks,
1238 parent_process_id,
1239 properties,
1240 }
1241}