saluki_components/decoders/otlp/
mod.rs1use std::time::Duration;
2
3use agent_data_plane_config::domains;
4use async_trait::async_trait;
5use otlp_protos::opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest;
6use prost::Message;
7use saluki_core::accounting::{MemoryBounds, MemoryBoundsBuilder};
8use saluki_core::{
9 components::{
10 decoders::{Decoder, DecoderBuilder, DecoderContext},
11 ComponentContext,
12 },
13 data_model::{event::EventType, payload::PayloadType},
14 topology::interconnect::EventBufferManager,
15};
16use saluki_error::GenericError;
17use tokio::{
18 select,
19 time::{interval, MissedTickBehavior},
20};
21use tracing::{debug, error, warn};
22
23use crate::common::otlp::traces::translator::OtlpTracesTranslator;
24use crate::common::otlp::{
25 build_metrics, Metrics, OTLP_LOGS_GRPC_SERVICE_PATH, OTLP_METRICS_GRPC_SERVICE_PATH, OTLP_TRACES_GRPC_SERVICE_PATH,
26};
27
28#[derive(Default)]
30pub struct OtlpDecoderConfiguration {
31 traces: domains::otlp::Traces,
33}
34
35impl OtlpDecoderConfiguration {
36 pub fn from_configuration(traces: &domains::otlp::Traces) -> Self {
38 Self { traces: traces.clone() }
39 }
40}
41
42#[async_trait]
43impl DecoderBuilder for OtlpDecoderConfiguration {
44 fn input_payload_type(&self) -> PayloadType {
45 PayloadType::Grpc
46 }
47
48 fn output_event_type(&self) -> EventType {
49 EventType::Trace
50 }
51
52 async fn build(&self, context: ComponentContext) -> Result<Box<dyn Decoder + Send>, GenericError> {
53 let metrics = build_metrics(&context);
54 let traces_translator = OtlpTracesTranslator::new(self.traces.clone());
55
56 Ok(Box::new(OtlpDecoder {
57 traces_translator,
58 metrics,
59 }))
60 }
61}
62
63impl MemoryBounds for OtlpDecoderConfiguration {
64 fn specify_bounds(&self, builder: &mut MemoryBoundsBuilder) {
65 builder.minimum().with_single_value::<OtlpDecoder>("decoder struct");
66 }
67}
68
69pub struct OtlpDecoder {
71 traces_translator: OtlpTracesTranslator,
72 metrics: Metrics,
73}
74
75#[async_trait]
76impl Decoder for OtlpDecoder {
77 async fn run(self: Box<Self>, mut context: DecoderContext) -> Result<(), GenericError> {
78 let Self {
79 mut traces_translator,
80 metrics,
81 } = *self;
82 let mut health = context.take_health_handle();
83 health.mark_ready();
84
85 debug!("OTLP decoder started.");
86
87 let mut buffer_flush = interval(Duration::from_millis(100));
89 buffer_flush.set_missed_tick_behavior(MissedTickBehavior::Delay);
90
91 let mut event_buffer_manager = EventBufferManager::default();
92
93 loop {
94 select! {
95 maybe_payload = context.payloads().next() => {
96 let payload = match maybe_payload {
97 Some(payload) => payload,
98 None => {
99 debug!("Payloads stream closed, shutting down decoder.");
100 break;
101 }
102 };
103
104 let grpc_payload = match payload.try_into_grpc_payload() {
105 Some(grpc) => grpc,
106 None => {
107 warn!("Received non-gRPC payload in OTLP decoder. Dropping payload.");
108 continue;
109 }
110 };
111
112 match grpc_payload.service_path() {
113 path if path == &*OTLP_TRACES_GRPC_SERVICE_PATH => {
114 let (_, _, _, body) = grpc_payload.into_parts();
115 let request = match ExportTraceServiceRequest::decode(body.into_bytes()) {
116 Ok(req) => req,
117 Err(e) => {
118 error!(error = %e, "Failed to decode OTLP trace request.");
119 continue;
120 }
121 };
122
123 for resource_spans in request.resource_spans {
124 for trace_event in traces_translator.translate_spans(resource_spans, &metrics) {
125 if let Some(event_buffer) = event_buffer_manager.try_push(trace_event) {
126 if let Err(e) = context.dispatcher().dispatch(event_buffer).await {
127 error!(error = %e, "Failed to dispatch trace events.");
128 }
129 }
130 }
131 }
132 }
133 path if path == &*OTLP_METRICS_GRPC_SERVICE_PATH => {
134 warn!("OTLP metrics decoding not yet implemented. Dropping metrics payload.");
135 }
136 path if path == &*OTLP_LOGS_GRPC_SERVICE_PATH => {
137 warn!("OTLP logs decoding not yet implemented. Dropping logs payload.");
138 }
139 path => {
140 warn!(service_path = path, "Received gRPC payload with unknown service path. Dropping payload.");
141 }
142 }
143 },
144 _ = buffer_flush.tick() => {
145 if let Some(event_buffer) = event_buffer_manager.consume() {
146 if let Err(e) = context.dispatcher().dispatch(event_buffer).await {
147 error!(error = %e, "Failed to dispatch buffered trace events.");
148 }
149 }
150 },
151 _ = health.live() => continue,
152 }
153 }
154
155 if let Some(event_buffer) = event_buffer_manager.consume() {
156 if let Err(e) = context.dispatcher().dispatch(event_buffer).await {
157 error!(error = %e, "Failed to dispatch final trace events.");
158 }
159 }
160
161 debug!("OTLP decoder stopped.");
162
163 Ok(())
164 }
165}