scouter_tracing/exporter/
testing.rs1use crate::exporter::ExporterType;
2use crate::exporter::SpanExporterBuilder;
3use crate::exporter::TraceError;
4use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest;
5use opentelemetry_proto::transform::common::tonic::ResourceAttributesWithSchema;
7use opentelemetry_proto::transform::trace::tonic::group_spans_by_resource_and_scope;
8use opentelemetry_sdk::{
9 error::OTelSdkResult,
10 trace::{SpanData, SpanExporter},
11};
12use pyo3::prelude::*;
13use scouter_types::{TraceBaggageRecord, TraceRecord, TraceServerRecord, TraceSpanRecord};
14use std::sync::{Arc, RwLock};
15
16#[derive(Debug)]
17pub struct TestRecords {
18 pub traces: Vec<TraceRecord>,
19 pub spans: Vec<TraceSpanRecord>,
20 pub baggage: Vec<TraceBaggageRecord>,
21}
22
23#[derive(Debug, Clone)]
24#[pyclass]
25pub struct TestSpanExporter {
26 records: Arc<RwLock<TestRecords>>,
27 batch_export: bool,
28}
29
30#[pymethods]
31impl TestSpanExporter {
32 #[new]
33 #[pyo3(signature = (batch_export=true))]
34 pub fn new(batch_export: bool) -> Self {
35 TestSpanExporter {
36 records: Arc::new(RwLock::new(TestRecords {
37 traces: Vec::new(),
38 spans: Vec::new(),
39 baggage: Vec::new(),
40 })),
41 batch_export,
42 }
43 }
44
45 #[getter]
46 pub fn traces(&self) -> Vec<TraceRecord> {
47 self.records.read().unwrap().traces.clone()
48 }
49
50 #[getter]
51 pub fn spans(&self) -> Vec<TraceSpanRecord> {
52 self.records.read().unwrap().spans.clone()
53 }
54
55 #[getter]
56 pub fn baggage(&self) -> Vec<TraceBaggageRecord> {
57 self.records.read().unwrap().baggage.clone()
58 }
59
60 pub fn clear(&self) {
61 let mut records = self.records.write().unwrap();
62 records.traces.clear();
63 records.spans.clear();
64 records.baggage.clear();
65 }
66}
67
68impl Default for TestSpanExporter {
69 fn default() -> Self {
70 Self::new(true)
71 }
72}
73
74impl SpanExporterBuilder for TestSpanExporter {
75 type Exporter = OtelTestSpanExporter;
76
77 fn export_type(&self) -> ExporterType {
78 ExporterType::Testing
79 }
80
81 fn sample_ratio(&self) -> Option<f64> {
82 Some(1.0)
83 }
84
85 fn batch_export(&self) -> bool {
86 self.batch_export
87 }
88
89 fn build_exporter(&self) -> Result<Self::Exporter, TraceError> {
90 Ok(OtelTestSpanExporter::new(self.records.clone()))
91 }
92}
93
94#[derive(Debug)]
95pub struct OtelTestSpanExporter {
96 records: Arc<RwLock<TestRecords>>,
97}
98
99impl OtelTestSpanExporter {
100 pub fn new(records: Arc<RwLock<TestRecords>>) -> Self {
101 OtelTestSpanExporter { records }
102 }
103}
104
105impl SpanExporter for OtelTestSpanExporter {
106 async fn export(&self, batch: Vec<SpanData>) -> OTelSdkResult {
107 let resource_spans =
109 group_spans_by_resource_and_scope(batch, &ResourceAttributesWithSchema::default());
110
111 let req = ExportTraceServiceRequest { resource_spans };
112
113 let record = TraceServerRecord {
114 request: req,
115 space: "test_space".to_string(),
116 name: "test_name".to_string(),
117 version: "test_version".to_string(),
118 };
119
120 let (traces, spans, baggage) = record
121 .to_records()
122 .map_err(|e| opentelemetry_sdk::error::OTelSdkError::InternalFailure(e.to_string()))?;
123
124 let mut records = self.records.write().unwrap();
125 records.traces.extend(traces);
126 records.spans.extend(spans);
127 records.baggage.extend(baggage);
128
129 Ok(())
130 }
131
132 fn shutdown(&mut self) -> OTelSdkResult {
133 Ok(())
134 }
135}