55//! handoff to precompute engine as TODO.
66
77use std:: collections:: HashMap ;
8+ use std:: io:: Read ;
89
910use axum:: { body:: Bytes , extract:: State , routing:: post, Json , Router } ;
11+ use flate2:: read:: GzDecoder ;
1012use opentelemetry_proto:: tonic:: collector:: metrics:: v1:: {
1113 metrics_service_server:: MetricsService , ExportMetricsServiceRequest ,
1214 ExportMetricsServiceResponse ,
1315} ;
16+ use opentelemetry_proto:: tonic:: common:: v1:: any_value:: Value as AnyValueVariant ;
1417use opentelemetry_proto:: tonic:: metrics:: v1:: number_data_point:: Value as NumberValue ;
1518use prost:: Message ;
19+ use sketchlib_rust:: proto:: sketchlib:: { sketch_envelope, SketchEnvelope } ;
1620use tonic:: { Request , Response , Status } ;
1721use tracing:: { debug, error, info} ;
1822
@@ -80,25 +84,49 @@ impl MetricsService for MetricsServiceImpl {
8084 & self ,
8185 request : Request < ExportMetricsServiceRequest > ,
8286 ) -> Result < Response < ExportMetricsServiceResponse > , Status > {
87+ debug ! ( "OTLP received request via gRPC" ) ;
8388 let req = request. into_inner ( ) ;
84- process_otlp_request ( & req) ;
89+ process_otlp_request ( & req, "gRPC" ) ;
90+ debug ! ( "OTLP sending response via gRPC" ) ;
8591 Ok ( Response :: new ( ExportMetricsServiceResponse {
8692 partial_success : None ,
8793 } ) )
8894 }
8995}
9096
9197async fn handle_otlp_http (
98+ headers : axum:: http:: HeaderMap ,
9299 State ( _state) : State < ( ) > ,
93100 body : Bytes ,
94101) -> Result < Json < serde_json:: Value > , ( axum:: http:: StatusCode , String ) > {
102+ debug ! ( "OTLP received request via HTTP, body_bytes={}" , body. len( ) ) ;
103+ let body = if let Some ( enc) = headers. get ( axum:: http:: header:: CONTENT_ENCODING ) {
104+ let enc = enc. to_str ( ) . unwrap_or ( "" ) . trim ( ) . to_ascii_lowercase ( ) ;
105+ if enc == "gzip" {
106+ let mut decoder = GzDecoder :: new ( body. as_ref ( ) ) ;
107+ let mut out = Vec :: new ( ) ;
108+ decoder. read_to_end ( & mut out) . map_err ( |e| {
109+ (
110+ axum:: http:: StatusCode :: BAD_REQUEST ,
111+ format ! ( "Gzip decode error: {}" , e) ,
112+ )
113+ } ) ?;
114+ Bytes :: from ( out)
115+ } else {
116+ body
117+ }
118+ } else {
119+ body
120+ } ;
121+
95122 let req = ExportMetricsServiceRequest :: decode ( body. as_ref ( ) ) . map_err ( |e| {
96123 (
97124 axum:: http:: StatusCode :: BAD_REQUEST ,
98125 format ! ( "Protobuf decode error: {}" , e) ,
99126 )
100127 } ) ?;
101- process_otlp_request ( & req) ;
128+ process_otlp_request ( & req, "HTTP" ) ;
129+ debug ! ( "OTLP sending response via HTTP" ) ;
102130 Ok ( Json ( serde_json:: json!( { "rejected" : 0 } ) ) )
103131}
104132
@@ -111,15 +139,101 @@ pub struct MetricPoint {
111139 pub value : f64 ,
112140}
113141
114- fn process_otlp_request ( request : & ExportMetricsServiceRequest ) {
142+ fn format_series_key ( name : & str , labels : & HashMap < String , String > ) -> String {
143+ let mut pairs: Vec < _ > = labels. iter ( ) . collect ( ) ;
144+ pairs. sort_by_key ( |( k, _) | * k) ;
145+ let labels_str = pairs
146+ . iter ( )
147+ . map ( |( k, v) | format ! ( "{}={}" , k, v) )
148+ . collect :: < Vec < _ > > ( )
149+ . join ( "," ) ;
150+ format ! ( "{}{{{}}}" , name, labels_str)
151+ }
152+
153+ fn get_sketch_payload_from_attrs (
154+ attrs : & [ opentelemetry_proto:: tonic:: common:: v1:: KeyValue ] ,
155+ ) -> Option < ( String , Vec < u8 > ) > {
156+ for kv in attrs {
157+ match kv. key . as_str ( ) {
158+ "kll.sketch_payload" | "cms.sketch_payload" | "countsketch.sketch_payload" => {
159+ if let Some ( value) = & kv. value {
160+ if let Some ( AnyValueVariant :: BytesValue ( bytes) ) = & value. value {
161+ return Some ( ( kv. key . clone ( ) , bytes. clone ( ) ) ) ;
162+ }
163+ }
164+ }
165+ _ => { }
166+ }
167+ }
168+ None
169+ }
170+
171+ fn log_sketch_envelope_type ( attr_name : & str , payload : & [ u8 ] , metric_name : & str ) {
172+ match SketchEnvelope :: decode ( payload) {
173+ Ok ( env) => {
174+ let sketch_type = match env. sketch_state {
175+ Some ( sketch_envelope:: SketchState :: Kll ( _) ) => "KLL" ,
176+ Some ( sketch_envelope:: SketchState :: CountMin ( _) ) => "CountMin" ,
177+ Some ( sketch_envelope:: SketchState :: CountSketch ( _) ) => "CountSketch" ,
178+ Some ( _) => "Other" ,
179+ None => "Unknown" ,
180+ } ;
181+ debug ! (
182+ "OTLP Sketches: metric='{}' attr='{}' payload_bytes={} sketch_type={}" ,
183+ metric_name,
184+ attr_name,
185+ payload. len( ) ,
186+ sketch_type
187+ ) ;
188+ }
189+ Err ( e) => {
190+ debug ! (
191+ "OTLP Sketches: metric='{}' attr='{}' payload_bytes={} decode_error='{}'" ,
192+ metric_name,
193+ attr_name,
194+ payload. len( ) ,
195+ e
196+ ) ;
197+ }
198+ }
199+ }
200+
201+ fn process_otlp_request ( request : & ExportMetricsServiceRequest , transport : & str ) {
115202 let resource_count = request. resource_metrics . len ( ) ;
116203 let total_points = otlp_to_record_count ( request) ;
117- debug ! (
118- "OTLP ingest: received {} resource metrics, {} total data points" ,
119- resource_count, total_points
120- ) ;
204+ if resource_count > 0 || total_points > 0 {
205+ debug ! (
206+ "OTLP ingest: received {} resource metrics, {} total data points (transport={})" ,
207+ resource_count, total_points, transport
208+ ) ;
209+ }
210+
211+ let ( points, sketch_payloads) = otlp_to_metric_points_and_sketches ( request) ;
212+
213+ for ( metric_name, attr_name, payload) in & sketch_payloads {
214+ log_sketch_envelope_type ( attr_name, payload, metric_name) ;
215+ }
216+ if !sketch_payloads. is_empty ( ) {
217+ debug ! (
218+ "OTLP Sketch Payload Flow: received {} sketch payload(s), decoded successfully" ,
219+ sketch_payloads. len( )
220+ ) ;
221+ }
121222
122- let points = otlp_to_metric_points ( request) ;
223+ let mut by_series: HashMap < String , usize > = HashMap :: new ( ) ;
224+ for point in & points {
225+ let key = format_series_key ( & point. name , & point. labels ) ;
226+ * by_series. entry ( key) . or_insert ( 0 ) += 1 ;
227+ }
228+ if !by_series. is_empty ( ) {
229+ debug ! (
230+ "OTLP Raw Metrics Flow: received {} raw metric series" ,
231+ by_series. len( )
232+ ) ;
233+ for ( series, count) in by_series {
234+ debug ! ( "OTLP Raw Metrics Flow: series {} count={}" , series, count) ;
235+ }
236+ }
123237 if let Some ( first) = points. first ( ) {
124238 debug ! (
125239 "OTLP parse example: {} {:?} @{}ns = {}" ,
@@ -178,8 +292,13 @@ fn otlp_to_record_count(request: &ExportMetricsServiceRequest) -> usize {
178292}
179293
180294/// Parse OTLP request and convert to metric data points (name, labels, timestamp, value).
181- fn otlp_to_metric_points ( request : & ExportMetricsServiceRequest ) -> Vec < MetricPoint > {
295+ /// Data points with sketch payloads in attributes are excluded from points and returned
296+ /// separately for Sketch Payload Flow processing.
297+ fn otlp_to_metric_points_and_sketches (
298+ request : & ExportMetricsServiceRequest ,
299+ ) -> ( Vec < MetricPoint > , Vec < ( String , String , Vec < u8 > ) > ) {
182300 let mut points = Vec :: new ( ) ;
301+ let mut sketch_payloads = Vec :: new ( ) ;
183302 for resource_metrics in & request. resource_metrics {
184303 let resource_attrs = resource_metrics
185304 . resource
@@ -209,6 +328,12 @@ fn otlp_to_metric_points(request: &ExportMetricsServiceRequest) -> Vec<MetricPoi
209328 match & metric. data {
210329 Some ( Data :: Gauge ( g) ) => {
211330 for dp in & g. data_points {
331+ if let Some ( ( attr_name, payload) ) =
332+ get_sketch_payload_from_attrs ( & dp. attributes )
333+ {
334+ sketch_payloads. push ( ( metric. name . clone ( ) , attr_name, payload) ) ;
335+ continue ;
336+ }
212337 let labels = merge_point_attributes ( & base_labels, & dp. attributes ) ;
213338 let value = number_value_to_f64 ( & dp. value ) ;
214339 points. push ( MetricPoint {
@@ -221,6 +346,12 @@ fn otlp_to_metric_points(request: &ExportMetricsServiceRequest) -> Vec<MetricPoi
221346 }
222347 Some ( Data :: Sum ( s) ) => {
223348 for dp in & s. data_points {
349+ if let Some ( ( attr_name, payload) ) =
350+ get_sketch_payload_from_attrs ( & dp. attributes )
351+ {
352+ sketch_payloads. push ( ( metric. name . clone ( ) , attr_name, payload) ) ;
353+ continue ;
354+ }
224355 let labels = merge_point_attributes ( & base_labels, & dp. attributes ) ;
225356 let value = number_value_to_f64 ( & dp. value ) ;
226357 points. push ( MetricPoint {
@@ -233,6 +364,12 @@ fn otlp_to_metric_points(request: &ExportMetricsServiceRequest) -> Vec<MetricPoi
233364 }
234365 Some ( Data :: Histogram ( hist) ) => {
235366 for dp in & hist. data_points {
367+ if let Some ( ( attr_name, payload) ) =
368+ get_sketch_payload_from_attrs ( & dp. attributes )
369+ {
370+ sketch_payloads. push ( ( metric. name . clone ( ) , attr_name, payload) ) ;
371+ continue ;
372+ }
236373 let labels = merge_point_attributes ( & base_labels, & dp. attributes ) ;
237374 if let Some ( sum) = dp. sum {
238375 points. push ( MetricPoint {
@@ -252,6 +389,12 @@ fn otlp_to_metric_points(request: &ExportMetricsServiceRequest) -> Vec<MetricPoi
252389 }
253390 Some ( Data :: ExponentialHistogram ( eh) ) => {
254391 for dp in & eh. data_points {
392+ if let Some ( ( attr_name, payload) ) =
393+ get_sketch_payload_from_attrs ( & dp. attributes )
394+ {
395+ sketch_payloads. push ( ( metric. name . clone ( ) , attr_name, payload) ) ;
396+ continue ;
397+ }
255398 let labels = merge_point_attributes ( & base_labels, & dp. attributes ) ;
256399 if let Some ( sum) = dp. sum {
257400 points. push ( MetricPoint {
@@ -271,6 +414,12 @@ fn otlp_to_metric_points(request: &ExportMetricsServiceRequest) -> Vec<MetricPoi
271414 }
272415 Some ( Data :: Summary ( sm) ) => {
273416 for dp in & sm. data_points {
417+ if let Some ( ( attr_name, payload) ) =
418+ get_sketch_payload_from_attrs ( & dp. attributes )
419+ {
420+ sketch_payloads. push ( ( metric. name . clone ( ) , attr_name, payload) ) ;
421+ continue ;
422+ }
274423 let labels = merge_point_attributes ( & base_labels, & dp. attributes ) ;
275424 points. push ( MetricPoint {
276425 name : format ! ( "{}_sum" , metric. name) ,
@@ -291,7 +440,7 @@ fn otlp_to_metric_points(request: &ExportMetricsServiceRequest) -> Vec<MetricPoi
291440 }
292441 }
293442 }
294- points
443+ ( points, sketch_payloads )
295444}
296445
297446fn merge_point_attributes (
0 commit comments