@@ -3,7 +3,10 @@ use opentelemetry::global;
33use opentelemetry_otlp:: { Protocol , WithExportConfig } ;
44use std:: time:: Duration ;
55
6+ use crate :: metrics:: MetricValue ;
67use metrics:: Key ;
8+ use std:: collections:: HashMap ;
9+ use std:: sync:: Mutex ;
710
811/// Configuration for OpenTelemetry metrics export
912#[ derive( Debug , Clone ) ]
@@ -19,7 +22,7 @@ impl OtlpConfig {
1922 pub fn new ( endpoint : & str ) -> Self {
2023 Self {
2124 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
2326 }
2427 }
2528
@@ -33,17 +36,31 @@ impl OtlpConfig {
3336#[ derive( Debug ) ]
3437pub struct OtlpMetricsExporter {
3538 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 > > > ,
3642}
3743
3844impl OtlpMetricsExporter {
3945 /// Create a new OtlpMetricsExporter with the specified configuration
4046 /// Returns a Result containing the new exporter or an error if initialisation failed
4147 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+
4259 // Initialise OTLP exporter using HTTP binary protocol with the specified endpoint
4360 let exporter = opentelemetry_otlp:: MetricExporter :: builder ( )
4461 . with_http ( )
4562 . with_protocol ( Protocol :: HttpBinary )
46- . with_endpoint ( & config . endpoint )
63+ . with_endpoint ( & endpoint_url )
4764 . build ( ) ?;
4865
4966 // Create a meter provider with the OTLP Metric Exporter that will collect and export metrics at regular intervals
@@ -62,29 +79,55 @@ impl OtlpMetricsExporter {
6279 // The meter will be used to create specific metric instruments (counters, gauges, histograms) and record values to them
6380 let meter = global:: meter ( "mountpoint-s3" ) ;
6481
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+ } )
6688 }
6789
6890 /// Record a counter metric in OTel format
6991 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 ( ) ) ;
7297 counter. add ( value, attributes) ;
7398 }
7499
75100 /// Record a gauge metric in OTel format
76101 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 ( ) ) ;
79107 gauge. record ( value, attributes) ;
80108 }
81109
82110 /// Record a histogram metric in OTel format
83111 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 ( ) ) ;
86117 histogram. record ( value, attributes) ;
87118 }
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+ }
88131}
89132
90133#[ cfg( test) ]
0 commit comments