opentelemetry_stdout/metrics/
exporter.rs1use chrono::{DateTime, Utc};
2use core::{f64, fmt};
3use opentelemetry_sdk::metrics::Temporality;
4use opentelemetry_sdk::{
5 error::OTelSdkResult,
6 metrics::{
7 data::{
8 ExponentialHistogram, Gauge, GaugeDataPoint, Histogram, HistogramDataPoint,
9 ResourceMetrics, ScopeMetrics, Sum, SumDataPoint,
10 },
11 exporter::PushMetricExporter,
12 },
13};
14use std::fmt::Debug;
15use std::sync::atomic;
16
17pub struct MetricExporter {
19 is_shutdown: atomic::AtomicBool,
20 temporality: Temporality,
21}
22
23impl MetricExporter {
24 pub fn builder() -> MetricExporterBuilder {
26 MetricExporterBuilder::default()
27 }
28}
29impl Default for MetricExporter {
30 fn default() -> Self {
31 MetricExporterBuilder::default().build()
32 }
33}
34
35impl fmt::Debug for MetricExporter {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 f.write_str("MetricExporter")
38 }
39}
40
41impl PushMetricExporter for MetricExporter {
42 async fn export(&self, metrics: &mut ResourceMetrics) -> OTelSdkResult {
44 if self.is_shutdown.load(atomic::Ordering::SeqCst) {
45 Err(opentelemetry_sdk::error::OTelSdkError::AlreadyShutdown)
46 } else {
47 println!("Metrics");
48 println!("Resource");
49 if let Some(schema_url) = metrics.resource.schema_url() {
50 println!("\tResource SchemaUrl: {:?}", schema_url);
51 }
52
53 metrics.resource.iter().for_each(|(k, v)| {
54 println!("\t -> {}={:?}", k, v);
55 });
56 print_metrics(&metrics.scope_metrics);
57 Ok(())
58 }
59 }
60
61 fn force_flush(&self) -> OTelSdkResult {
62 Ok(())
64 }
65
66 fn shutdown(&self) -> OTelSdkResult {
67 self.is_shutdown.store(true, atomic::Ordering::SeqCst);
68 Ok(())
69 }
70
71 fn temporality(&self) -> Temporality {
72 self.temporality
73 }
74}
75
76fn print_metrics(metrics: &[ScopeMetrics]) {
77 for (i, metric) in metrics.iter().enumerate() {
78 println!("\tInstrumentation Scope #{}", i);
79 println!("\t\tName : {}", &metric.scope.name());
80 if let Some(version) = &metric.scope.version() {
81 println!("\t\tVersion : {:?}", version);
82 }
83 if let Some(schema_url) = &metric.scope.schema_url() {
84 println!("\t\tSchemaUrl: {:?}", schema_url);
85 }
86 metric
87 .scope
88 .attributes()
89 .enumerate()
90 .for_each(|(index, kv)| {
91 if index == 0 {
92 println!("\t\tScope Attributes:");
93 }
94 println!("\t\t\t -> {}: {}", kv.key, kv.value);
95 });
96
97 metric.metrics.iter().enumerate().for_each(|(i, metric)| {
98 println!("Metric #{}", i);
99 println!("\t\tName : {}", &metric.name);
100 println!("\t\tDescription : {}", &metric.description);
101 println!("\t\tUnit : {}", &metric.unit);
102
103 let data = metric.data.as_any();
104 if let Some(hist) = data.downcast_ref::<Histogram<u64>>() {
105 println!("\t\tType : Histogram");
106 print_histogram(hist);
107 } else if let Some(hist) = data.downcast_ref::<Histogram<f64>>() {
108 println!("\t\tType : Histogram");
109 print_histogram(hist);
110 } else if let Some(_hist) = data.downcast_ref::<ExponentialHistogram<u64>>() {
111 println!("\t\tType : Exponential Histogram");
112 } else if let Some(_hist) = data.downcast_ref::<ExponentialHistogram<f64>>() {
114 println!("\t\tType : Exponential Histogram");
115 } else if let Some(sum) = data.downcast_ref::<Sum<u64>>() {
117 println!("\t\tType : Sum");
118 print_sum(sum);
119 } else if let Some(sum) = data.downcast_ref::<Sum<i64>>() {
120 println!("\t\tType : Sum");
121 print_sum(sum);
122 } else if let Some(sum) = data.downcast_ref::<Sum<f64>>() {
123 println!("\t\tType : Sum");
124 print_sum(sum);
125 } else if let Some(gauge) = data.downcast_ref::<Gauge<u64>>() {
126 println!("\t\tType : Gauge");
127 print_gauge(gauge);
128 } else if let Some(gauge) = data.downcast_ref::<Gauge<i64>>() {
129 println!("\t\tType : Gauge");
130 print_gauge(gauge);
131 } else if let Some(gauge) = data.downcast_ref::<Gauge<f64>>() {
132 println!("\t\tType : Gauge");
133 print_gauge(gauge);
134 } else {
135 println!("Unsupported data type");
136 }
137 });
138 }
139}
140
141fn print_sum<T: Debug>(sum: &Sum<T>) {
142 println!("\t\tSum DataPoints");
143 println!("\t\tMonotonic : {}", sum.is_monotonic);
144 if sum.temporality == Temporality::Cumulative {
145 println!("\t\tTemporality : Cumulative");
146 } else {
147 println!("\t\tTemporality : Delta");
148 }
149 let datetime: DateTime<Utc> = sum.start_time.into();
150 println!(
151 "\t\tStartTime : {}",
152 datetime.format("%Y-%m-%d %H:%M:%S%.6f")
153 );
154 let datetime: DateTime<Utc> = sum.time.into();
155 println!(
156 "\t\tEndTime : {}",
157 datetime.format("%Y-%m-%d %H:%M:%S%.6f")
158 );
159 print_sum_data_points(&sum.data_points);
160}
161
162fn print_gauge<T: Debug>(gauge: &Gauge<T>) {
163 println!("\t\tGauge DataPoints");
164 if let Some(start_time) = gauge.start_time {
165 let datetime: DateTime<Utc> = start_time.into();
166 println!(
167 "\t\tStartTime : {}",
168 datetime.format("%Y-%m-%d %H:%M:%S%.6f")
169 );
170 }
171 let datetime: DateTime<Utc> = gauge.time.into();
172 println!(
173 "\t\tEndTime : {}",
174 datetime.format("%Y-%m-%d %H:%M:%S%.6f")
175 );
176 print_gauge_data_points(&gauge.data_points);
177}
178
179fn print_histogram<T: Debug>(histogram: &Histogram<T>) {
180 if histogram.temporality == Temporality::Cumulative {
181 println!("\t\tTemporality : Cumulative");
182 } else {
183 println!("\t\tTemporality : Delta");
184 }
185 let datetime: DateTime<Utc> = histogram.start_time.into();
186 println!(
187 "\t\tStartTime : {}",
188 datetime.format("%Y-%m-%d %H:%M:%S%.6f")
189 );
190 let datetime: DateTime<Utc> = histogram.time.into();
191 println!(
192 "\t\tEndTime : {}",
193 datetime.format("%Y-%m-%d %H:%M:%S%.6f")
194 );
195 println!("\t\tHistogram DataPoints");
196 print_hist_data_points(&histogram.data_points);
197}
198
199fn print_sum_data_points<T: Debug>(data_points: &[SumDataPoint<T>]) {
200 for (i, data_point) in data_points.iter().enumerate() {
201 println!("\t\tDataPoint #{}", i);
202 println!("\t\t\tValue : {:#?}", data_point.value);
203 println!("\t\t\tAttributes :");
204 for kv in data_point.attributes.iter() {
205 println!("\t\t\t\t -> {}: {}", kv.key, kv.value.as_str());
206 }
207 }
208}
209
210fn print_gauge_data_points<T: Debug>(data_points: &[GaugeDataPoint<T>]) {
211 for (i, data_point) in data_points.iter().enumerate() {
212 println!("\t\tDataPoint #{}", i);
213 println!("\t\t\tValue : {:#?}", data_point.value);
214 println!("\t\t\tAttributes :");
215 for kv in data_point.attributes.iter() {
216 println!("\t\t\t\t -> {}: {}", kv.key, kv.value.as_str());
217 }
218 }
219}
220
221fn print_hist_data_points<T: Debug>(data_points: &[HistogramDataPoint<T>]) {
222 for (i, data_point) in data_points.iter().enumerate() {
223 println!("\t\tDataPoint #{}", i);
224 println!("\t\t\tCount : {}", data_point.count);
225 println!("\t\t\tSum : {:?}", data_point.sum);
226 if let Some(min) = &data_point.min {
227 println!("\t\t\tMin : {:?}", min);
228 }
229
230 if let Some(max) = &data_point.max {
231 println!("\t\t\tMax : {:?}", max);
232 }
233
234 println!("\t\t\tAttributes :");
235 for kv in data_point.attributes.iter() {
236 println!("\t\t\t\t -> {}: {}", kv.key, kv.value.as_str());
237 }
238
239 println!("\t\t\tBuckets");
240 let mut lower_bound = f64::NEG_INFINITY;
241 for (i, &upper_bound) in data_point.bounds.iter().enumerate() {
242 let count = data_point.bucket_counts.get(i).unwrap_or(&0);
243 println!("\t\t\t\t {} to {} : {}", lower_bound, upper_bound, count);
244 lower_bound = upper_bound;
245 }
246
247 let last_count = data_point
248 .bucket_counts
249 .get(data_point.bounds.len())
250 .unwrap_or(&0);
251 println!("\t\t\t\t{} to +Infinity : {}", lower_bound, last_count);
252 }
253}
254
255#[derive(Default)]
257pub struct MetricExporterBuilder {
258 temporality: Option<Temporality>,
259}
260
261impl MetricExporterBuilder {
262 pub fn with_temporality(mut self, temporality: Temporality) -> Self {
264 self.temporality = Some(temporality);
265 self
266 }
267
268 pub fn build(self) -> MetricExporter {
270 MetricExporter {
271 temporality: self.temporality.unwrap_or_default(),
272 is_shutdown: atomic::AtomicBool::new(false),
273 }
274 }
275}
276
277impl fmt::Debug for MetricExporterBuilder {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 f.write_str("MetricExporterBuilder")
280 }
281}