1#![allow(non_snake_case)]
19
20use serde::{Deserialize, Serialize};
92use std::cell::RefCell;
93use std::collections::HashMap as FxHashMap;
94use std::sync::{Arc, RwLock};
95use std::time::{Duration, SystemTime, UNIX_EPOCH};
96use uuid::Uuid;
97
98use crate::core::RiResult;
99use crate::core::RiError;
100use crate::core::lock::RwLockExtensions;
101
102#[cfg(feature = "pyo3")]
103use pyo3::prelude::*;
104
105#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
107pub struct RiSpanId(String);
108
109impl Default for RiSpanId {
110 fn default() -> Self {
111 Self::new()
112 }
113}
114
115impl RiSpanId {
116 pub fn new() -> Self {
117 Self(Uuid::new_v4().to_string())
118 }
119
120 pub fn from_string(s: String) -> Self {
121 Self(s)
122 }
123
124 pub fn as_str(&self) -> &str {
125 &self.0
126 }
127}
128
129#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
131pub struct RiTraceId(String);
132
133impl Default for RiTraceId {
134 fn default() -> Self {
135 Self::new()
136 }
137}
138
139impl RiTraceId {
140 pub fn new() -> Self {
141 Self(Uuid::new_v4().to_string())
142 }
143
144 pub fn from_string(s: String) -> Self {
145 Self(s)
146 }
147
148 pub fn as_str(&self) -> &str {
149 &self.0
150 }
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
155pub enum RiSpanKind {
156 Server,
157 Client,
158 Producer,
159 Consumer,
160 Internal,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
165pub enum RiSpanStatus {
166 Ok,
167 Error(String),
168 Unset,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct RiSpan {
174 pub trace_id: RiTraceId,
175 pub span_id: RiSpanId,
176 pub parent_span_id: Option<RiSpanId>,
177 pub name: String,
178 pub kind: RiSpanKind,
179 pub start_time: u64, pub end_time: Option<u64>,
181 pub attributes: FxHashMap<String, String>,
182 pub events: Vec<RiSpanEvent>,
183 pub status: RiSpanStatus,
184}
185
186impl RiSpan {
187 pub fn new(
188 trace_id: RiTraceId,
189 parent_span_id: Option<RiSpanId>,
190 name: String,
191 kind: RiSpanKind,
192 ) -> Self {
193 let start_time = SystemTime::now()
194 .duration_since(UNIX_EPOCH)
195 .unwrap_or(Duration::from_secs(0))
196 .as_micros() as u64;
197
198 Self {
199 trace_id,
200 span_id: RiSpanId::new(),
201 parent_span_id,
202 name,
203 kind,
204 start_time,
205 end_time: None,
206 attributes: FxHashMap::default(),
207 events: Vec::new(),
208 status: RiSpanStatus::Unset,
209 }
210 }
211
212 pub fn set_attribute(&mut self, key: String, value: String) {
213 let safe_value = if Self::is_sensitive_attribute(&key) {
215 Self::mask_sensitive_value(&value)
216 } else {
217 value
218 };
219 self.attributes.insert(key, safe_value);
220 }
221
222 fn is_sensitive_attribute(key: &str) -> bool {
229 let key_lower = key.to_lowercase();
230 let sensitive_patterns = [
231 "password",
232 "passwd",
233 "secret",
234 "key",
235 "token",
236 "auth",
237 "credential",
238 "api_key",
239 "apikey",
240 "private",
241 "session",
242 "cookie",
243 "authorization",
244 "bearer",
245 ];
246
247 for pattern in &sensitive_patterns {
248 if key_lower.contains(pattern) {
249 return true;
250 }
251 }
252 false
253 }
254
255 fn mask_sensitive_value(value: &str) -> String {
261 if value.len() <= 4 {
262 return "*".repeat(value.len().max(4));
263 }
264
265 let first_chars = &value[..2];
266 let last_chars = &value[value.len()-2..];
267 let middle_len = value.len() - 4;
268
269 format!("{}{}{}", first_chars, "*".repeat(middle_len), last_chars)
270 }
271
272 pub fn add_event(&mut self, name: String, attributes: FxHashMap<String, String>) {
273 let timestamp = SystemTime::now()
274 .duration_since(UNIX_EPOCH)
275 .unwrap_or(Duration::from_secs(0))
276 .as_micros() as u64;
277
278 self.events.push(RiSpanEvent {
279 name,
280 timestamp,
281 attributes,
282 });
283 }
284
285 pub fn end(&mut self, status: RiSpanStatus) {
286 let end_time = SystemTime::now()
287 .duration_since(UNIX_EPOCH)
288 .unwrap_or(Duration::from_secs(0))
289 .as_micros() as u64;
290
291 self.end_time = Some(end_time);
292 self.status = status;
293 }
294
295 pub fn duration(&self) -> Option<Duration> {
296 if let Some(end_time) = self.end_time {
297 let duration_micros = end_time.saturating_sub(self.start_time);
298 Some(Duration::from_micros(duration_micros))
299 } else {
300 None
301 }
302 }
303}
304
305#[derive(Debug, Clone, Serialize, Deserialize)]
307pub struct RiSpanEvent {
308 pub name: String,
309 pub timestamp: u64, pub attributes: FxHashMap<String, String>,
311}
312
313#[derive(Debug, Clone)]
315pub struct RiTracingContext {
316 current_trace_id: Option<RiTraceId>,
317 current_span_id: Option<RiSpanId>,
318 baggage: FxHashMap<String, String>,
319}
320
321thread_local! {
323 static CURRENTONTEXT: RefCell<Option<RiTracingContext>> = const { RefCell::new(None) };
324}
325
326impl Default for RiTracingContext {
327 fn default() -> Self {
328 Self::new()
329 }
330}
331
332impl RiTracingContext {
333 pub fn new() -> Self {
334 Self {
335 current_trace_id: None,
336 current_span_id: None,
337 baggage: FxHashMap::default(),
338 }
339 }
340
341 pub fn with_trace_id(mut self, trace_id: RiTraceId) -> Self {
342 self.current_trace_id = Some(trace_id);
343 self
344 }
345
346 pub fn with_span_id(mut self, span_id: RiSpanId) -> Self {
347 self.current_span_id = Some(span_id);
348 self
349 }
350
351 pub fn set_baggage(&mut self, key: String, value: String) {
352 self.baggage.insert(key, value);
353 }
354
355 pub fn get_baggage(&self, key: &str) -> Option<&String> {
356 self.baggage.get(key)
357 }
358
359 pub fn trace_id(&self) -> Option<&RiTraceId> {
360 self.current_trace_id.as_ref()
361 }
362
363 pub fn span_id(&self) -> Option<&RiSpanId> {
364 self.current_span_id.as_ref()
365 }
366
367 pub fn set_as_current(&self) {
369 CURRENTONTEXT.with(|ctx| {
370 *ctx.borrow_mut() = Some(self.clone());
371 });
372 }
373
374 pub fn current() -> Option<Self> {
376 CURRENTONTEXT.with(|ctx| {
377 ctx.borrow().clone()
378 })
379 }
380
381 pub fn new_child(&self, span_id: RiSpanId) -> Self {
383 Self {
384 current_trace_id: self.current_trace_id.clone(),
385 current_span_id: Some(span_id),
386 baggage: self.baggage.clone(),
387 }
388 }
389}
390
391#[derive(Debug, Clone, Serialize, Deserialize)]
393pub enum RiSamplingStrategy {
394 Rate(f64),
396 Deterministic(f64),
398 Adaptive(f64),
400}
401
402#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
404pub struct RiTracer {
405 spans: Arc<RwLock<FxHashMap<RiTraceId, Vec<RiSpan>>>>,
406 active_spans: Arc<RwLock<FxHashMap<RiSpanId, RiSpan>>>,
407 sampling_strategy: RiSamplingStrategy,
408 adaptive_window: Arc<RwLock<Vec<u64>>>,
409 max_adaptive_window: usize,
410}
411
412impl RiTracer {
413 pub fn new(sampling_rate: f64) -> Self {
414 Self {
415 spans: Arc::new(RwLock::new(FxHashMap::default())),
416 active_spans: Arc::new(RwLock::new(FxHashMap::default())),
417 sampling_strategy: RiSamplingStrategy::Rate(sampling_rate.clamp(0.0, 1.0)),
418 adaptive_window: Arc::new(RwLock::new(Vec::new())),
419 max_adaptive_window: 100,
420 }
421 }
422
423 pub fn with_strategy(strategy: RiSamplingStrategy) -> Self {
425 Self {
426 spans: Arc::new(RwLock::new(FxHashMap::default())),
427 active_spans: Arc::new(RwLock::new(FxHashMap::default())),
428 sampling_strategy: strategy,
429 adaptive_window: Arc::new(RwLock::new(Vec::new())),
430 max_adaptive_window: 100,
431 }
432 }
433
434 pub fn start_trace(&self, name: String) -> Option<RiTraceId> {
436 if !self.should_sample() {
437 return None;
438 }
439
440 let trace_id = RiTraceId::new();
441 let span = RiSpan::new(trace_id.clone(), None, name, RiSpanKind::Server);
442
443 let span_id = span.span_id.clone();
444 {
445 let mut active_spans = self.active_spans.write_safe("active spans for new trace").ok()?;
446 active_spans.insert(span_id.clone(), span);
447 }
448 {
449 let mut spans = self.spans.write_safe("spans for new trace").ok()?;
450 spans.insert(trace_id.clone(), Vec::new());
451 }
452
453 let context = RiTracingContext::new()
455 .with_trace_id(trace_id.clone())
456 .with_span_id(span_id);
457 context.set_as_current();
458
459 Some(trace_id)
460 }
461
462 pub fn start_span(
464 &self,
465 trace_id: Option<&RiTraceId>,
466 parent_span_id: Option<RiSpanId>,
467 name: String,
468 kind: RiSpanKind,
469 ) -> Option<RiSpanId> {
470 let resolved_trace_id = match trace_id {
472 Some(id) => id.clone(),
473 None => {
474 if let Some(context) = RiTracingContext::current() {
475 if let Some(id) = context.trace_id() {
476 id.clone()
477 } else {
478 return None;
479 }
480 } else {
481 return None;
482 }
483 }
484 };
485
486 let resolved_parent_span_id = match parent_span_id {
488 Some(id) => Some(id.clone()),
489 None => RiTracingContext::current().and_then(|context| context.span_id().cloned()),
490 };
491
492 let spans = match self.spans.read_safe("spans for span check") {
493 Ok(s) => s,
494 Err(_) => return None,
495 };
496 if !spans.contains_key(&resolved_trace_id) {
497 return None;
498 }
499
500 let span = RiSpan::new(
501 resolved_trace_id.clone(),
502 resolved_parent_span_id,
503 name,
504 kind,
505 );
506
507 let span_id = span.span_id.clone();
508 {
509 let mut active_spans = self.active_spans.write_safe("active spans for new span").ok()?;
510 active_spans.insert(span_id.clone(), span);
511 }
512
513 if let Some(context) = RiTracingContext::current() {
515 let new_context = context.new_child(span_id.clone());
516 new_context.set_as_current();
517 } else {
518 let context = RiTracingContext::new()
520 .with_trace_id(resolved_trace_id)
521 .with_span_id(span_id.clone());
522 context.set_as_current();
523 }
524
525 Some(span_id)
526 }
527
528 pub fn start_span_from_context(&self, name: String, kind: RiSpanKind) -> Option<RiSpanId> {
530 self.start_span(None, None, name, kind)
531 }
532
533 pub fn end_span(&self, span_id: &RiSpanId, status: RiSpanStatus) -> RiResult<()> {
535 let mut active_spans = self.active_spans.write_safe("active spans for end span")?;
536
537 if let Some(mut span) = active_spans.remove(span_id) {
538 span.end(status);
539
540 let trace_id = span.trace_id.clone();
541 let parent_span_id = span.parent_span_id.clone();
542 drop(active_spans);
543
544 {
545 let mut spans = self.spans.write_safe("spans for end span")?;
546 if let Some(spans_list) = spans.get_mut(&trace_id) {
547 spans_list.push(span);
548 }
549 }
550
551 if let Some(parent_span_id) = parent_span_id {
553 let active_spans = self.active_spans.read_safe("active spans for parent check")?;
555 if active_spans.get(&parent_span_id).is_some() {
556 let context = RiTracingContext::new()
557 .with_trace_id(trace_id)
558 .with_span_id(parent_span_id);
559 context.set_as_current();
560 }
561 } else {
562 let context = RiTracingContext::new();
564 context.set_as_current();
565 }
566 }
567
568 Ok(())
569 }
570
571 pub fn span_mut<F>(&self, span_id: &RiSpanId, f: F) -> RiResult<()>
573 where
574 F: FnOnce(&mut RiSpan),
575 {
576 let mut active_spans = self.active_spans.write_safe("active spans for span_mut")?;
577
578 if let Some(span) = active_spans.get_mut(span_id) {
579 f(span);
580 Ok(())
581 } else {
582 Err(crate::core::RiError::Other("Span not found".to_string()))
583 }
584 }
585
586 pub fn export_traces(&self) -> FxHashMap<RiTraceId, Vec<RiSpan>> {
588 match self.spans.read_safe("spans for export") {
589 Ok(spans) => spans.clone(),
590 Err(_) => FxHashMap::default(),
591 }
592 }
593
594 pub fn active_trace_count(&self) -> usize {
596 match self.spans.read_safe("spans for count") {
597 Ok(spans) => spans.len(),
598 Err(_) => 0,
599 }
600 }
601
602 pub fn active_span_count(&self) -> usize {
604 match self.active_spans.read_safe("active spans for count") {
605 Ok(active_spans) => active_spans.len(),
606 Err(_) => 0,
607 }
608 }
609
610 fn should_sample(&self) -> bool {
611 match &self.sampling_strategy {
612 RiSamplingStrategy::Rate(rate) => {
613 if *rate >= 1.0 {
614 true
615 } else if *rate <= 0.0 {
616 false
617 } else {
618 use rand::Rng;
619 let mut rng = rand::thread_rng();
620 rng.gen::<f64>() < *rate
621 }
622 }
623 RiSamplingStrategy::Deterministic(rate) => {
624 if *rate >= 1.0 {
625 true
626 } else if *rate <= 0.0 {
627 false
628 } else {
629 let now = SystemTime::now()
631 .duration_since(UNIX_EPOCH)
632 .unwrap_or(Duration::from_secs(0))
633 .as_nanos();
634 let thread_id = format!("{:?}", std::thread::current().id())
636 .as_bytes()
637 .iter()
638 .fold(0u64, |acc, &b| acc.wrapping_mul(31).wrapping_add(b as u64));
639 let combined = now.wrapping_add(thread_id as u128);
640
641 let hash = (combined as u64).wrapping_mul(0x517cc1b727220a95);
643 let hash_f64 = (hash as f64) / (u64::MAX as f64);
644
645 hash_f64 < *rate
646 }
647 }
648 RiSamplingStrategy::Adaptive(target_rate) => {
649 if *target_rate >= 1.0 {
650 true
651 } else if *target_rate <= 0.0 {
652 false
653 } else {
654 let active_count = match self.active_spans.read_safe("active spans for sampling") {
656 Ok(active_spans) => active_spans.len() as f64,
657 Err(_) => 0.0,
658 };
659
660 let mut window = match self.adaptive_window.write_safe("adaptive window for sampling") {
661 Ok(w) => w,
662 Err(_) => {
663 return false;
665 }
666 };
667
668 window.push(active_count as u64);
670 if window.len() > self.max_adaptive_window {
671 window.remove(0);
672 }
673
674 let avg_load = if window.is_empty() {
676 0.0
677 } else {
678 window.iter().sum::<u64>() as f64 / window.len() as f64
679 };
680
681 const BASE_LOAD: f64 = 100.0;
683 let adjusted_rate = target_rate * (1.0 + (BASE_LOAD - avg_load) / BASE_LOAD);
684 let clamped_rate = adjusted_rate.clamp(0.01, 1.0);
685
686 use rand::Rng;
687 let mut rng = rand::thread_rng();
688 rng.gen::<f64>() < clamped_rate
689 }
690 }
691 }
692 }
693
694}
695
696#[cfg(feature = "pyo3")]
697#[pyo3::prelude::pymethods]
698impl RiTracer {
699 #[new]
701 fn py_new(sampling_rate: f64) -> Self {
702 Self::new(sampling_rate)
703 }
704
705 #[pyo3(name = "start_trace")]
707 fn start_trace_impl(&self, name: String) -> PyResult<Option<String>> {
708 match self.start_trace(name) {
709 Some(trace_id) => Ok(Some(trace_id.as_str().to_string())),
710 None => Ok(None),
711 }
712 }
713
714 #[pyo3(name = "start_span_from_context")]
716 fn start_span_from_context_impl(&self, name: String, kind: String) -> PyResult<Option<String>> {
717 let span_kind = match kind.as_str() {
718 "Server" => RiSpanKind::Server,
719 "Client" => RiSpanKind::Client,
720 "Producer" => RiSpanKind::Producer,
721 "Consumer" => RiSpanKind::Consumer,
722 _ => RiSpanKind::Internal,
723 };
724
725 match self.start_span_from_context(name, span_kind) {
726 Some(span_id) => Ok(Some(span_id.as_str().to_string())),
727 None => Ok(None),
728 }
729 }
730
731 #[pyo3(name = "end_span")]
733 fn end_span_impl(&self, span_id: String, status: String) -> PyResult<()> {
734 let span_id_obj = RiSpanId::from_string(span_id);
735 let span_status = match status.as_str() {
736 "Ok" => RiSpanStatus::Ok,
737 "Error" => RiSpanStatus::Error("Python error".to_string()),
738 _ => RiSpanStatus::Unset,
739 };
740
741 self.end_span(&span_id_obj, span_status)
742 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to end span: {e}")))
743 }
744
745 #[pyo3(name = "span_set_attribute")]
747 fn span_set_attribute_impl(&self, span_id: String, key: String, value: String) -> PyResult<()> {
748 let span_id_obj = RiSpanId::from_string(span_id);
749 self.span_mut(&span_id_obj, |span| {
750 span.set_attribute(key, value);
751 })
752 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to set attribute: {e}")))
753 }
754
755 #[pyo3(name = "span_add_event")]
757 fn span_add_event_impl(&self, span_id: String, name: String, attributes: FxHashMap<String, String>) -> PyResult<()> {
758 let span_id_obj = RiSpanId::from_string(span_id);
759 self.span_mut(&span_id_obj, |span| {
760 span.add_event(name, attributes);
761 })
762 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to add event: {e}")))
763 }
764
765 #[pyo3(name = "export_traces")]
767 fn export_traces_impl(&self, py: pyo3::Python<'_>) -> PyResult<FxHashMap<String, Vec<pyo3::Py<pyo3::PyAny>>>> {
768 let traces = self.export_traces();
769 let mut result = FxHashMap::with_capacity(traces.len());
770
771 for (trace_id, spans) in traces {
772 let mut span_list = Vec::with_capacity(spans.len());
773 for span in spans {
774 let span_dict = pyo3::types::PyDict::new(py);
775 span_dict.set_item("trace_id", span.trace_id.as_str())?;
776 span_dict.set_item("span_id", span.span_id.as_str())?;
777 if let Some(parent_id) = &span.parent_span_id {
778 span_dict.set_item("parent_span_id", parent_id.as_str())?;
779 }
780 span_dict.set_item("name", &span.name)?;
781 span_dict.set_item("kind", format!("{:?}", span.kind))?;
782 span_dict.set_item("start_time", span.start_time)?;
783 span_dict.set_item("end_time", span.end_time)?;
784 span_dict.set_item("attributes", span.attributes.clone())?;
785 span_dict.set_item("events", span.events.len())?;
786 span_dict.set_item("status", format!("{:?}", span.status))?;
787 span_list.push(span_dict.into());
788 }
789 result.insert(trace_id.as_str().to_string(), span_list);
790 }
791 Ok(result)
792 }
793
794 #[pyo3(name = "active_trace_count")]
796 fn active_trace_count_impl(&self) -> usize {
797 self.active_trace_count()
798 }
799
800 #[pyo3(name = "active_span_count")]
802 fn active_span_count_impl(&self) -> usize {
803 self.active_span_count()
804 }
805}
806
807pub struct RiTracerManager {
809 tracers: FxHashMap<String, Arc<RiTracer>>,
810 default_tracer: Option<String>,
811}
812
813impl Default for RiTracerManager {
814 fn default() -> Self {
815 Self::new()
816 }
817}
818
819impl RiTracerManager {
820 pub fn new() -> Self {
821 Self {
822 tracers: FxHashMap::default(),
823 default_tracer: None,
824 }
825 }
826
827 pub fn register_tracer(&mut self, name: &str, tracer: Arc<RiTracer>) {
828 self.tracers.insert(name.to_string(), tracer);
829 if self.default_tracer.is_none() {
830 self.default_tracer = Some(name.to_string());
831 }
832 }
833
834 #[allow(dead_code)]
835 pub fn get_tracer(&self, name: &str) -> Option<&Arc<RiTracer>> {
836 self.tracers.get(name)
837 }
838
839 pub fn get_default_tracer(&self) -> Option<&Arc<RiTracer>> {
840 if let Some(default_name) = &self.default_tracer {
841 self.tracers.get(default_name)
842 } else {
843 None
844 }
845 }
846
847 #[allow(dead_code)]
848 pub fn set_default_tracer(&mut self, name: &str) -> bool {
849 if self.tracers.contains_key(name) {
850 self.default_tracer = Some(name.to_string());
851 true
852 } else {
853 false
854 }
855 }
856
857 #[allow(dead_code)]
858 pub fn remove_tracer(&mut self, name: &str) -> bool {
859 let removed = self.tracers.remove(name).is_some();
860 if let Some(default_name) = &self.default_tracer {
861 if default_name == name {
862 self.default_tracer = None;
863 }
864 }
865 removed
866 }
867}
868
869pub struct DefaultTracerManager {
871 inner: Arc<RwLock<RiTracerManager>>,
872}
873
874impl Default for DefaultTracerManager {
875 fn default() -> Self {
876 Self {
877 inner: Arc::new(RwLock::new(RiTracerManager::new())),
878 }
879 }
880}
881
882impl DefaultTracerManager {
883 #[allow(dead_code)]
884 pub fn new() -> Self {
885 Default::default()
886 }
887
888 pub async fn register_tracer(&self, name: &str, sampling_rate: f64) -> RiResult<()> {
889 let tracer = Arc::new(RiTracer::new(sampling_rate));
890 let mut manager = self.inner.write_safe("tracer manager for register")?;
891 manager.register_tracer(name, tracer);
892 Ok(())
893 }
894
895 pub async fn register_tracer_with_strategy(&self, name: &str, strategy: RiSamplingStrategy) -> RiResult<()> {
896 let tracer = Arc::new(RiTracer::with_strategy(strategy));
897 let mut manager = self.inner.write_safe("tracer manager for register with strategy")?;
898 manager.register_tracer(name, tracer);
899 Ok(())
900 }
901
902 #[allow(dead_code)]
903 pub async fn get_tracer(&self, name: &str) -> RiResult<Option<Arc<RiTracer>>> {
904 let manager = self.inner.read_safe("tracer manager for get")?;
905 Ok(manager.get_tracer(name).cloned())
906 }
907
908 pub async fn get_default_tracer(&self) -> RiResult<Option<Arc<RiTracer>>> {
909 let manager = self.inner.read_safe("tracer manager for get default")?;
910 Ok(manager.get_default_tracer().cloned())
911 }
912
913 #[allow(dead_code)]
914 pub async fn set_default_tracer(&self, name: &str) -> RiResult<bool> {
915 let mut manager = self.inner.write_safe("tracer manager for set default")?;
916 Ok(manager.set_default_tracer(name))
917 }
918
919 #[allow(dead_code)]
920 pub async fn remove_tracer(&self, name: &str) -> RiResult<bool> {
921 let mut manager = self.inner.write_safe("tracer manager for remove")?;
922 Ok(manager.remove_tracer(name))
923 }
924}
925
926pub static DEFAULT_TRACER_MANAGER: std::sync::LazyLock<DefaultTracerManager> = std::sync::LazyLock::new(DefaultTracerManager::default);
928
929pub fn init_tracer(sampling_rate: f64) {
931 let runtime = match tokio::runtime::Builder::new_current_thread()
932 .enable_all()
933 .build()
934 {
935 Ok(r) => r,
936 Err(e) => {
937 eprintln!("Failed to create tokio runtime: {}", e);
938 return;
939 }
940 };
941
942 runtime.block_on(async {
943 if let Err(e) = DEFAULT_TRACER_MANAGER.register_tracer("default", sampling_rate).await {
944 eprintln!("Failed to register tracer: {}", e);
945 }
946 });
947}
948
949pub fn init_tracer_with_strategy(strategy: RiSamplingStrategy) {
951 let rate = match strategy {
952 RiSamplingStrategy::Rate(rate) => rate,
953 RiSamplingStrategy::Deterministic(rate) => rate,
954 RiSamplingStrategy::Adaptive(rate) => rate,
955 };
956
957 let runtime = match tokio::runtime::Builder::new_current_thread()
958 .enable_all()
959 .build()
960 {
961 Ok(r) => r,
962 Err(e) => {
963 eprintln!("Failed to create tokio runtime: {}", e);
964 return;
965 }
966 };
967
968 runtime.block_on(async {
969 if let Err(e) = DEFAULT_TRACER_MANAGER.register_tracer("default", rate).await {
970 eprintln!("Failed to register tracer: {}", e);
971 }
972 });
973}
974
975pub fn tracer() -> Result<Arc<RiTracer>, Box<RiError>> {
977 let runtime = match tokio::runtime::Builder::new_current_thread()
978 .enable_all()
979 .build()
980 {
981 Ok(r) => r,
982 Err(e) => {
983 return Err(Box::new(RiError::Other(format!(
984 "Failed to create tokio runtime for tracer: {}",
985 e
986 ))));
987 }
988 };
989
990 runtime.block_on(async {
991 match DEFAULT_TRACER_MANAGER.get_default_tracer().await {
992 Ok(Some(tracer)) => Ok(tracer),
993 Ok(None) => {
994 Err(Box::new(RiError::Other(
995 "Tracer not initialized".to_string(),
996 )))
997 }
998 Err(e) => Err(Box::new(RiError::Other(format!(
999 "Failed to get tracer: {}",
1000 e
1001 )))),
1002 }
1003 })
1004}