18
18
19
19
use std:: collections:: { HashMap , HashSet } ;
20
20
21
- use actix_web:: web:: { Json , Path } ;
21
+ use actix_web:: web:: { self , Json , Path } ;
22
22
use actix_web:: { HttpRequest , HttpResponse , http:: header:: ContentType } ;
23
23
use arrow_array:: RecordBatch ;
24
24
use bytes:: Bytes ;
@@ -33,20 +33,16 @@ use crate::handlers::http::modal::utils::ingest_utils::push_logs;
33
33
use crate :: handlers:: { EXTRACT_LOG_KEY , LOG_SOURCE_KEY , STREAM_NAME_HEADER_KEY } ;
34
34
use crate :: metadata:: SchemaVersion ;
35
35
use crate :: option:: Mode ;
36
- use crate :: otel:: logs:: { flatten_otel_protobuf , OTEL_LOG_KNOWN_FIELD_LIST } ;
37
- use crate :: otel:: metrics:: OTEL_METRICS_KNOWN_FIELD_LIST ;
38
- use crate :: otel:: traces:: OTEL_TRACES_KNOWN_FIELD_LIST ;
36
+ use crate :: otel:: logs:: { OTEL_LOG_KNOWN_FIELD_LIST , flatten_otel_protobuf } ;
37
+ use crate :: otel:: metrics:: { OTEL_METRICS_KNOWN_FIELD_LIST , flatten_otel_metrics_protobuf } ;
38
+ use crate :: otel:: traces:: { OTEL_TRACES_KNOWN_FIELD_LIST , flatten_otel_traces_protobuf } ;
39
39
use crate :: parseable:: { PARSEABLE , StreamNotFound } ;
40
40
use crate :: storage:: { ObjectStorageError , StreamType } ;
41
41
use crate :: utils:: header_parsing:: ParseHeaderError ;
42
42
use crate :: utils:: json:: { flatten:: JsonFlattenError , strict:: StrictValue } ;
43
- use actix_web:: web:: { self , Json , Path } ;
44
- use actix_web:: { http:: header:: ContentType , HttpRequest , HttpResponse } ;
45
- use arrow_array:: RecordBatch ;
46
- use bytes:: Bytes ;
47
- use chrono:: Utc ;
48
- use http:: StatusCode ;
49
43
use opentelemetry_proto:: tonic:: collector:: logs:: v1:: ExportLogsServiceRequest ;
44
+ use opentelemetry_proto:: tonic:: collector:: metrics:: v1:: ExportMetricsServiceRequest ;
45
+ use opentelemetry_proto:: tonic:: collector:: trace:: v1:: ExportTraceServiceRequest ;
50
46
use prost:: Message ;
51
47
52
48
use super :: logstream:: error:: { CreateStreamError , StreamError } ;
@@ -229,14 +225,25 @@ pub async fn handle_otel_logs_ingestion(
229
225
}
230
226
231
227
if content_type == "application/x-protobuf" {
228
+ const MAX_PROTOBUF_SIZE : usize = 10 * 1024 * 1024 ; // 10MB limit
229
+ if body. len ( ) > MAX_PROTOBUF_SIZE {
230
+ return Err ( PostError :: Invalid ( anyhow:: anyhow!(
231
+ "Protobuf message size {} exceeds maximum allowed size of {} bytes" ,
232
+ body. len( ) ,
233
+ MAX_PROTOBUF_SIZE
234
+ ) ) ) ;
235
+ }
232
236
match ExportLogsServiceRequest :: decode ( body) {
233
237
Ok ( json) => {
234
238
for record in flatten_otel_protobuf ( & json) {
235
239
push_logs ( & stream_name, record, & log_source, & p_custom_fields) . await ?;
236
240
}
237
241
}
238
242
Err ( e) => {
239
- return Err ( PostError :: Invalid ( e. into ( ) ) ) ;
243
+ return Err ( PostError :: Invalid ( anyhow:: anyhow!(
244
+ "Failed to decode protobuf message: {}" ,
245
+ e
246
+ ) ) ) ;
240
247
}
241
248
}
242
249
}
@@ -254,7 +261,7 @@ pub async fn handle_otel_logs_ingestion(
254
261
// creates if stream does not exist
255
262
pub async fn handle_otel_metrics_ingestion (
256
263
req : HttpRequest ,
257
- Json ( json ) : Json < StrictValue > ,
264
+ body : web :: Bytes ,
258
265
) -> Result < HttpResponse , PostError > {
259
266
let Some ( stream_name) = req. headers ( ) . get ( STREAM_NAME_HEADER_KEY ) else {
260
267
return Err ( PostError :: Header ( ParseHeaderError :: MissingStreamName ) ) ;
@@ -303,13 +310,35 @@ pub async fn handle_otel_metrics_ingestion(
303
310
304
311
let p_custom_fields = get_custom_fields_from_header ( & req) ;
305
312
306
- flatten_and_push_logs (
307
- json. into_inner ( ) ,
308
- & stream_name,
309
- & log_source,
310
- & p_custom_fields,
311
- )
312
- . await ?;
313
+ match req. headers ( ) . get ( "Content-Type" ) {
314
+ Some ( content_type) => {
315
+ if content_type == "application/json" {
316
+ flatten_and_push_logs (
317
+ serde_json:: from_slice ( & body) ?,
318
+ & stream_name,
319
+ & log_source,
320
+ & p_custom_fields,
321
+ )
322
+ . await ?;
323
+ }
324
+
325
+ if content_type == "application/x-protobuf" {
326
+ match ExportMetricsServiceRequest :: decode ( body) {
327
+ Ok ( json) => {
328
+ for record in flatten_otel_metrics_protobuf ( & json) {
329
+ push_logs ( & stream_name, record, & log_source, & p_custom_fields) . await ?;
330
+ }
331
+ }
332
+ Err ( e) => {
333
+ return Err ( PostError :: Invalid ( e. into ( ) ) ) ;
334
+ }
335
+ }
336
+ }
337
+ }
338
+ None => {
339
+ return Err ( PostError :: Header ( ParseHeaderError :: InvalidValue ) ) ;
340
+ }
341
+ }
313
342
314
343
Ok ( HttpResponse :: Ok ( ) . finish ( ) )
315
344
}
@@ -319,7 +348,7 @@ pub async fn handle_otel_metrics_ingestion(
319
348
// creates if stream does not exist
320
349
pub async fn handle_otel_traces_ingestion (
321
350
req : HttpRequest ,
322
- Json ( json ) : Json < StrictValue > ,
351
+ body : web :: Bytes ,
323
352
) -> Result < HttpResponse , PostError > {
324
353
let Some ( stream_name) = req. headers ( ) . get ( STREAM_NAME_HEADER_KEY ) else {
325
354
return Err ( PostError :: Header ( ParseHeaderError :: MissingStreamName ) ) ;
@@ -369,13 +398,35 @@ pub async fn handle_otel_traces_ingestion(
369
398
370
399
let p_custom_fields = get_custom_fields_from_header ( & req) ;
371
400
372
- flatten_and_push_logs (
373
- json. into_inner ( ) ,
374
- & stream_name,
375
- & log_source,
376
- & p_custom_fields,
377
- )
378
- . await ?;
401
+ match req. headers ( ) . get ( "Content-Type" ) {
402
+ Some ( content_type) => {
403
+ if content_type == "application/json" {
404
+ flatten_and_push_logs (
405
+ serde_json:: from_slice ( & body) ?,
406
+ & stream_name,
407
+ & log_source,
408
+ & p_custom_fields,
409
+ )
410
+ . await ?;
411
+ }
412
+
413
+ if content_type == "application/x-protobuf" {
414
+ match ExportTraceServiceRequest :: decode ( body) {
415
+ Ok ( json) => {
416
+ for record in flatten_otel_traces_protobuf ( & json) {
417
+ push_logs ( & stream_name, record, & log_source, & p_custom_fields) . await ?;
418
+ }
419
+ }
420
+ Err ( e) => {
421
+ return Err ( PostError :: Invalid ( e. into ( ) ) ) ;
422
+ }
423
+ }
424
+ }
425
+ }
426
+ None => {
427
+ return Err ( PostError :: Header ( ParseHeaderError :: InvalidValue ) ) ;
428
+ }
429
+ }
379
430
380
431
Ok ( HttpResponse :: Ok ( ) . finish ( ) )
381
432
}
0 commit comments