@@ -3,7 +3,10 @@ use opentelemetry::global;
3
3
use opentelemetry_otlp:: { Protocol , WithExportConfig } ;
4
4
use std:: time:: Duration ;
5
5
6
+ use crate :: metrics:: MetricValue ;
6
7
use metrics:: Key ;
8
+ use std:: collections:: HashMap ;
9
+ use std:: sync:: Mutex ;
7
10
8
11
/// Configuration for OpenTelemetry metrics export
9
12
#[ derive( Debug , Clone ) ]
@@ -19,7 +22,7 @@ impl OtlpConfig {
19
22
pub fn new ( endpoint : & str ) -> Self {
20
23
Self {
21
24
endpoint : endpoint. to_string ( ) ,
22
- interval_secs : 5 , // Default to 5 seconds
25
+ interval_secs : 60 , // Default to 60 seconds to align with the meter provider default interval
23
26
}
24
27
}
25
28
@@ -33,17 +36,31 @@ impl OtlpConfig {
33
36
#[ derive( Debug ) ]
34
37
pub struct OtlpMetricsExporter {
35
38
meter : opentelemetry:: metrics:: Meter ,
39
+ counters : Mutex < HashMap < String , opentelemetry:: metrics:: Counter < u64 > > > ,
40
+ gauges : Mutex < HashMap < String , opentelemetry:: metrics:: Gauge < f64 > > > ,
41
+ histograms : Mutex < HashMap < String , opentelemetry:: metrics:: Histogram < f64 > > > ,
36
42
}
37
43
38
44
impl OtlpMetricsExporter {
39
45
/// Create a new OtlpMetricsExporter with the specified configuration
40
46
/// Returns a Result containing the new exporter or an error if initialisation failed
41
47
pub fn new ( config : & OtlpConfig ) -> Result < Self , Box < dyn std:: error:: Error > > {
48
+ // Ensure endpoint ends with /v1/metrics
49
+ let endpoint_url = if !config. endpoint . ends_with ( "/v1/metrics" ) {
50
+ if config. endpoint . ends_with ( '/' ) {
51
+ format ! ( "{}v1/metrics" , config. endpoint)
52
+ } else {
53
+ format ! ( "{}/v1/metrics" , config. endpoint)
54
+ }
55
+ } else {
56
+ config. endpoint . to_string ( )
57
+ } ;
58
+
42
59
// Initialise OTLP exporter using HTTP binary protocol with the specified endpoint
43
60
let exporter = opentelemetry_otlp:: MetricExporter :: builder ( )
44
61
. with_http ( )
45
62
. with_protocol ( Protocol :: HttpBinary )
46
- . with_endpoint ( & config . endpoint )
63
+ . with_endpoint ( & endpoint_url )
47
64
. build ( ) ?;
48
65
49
66
// Create a meter provider with the OTLP Metric Exporter that will collect and export metrics at regular intervals
@@ -62,29 +79,55 @@ impl OtlpMetricsExporter {
62
79
// The meter will be used to create specific metric instruments (counters, gauges, histograms) and record values to them
63
80
let meter = global:: meter ( "mountpoint-s3" ) ;
64
81
65
- Ok ( Self { meter } )
82
+ Ok ( Self {
83
+ meter,
84
+ counters : Mutex :: new ( HashMap :: new ( ) ) ,
85
+ gauges : Mutex :: new ( HashMap :: new ( ) ) ,
86
+ histograms : Mutex :: new ( HashMap :: new ( ) ) ,
87
+ } )
66
88
}
67
89
68
90
/// Record a counter metric in OTel format
69
91
pub fn record_counter ( & self , key : & Key , value : u64 , attributes : & [ KeyValue ] ) {
70
- let counter = self . meter . u64_counter ( key. name ( ) . to_string ( ) ) . build ( ) ;
71
-
92
+ let name = format ! ( "mountpoint.{}" , key. name( ) ) ;
93
+ let mut counters = self . counters . lock ( ) . unwrap ( ) ;
94
+ let counter = counters
95
+ . entry ( name. clone ( ) )
96
+ . or_insert_with ( || self . meter . u64_counter ( name) . build ( ) ) ;
72
97
counter. add ( value, attributes) ;
73
98
}
74
99
75
100
/// Record a gauge metric in OTel format
76
101
pub fn record_gauge ( & self , key : & Key , value : f64 , attributes : & [ KeyValue ] ) {
77
- let gauge = self . meter . f64_gauge ( key. name ( ) . to_string ( ) ) . build ( ) ;
78
-
102
+ let name = format ! ( "mountpoint.{}" , key. name( ) ) ;
103
+ let mut gauges = self . gauges . lock ( ) . unwrap ( ) ;
104
+ let gauge = gauges
105
+ . entry ( name. clone ( ) )
106
+ . or_insert_with ( || self . meter . f64_gauge ( name) . build ( ) ) ;
79
107
gauge. record ( value, attributes) ;
80
108
}
81
109
82
110
/// Record a histogram metric in OTel format
83
111
pub fn record_histogram ( & self , key : & Key , value : f64 , attributes : & [ KeyValue ] ) {
84
- let histogram = self . meter . f64_histogram ( key. name ( ) . to_string ( ) ) . build ( ) ;
85
-
112
+ let name = format ! ( "mountpoint.{}" , key. name( ) ) ;
113
+ let mut histograms = self . histograms . lock ( ) . unwrap ( ) ;
114
+ let histogram = histograms
115
+ . entry ( name. clone ( ) )
116
+ . or_insert_with ( || self . meter . f64_histogram ( name) . build ( ) ) ;
86
117
histogram. record ( value, attributes) ;
87
118
}
119
+
120
+ /// Record a metric using its MetricValue
121
+ pub fn record_metric ( & self , key : & Key , value : & MetricValue , attributes : & [ KeyValue ] ) {
122
+ match value {
123
+ MetricValue :: Counter ( count) => self . record_counter ( key, * count, attributes) ,
124
+ MetricValue :: Gauge ( val) => self . record_gauge ( key, * val, attributes) ,
125
+ MetricValue :: Histogram ( _mean) => {
126
+ // Do nothing for histograms for now
127
+ // Will be implemented later
128
+ }
129
+ }
130
+ }
88
131
}
89
132
90
133
#[ cfg( test) ]
0 commit comments