@@ -36,13 +36,15 @@ use crate::handlers::{
36
36
use crate :: metadata:: SchemaVersion ;
37
37
use crate :: option:: Mode ;
38
38
use crate :: otel:: logs:: { OTEL_LOG_KNOWN_FIELD_LIST , flatten_otel_protobuf} ;
39
- use crate :: otel:: metrics:: OTEL_METRICS_KNOWN_FIELD_LIST ;
40
- use crate :: otel:: traces:: OTEL_TRACES_KNOWN_FIELD_LIST ;
39
+ use crate :: otel:: metrics:: { OTEL_METRICS_KNOWN_FIELD_LIST , flatten_otel_metrics_protobuf } ;
40
+ use crate :: otel:: traces:: { OTEL_TRACES_KNOWN_FIELD_LIST , flatten_otel_traces_protobuf } ;
41
41
use crate :: parseable:: { PARSEABLE , StreamNotFound } ;
42
42
use crate :: storage:: { ObjectStorageError , StreamType } ;
43
43
use crate :: utils:: header_parsing:: ParseHeaderError ;
44
44
use crate :: utils:: json:: { flatten:: JsonFlattenError , strict:: StrictValue } ;
45
45
use opentelemetry_proto:: tonic:: collector:: logs:: v1:: ExportLogsServiceRequest ;
46
+ use opentelemetry_proto:: tonic:: collector:: metrics:: v1:: ExportMetricsServiceRequest ;
47
+ use opentelemetry_proto:: tonic:: collector:: trace:: v1:: ExportTraceServiceRequest ;
46
48
use prost:: Message ;
47
49
48
50
use super :: logstream:: error:: { CreateStreamError , StreamError } ;
@@ -233,14 +235,25 @@ pub async fn handle_otel_logs_ingestion(
233
235
}
234
236
235
237
if content_type == "application/x-protobuf" {
238
+ const MAX_PROTOBUF_SIZE : usize = 10 * 1024 * 1024 ; // 10MB limit
239
+ if body. len ( ) > MAX_PROTOBUF_SIZE {
240
+ return Err ( PostError :: Invalid ( anyhow:: anyhow!(
241
+ "Protobuf message size {} exceeds maximum allowed size of {} bytes" ,
242
+ body. len( ) ,
243
+ MAX_PROTOBUF_SIZE
244
+ ) ) ) ;
245
+ }
236
246
match ExportLogsServiceRequest :: decode ( body) {
237
247
Ok ( json) => {
238
248
for record in flatten_otel_protobuf ( & json) {
239
249
push_logs ( & stream_name, record, & log_source, & p_custom_fields) . await ?;
240
250
}
241
251
}
242
252
Err ( e) => {
243
- return Err ( PostError :: Invalid ( e. into ( ) ) ) ;
253
+ return Err ( PostError :: Invalid ( anyhow:: anyhow!(
254
+ "Failed to decode protobuf message: {}" ,
255
+ e
256
+ ) ) ) ;
244
257
}
245
258
}
246
259
}
@@ -258,7 +271,7 @@ pub async fn handle_otel_logs_ingestion(
258
271
// creates if stream does not exist
259
272
pub async fn handle_otel_metrics_ingestion (
260
273
req : HttpRequest ,
261
- Json ( json ) : Json < StrictValue > ,
274
+ body : web :: Bytes ,
262
275
) -> Result < HttpResponse , PostError > {
263
276
let Some ( stream_name) = req. headers ( ) . get ( STREAM_NAME_HEADER_KEY ) else {
264
277
return Err ( PostError :: Header ( ParseHeaderError :: MissingStreamName ) ) ;
@@ -308,13 +321,35 @@ pub async fn handle_otel_metrics_ingestion(
308
321
309
322
let p_custom_fields = get_custom_fields_from_header ( & req) ;
310
323
311
- flatten_and_push_logs (
312
- json. into_inner ( ) ,
313
- & stream_name,
314
- & log_source,
315
- & p_custom_fields,
316
- )
317
- . await ?;
324
+ match req. headers ( ) . get ( "Content-Type" ) {
325
+ Some ( content_type) => {
326
+ if content_type == "application/json" {
327
+ flatten_and_push_logs (
328
+ serde_json:: from_slice ( & body) ?,
329
+ & stream_name,
330
+ & log_source,
331
+ & p_custom_fields,
332
+ )
333
+ . await ?;
334
+ }
335
+
336
+ if content_type == "application/x-protobuf" {
337
+ match ExportMetricsServiceRequest :: decode ( body) {
338
+ Ok ( json) => {
339
+ for record in flatten_otel_metrics_protobuf ( & json) {
340
+ push_logs ( & stream_name, record, & log_source, & p_custom_fields) . await ?;
341
+ }
342
+ }
343
+ Err ( e) => {
344
+ return Err ( PostError :: Invalid ( e. into ( ) ) ) ;
345
+ }
346
+ }
347
+ }
348
+ }
349
+ None => {
350
+ return Err ( PostError :: Header ( ParseHeaderError :: InvalidValue ) ) ;
351
+ }
352
+ }
318
353
319
354
Ok ( HttpResponse :: Ok ( ) . finish ( ) )
320
355
}
@@ -324,7 +359,7 @@ pub async fn handle_otel_metrics_ingestion(
324
359
// creates if stream does not exist
325
360
pub async fn handle_otel_traces_ingestion (
326
361
req : HttpRequest ,
327
- Json ( json ) : Json < StrictValue > ,
362
+ body : web :: Bytes ,
328
363
) -> Result < HttpResponse , PostError > {
329
364
let Some ( stream_name) = req. headers ( ) . get ( STREAM_NAME_HEADER_KEY ) else {
330
365
return Err ( PostError :: Header ( ParseHeaderError :: MissingStreamName ) ) ;
@@ -375,13 +410,35 @@ pub async fn handle_otel_traces_ingestion(
375
410
376
411
let p_custom_fields = get_custom_fields_from_header ( & req) ;
377
412
378
- flatten_and_push_logs (
379
- json. into_inner ( ) ,
380
- & stream_name,
381
- & log_source,
382
- & p_custom_fields,
383
- )
384
- . await ?;
413
+ match req. headers ( ) . get ( "Content-Type" ) {
414
+ Some ( content_type) => {
415
+ if content_type == "application/json" {
416
+ flatten_and_push_logs (
417
+ serde_json:: from_slice ( & body) ?,
418
+ & stream_name,
419
+ & log_source,
420
+ & p_custom_fields,
421
+ )
422
+ . await ?;
423
+ }
424
+
425
+ if content_type == "application/x-protobuf" {
426
+ match ExportTraceServiceRequest :: decode ( body) {
427
+ Ok ( json) => {
428
+ for record in flatten_otel_traces_protobuf ( & json) {
429
+ push_logs ( & stream_name, record, & log_source, & p_custom_fields) . await ?;
430
+ }
431
+ }
432
+ Err ( e) => {
433
+ return Err ( PostError :: Invalid ( e. into ( ) ) ) ;
434
+ }
435
+ }
436
+ }
437
+ }
438
+ None => {
439
+ return Err ( PostError :: Header ( ParseHeaderError :: InvalidValue ) ) ;
440
+ }
441
+ }
385
442
386
443
Ok ( HttpResponse :: Ok ( ) . finish ( ) )
387
444
}
0 commit comments