1use std::sync::Arc;
2
3use metrics::set_global_recorder;
4use otlp_recorder::OtlpRecorder;
5
6mod json;
7mod metric;
8pub mod otlp_recorder;
9mod time;
10pub mod transport;
11
12pub fn install_recorder(
27 name: impl ToString,
28 version: impl ToString,
29 instance_id: impl ToString,
30) -> Arc<OtlpRecorder> {
31 let recorder = Arc::new(OtlpRecorder::new(name, version, instance_id));
32 set_global_recorder(recorder.clone()).expect("Recorder installed");
33 recorder
34}
35
36#[cfg(test)]
37mod tests {
38 use core::time::Duration;
39
40 use metrics::{
41 counter, describe_counter, describe_gauge, describe_histogram, gauge, histogram,
42 set_default_local_recorder, Unit,
43 };
44
45 use crate::time::set_time;
46
47 use super::*;
48
49 #[test]
50 fn test_recorder_to_json() {
51 set_time(1739394449205);
52 let recorder = OtlpRecorder::new("otlp-metrics", "1", "test_recorder_to_json");
53 let _guard = set_default_local_recorder(&recorder);
54 for i in 1..3 {
55 counter!("test_counter", "label1" => "label_value1").increment(1);
56 gauge!("test_gauge", "label2" => "label_value2").set(i * 10);
57 histogram!("test_histogram", "label3" => "label_value3").record(i * 10);
58 histogram!("test_histogram_with_buckets", "buckets" => "10,30").record(i * 10);
59 }
60
61 assert_eq!(
62 recorder.to_json(None),
63 r#"{"resourceMetrics":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"otlp-metrics"}},{"key":"service.version","value":{"stringValue":"1"}},{"key":"service.instance.id","value":{"stringValue":"test_recorder_to_json"}}]},"scopeMetrics":[{"metrics":[{"name":"test_counter","unit":"1","description":"","sum":{"aggregationTemporality":2,"isMonotonic":true,"dataPoints":[{"asInt":2,"startTimeUnixNano":1739394449305000000,"timeUnixNano":1739394450105000000,"attributes":[{"key":"label1","value":{"stringValue":"label_value1"}}]}]}},{"name":"test_gauge","unit":"1","description":"","gauge":{"dataPoints":[{"asDouble":20,"startTimeUnixNano":1739394449505000000,"timeUnixNano":1739394450205000000,"attributes":[{"key":"label2","value":{"stringValue":"label_value2"}}]}]}},{"name":"test_histogram","unit":"1","description":"","histogram":{"aggregationTemporality":2,"dataPoints":[{"startTimeUnixNano":1739394449705000000,"timeUnixNano":1739394450305000000,"count":2,"sum":30,"attributes":[{"key":"label3","value":{"stringValue":"label_value3"}}],"bucketCounts":[],"explicitBounds":[]}]}},{"name":"test_histogram_with_buckets","unit":"1","description":"","histogram":{"aggregationTemporality":2,"dataPoints":[{"startTimeUnixNano":1739394449905000000,"timeUnixNano":1739394450405000000,"count":2,"sum":30,"attributes":[{"key":"buckets","value":{"stringValue":"10,30"}}],"bucketCounts":[1,1,0],"explicitBounds":[10,30]}]}}]}]}]}"#,
64 );
65 }
66
67 #[test]
68 fn test_recorder_with_descriptions_and_units() {
69 set_time(1739394449205);
70 let recorder = OtlpRecorder::new(
71 "otlp-metrics",
72 "1",
73 "test_recorder_with_descriptions_and_units",
74 );
75 let _guard = set_default_local_recorder(&recorder);
76
77 describe_counter!("bytes_total", Unit::Bytes, "Counter for bytes");
78 describe_gauge!("limit_reached", Unit::Percent, "Gauge percent");
79 describe_histogram!(
80 "request_time",
81 Unit::Milliseconds,
82 "Request time in milliseconds"
83 );
84
85 counter!("bytes_total").increment(1);
86 gauge!("limit_reached").set(10);
87 histogram!("request_time").record(10);
88
89 assert_eq!(
90 recorder.to_json(None),
91 r#"{"resourceMetrics":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"otlp-metrics"}},{"key":"service.version","value":{"stringValue":"1"}},{"key":"service.instance.id","value":{"stringValue":"test_recorder_with_descriptions_and_units"}}]},"scopeMetrics":[{"metrics":[{"name":"bytes_total","unit":"B","description":"Counter for bytes","sum":{"aggregationTemporality":2,"isMonotonic":true,"dataPoints":[{"asInt":1,"startTimeUnixNano":1739394449305000000,"timeUnixNano":1739394449405000000,"attributes":[]}]}},{"name":"limit_reached","unit":"%","description":"Gauge percent","gauge":{"dataPoints":[{"asDouble":10,"startTimeUnixNano":1739394449505000000,"timeUnixNano":1739394449605000000,"attributes":[]}]}},{"name":"request_time","unit":"ms","description":"Request time in milliseconds","histogram":{"aggregationTemporality":2,"dataPoints":[{"startTimeUnixNano":1739394449705000000,"timeUnixNano":1739394449805000000,"count":1,"sum":10,"attributes":[],"bucketCounts":[],"explicitBounds":[]}]}}]}]}]}"#,
92 );
93 }
94
95 #[test]
96 fn test_metric_times() {
97 set_time(1739394449205);
98 let recorder = OtlpRecorder::new("otlp-metrics", "1", "test_metric_times");
99 let _guard = set_default_local_recorder(&recorder);
100
101 counter!("test_counter", "label1" => "label_value1").increment(1);
102
103 assert_eq!(
104 recorder.to_json(None),
105 r#"{"resourceMetrics":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"otlp-metrics"}},{"key":"service.version","value":{"stringValue":"1"}},{"key":"service.instance.id","value":{"stringValue":"test_metric_times"}}]},"scopeMetrics":[{"metrics":[{"name":"test_counter","unit":"1","description":"","sum":{"aggregationTemporality":2,"isMonotonic":true,"dataPoints":[{"asInt":1,"startTimeUnixNano":1739394449305000000,"timeUnixNano":1739394449405000000,"attributes":[{"key":"label1","value":{"stringValue":"label_value1"}}]}]}}]}]}]}"#
106 );
107
108 counter!("test_counter", "label1" => "label_value1").increment(1);
109
110 assert_eq!(
111 recorder.to_json(None),
112 r#"{"resourceMetrics":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"otlp-metrics"}},{"key":"service.version","value":{"stringValue":"1"}},{"key":"service.instance.id","value":{"stringValue":"test_metric_times"}}]},"scopeMetrics":[{"metrics":[{"name":"test_counter","unit":"1","description":"","sum":{"aggregationTemporality":2,"isMonotonic":true,"dataPoints":[{"asInt":2,"startTimeUnixNano":1739394449305000000,"timeUnixNano":1739394449505000000,"attributes":[{"key":"label1","value":{"stringValue":"label_value1"}}]}]}}]}]}]}"#
113 );
114 }
115
116 #[test]
117 fn test_output_only_changed_values() {
118 set_time(1739394449205);
119 let recorder = OtlpRecorder::new("otlp-metrics", "1", "test_output_only_changed_values");
120 let _guard = set_default_local_recorder(&recorder);
121
122 counter!("test_counter", "label1" => "label_value1").increment(1);
123
124 assert_eq!(
125 recorder.to_json(None),
126 r#"{"resourceMetrics":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"otlp-metrics"}},{"key":"service.version","value":{"stringValue":"1"}},{"key":"service.instance.id","value":{"stringValue":"test_output_only_changed_values"}}]},"scopeMetrics":[{"metrics":[{"name":"test_counter","unit":"1","description":"","sum":{"aggregationTemporality":2,"isMonotonic":true,"dataPoints":[{"asInt":1,"startTimeUnixNano":1739394449305000000,"timeUnixNano":1739394449405000000,"attributes":[{"key":"label1","value":{"stringValue":"label_value1"}}]}]}}]}]}]}"#
127 );
128
129 assert_eq!(
130 recorder.to_json(Duration::from_millis(101).into()),
131 r#"{"resourceMetrics":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"otlp-metrics"}},{"key":"service.version","value":{"stringValue":"1"}},{"key":"service.instance.id","value":{"stringValue":"test_output_only_changed_values"}}]},"scopeMetrics":[{"metrics":[{"name":"test_counter","unit":"1","description":"","sum":{"aggregationTemporality":2,"isMonotonic":true,"dataPoints":[{"asInt":1,"startTimeUnixNano":1739394449305000000,"timeUnixNano":1739394449405000000,"attributes":[{"key":"label1","value":{"stringValue":"label_value1"}}]}]}}]}]}]}"#
132 );
133
134 assert_eq!(
135 recorder.to_json(Duration::from_millis(99).into()),
136 r#"{"resourceMetrics":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"otlp-metrics"}},{"key":"service.version","value":{"stringValue":"1"}},{"key":"service.instance.id","value":{"stringValue":"test_output_only_changed_values"}}]},"scopeMetrics":[{"metrics":[]}]}]}"#
137 );
138
139 counter!("test_counter", "label1" => "label_value1").increment(1);
140
141 assert_eq!(
142 recorder.to_json(Duration::from_secs(99).into()),
143 r#"{"resourceMetrics":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"otlp-metrics"}},{"key":"service.version","value":{"stringValue":"1"}},{"key":"service.instance.id","value":{"stringValue":"test_output_only_changed_values"}}]},"scopeMetrics":[{"metrics":[{"name":"test_counter","unit":"1","description":"","sum":{"aggregationTemporality":2,"isMonotonic":true,"dataPoints":[{"asInt":2,"startTimeUnixNano":1739394449305000000,"timeUnixNano":1739394449705000000,"attributes":[{"key":"label1","value":{"stringValue":"label_value1"}}]}]}}]}]}]}"#
144 );
145 }
146}