1use crate::encoding::ProtobufSerializer;
2use bytes::BytesMut;
3use opentelemetry_proto::metrics::metric_event_to_export_request;
4use opentelemetry_proto::proto::{
5 DESCRIPTOR_BYTES, LOGS_REQUEST_MESSAGE_TYPE, METRICS_REQUEST_MESSAGE_TYPE,
6 RESOURCE_LOGS_JSON_FIELD, RESOURCE_METRICS_JSON_FIELD, RESOURCE_SPANS_JSON_FIELD,
7 TRACES_REQUEST_MESSAGE_TYPE,
8};
9use prost::Message;
10use tokio_util::codec::Encoder;
11use vector_config_macros::configurable_component;
12use vector_core::{config::DataType, event::Event, schema};
13use vrl::{event_path, protobuf::encode::Options};
14
15#[configurable_component]
17#[derive(Debug, Clone, Default)]
18pub struct OtlpSerializerConfig {
19 }
21
22impl OtlpSerializerConfig {
23 pub fn build(&self) -> Result<OtlpSerializer, crate::encoding::BuildError> {
25 OtlpSerializer::new()
26 }
27
28 pub fn input_type(&self) -> DataType {
30 DataType::all_bits()
31 }
32
33 pub fn schema_requirement(&self) -> schema::Requirement {
35 schema::Requirement::empty()
36 }
37}
38
39#[derive(Debug, Clone)]
56#[allow(dead_code)] pub struct OtlpSerializer {
58 logs_descriptor: ProtobufSerializer,
59 metrics_descriptor: ProtobufSerializer,
60 traces_descriptor: ProtobufSerializer,
61 options: Options,
62}
63
64impl OtlpSerializer {
65 pub fn new() -> vector_common::Result<Self> {
67 let options = Options {
68 use_json_names: true,
69 allow_lossy_string_coercion: true,
70 };
71
72 let logs_descriptor = ProtobufSerializer::new_from_bytes(
73 DESCRIPTOR_BYTES,
74 LOGS_REQUEST_MESSAGE_TYPE,
75 &options,
76 )?;
77
78 let metrics_descriptor = ProtobufSerializer::new_from_bytes(
79 DESCRIPTOR_BYTES,
80 METRICS_REQUEST_MESSAGE_TYPE,
81 &options,
82 )?;
83
84 let traces_descriptor = ProtobufSerializer::new_from_bytes(
85 DESCRIPTOR_BYTES,
86 TRACES_REQUEST_MESSAGE_TYPE,
87 &options,
88 )?;
89
90 Ok(Self {
91 logs_descriptor,
92 metrics_descriptor,
93 traces_descriptor,
94 options,
95 })
96 }
97}
98
99impl Encoder<Event> for OtlpSerializer {
100 type Error = vector_common::Error;
101
102 fn encode(&mut self, event: Event, buffer: &mut BytesMut) -> Result<(), Self::Error> {
103 match event {
104 Event::Log(log) => {
105 if log.contains(event_path!(RESOURCE_LOGS_JSON_FIELD)) {
106 self.logs_descriptor.encode(Event::Log(log), buffer)
107 } else if log.contains(event_path!(RESOURCE_METRICS_JSON_FIELD)) {
108 self.metrics_descriptor.encode(Event::Log(log), buffer)
110 } else {
111 Err(format!(
112 "Log event does not contain OTLP top-level fields ({RESOURCE_LOGS_JSON_FIELD} or {RESOURCE_METRICS_JSON_FIELD})",
113 )
114 .into())
115 }
116 }
117 Event::Trace(trace) => {
118 if trace.contains(event_path!(RESOURCE_SPANS_JSON_FIELD)) {
119 self.traces_descriptor.encode(Event::Trace(trace), buffer)
120 } else {
121 Err(format!(
122 "Trace event does not contain OTLP top-level field ({RESOURCE_SPANS_JSON_FIELD})",
123 )
124 .into())
125 }
126 }
127 Event::Metric(metric) => {
128 let request = metric_event_to_export_request(metric)?;
129 request.encode(buffer).map_err(Into::into)
130 }
131 }
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use chrono::{TimeZone, Utc};
139 use opentelemetry_proto::proto::collector::metrics::v1::ExportMetricsServiceRequest;
140 use vector_core::event::{Metric, MetricKind, MetricTags, MetricValue, metric::Bucket};
141
142 fn with_empty_tags(metric: Metric) -> Metric {
146 metric.with_tags(Some(MetricTags::default()))
147 }
148
149 fn round_trip_metric(metric: Metric) -> Metric {
150 let mut serializer = OtlpSerializer::new().unwrap();
151 let mut buffer = BytesMut::new();
152 serializer
153 .encode(Event::Metric(metric), &mut buffer)
154 .expect("encode should succeed");
155
156 let request =
157 ExportMetricsServiceRequest::decode(buffer.freeze()).expect("decode should succeed");
158 let mut events: Vec<Event> = request
159 .resource_metrics
160 .into_iter()
161 .flat_map(|rm| rm.into_event_iter())
162 .collect();
163
164 assert_eq!(events.len(), 1);
165 match events.remove(0) {
166 Event::Metric(metric) => metric,
167 other => panic!("expected a metric event, got {other:?}"),
168 }
169 }
170
171 #[test]
172 fn round_trip_counter() {
173 let metric = with_empty_tags(
174 Metric::new(
175 "requests",
176 MetricKind::Incremental,
177 MetricValue::Counter { value: 42.0 },
178 )
179 .with_timestamp(Some(Utc.timestamp_nanos(1_000_000_000))),
180 );
181
182 assert_eq!(metric.clone(), round_trip_metric(metric));
183 }
184
185 #[test]
186 fn round_trip_gauge() {
187 let metric = with_empty_tags(
188 Metric::new(
189 "cpu_usage",
190 MetricKind::Absolute,
191 MetricValue::Gauge { value: 12.5 },
192 )
193 .with_timestamp(Some(Utc.timestamp_nanos(1_000_000_000))),
194 );
195
196 assert_eq!(metric.clone(), round_trip_metric(metric));
197 }
198
199 #[test]
200 fn round_trip_aggregated_histogram() {
201 let metric = with_empty_tags(
202 Metric::new(
203 "latency",
204 MetricKind::Absolute,
205 MetricValue::AggregatedHistogram {
206 buckets: vec![
207 Bucket {
208 upper_limit: 1.0,
209 count: 1,
210 },
211 Bucket {
212 upper_limit: 2.0,
213 count: 2,
214 },
215 Bucket {
216 upper_limit: f64::INFINITY,
217 count: 3,
218 },
219 ],
220 count: 6,
221 sum: 10.0,
222 },
223 )
224 .with_timestamp(Some(Utc.timestamp_nanos(1_000_000_000))),
225 );
226
227 assert_eq!(metric.clone(), round_trip_metric(metric));
228 }
229
230 #[test]
231 fn round_trip_aggregated_summary() {
232 let metric = with_empty_tags(
233 Metric::new(
234 "response_time",
235 MetricKind::Absolute,
236 MetricValue::AggregatedSummary {
237 quantiles: vec![
238 vector_core::event::metric::Quantile {
239 quantile: 0.5,
240 value: 10.0,
241 },
242 vector_core::event::metric::Quantile {
243 quantile: 0.99,
244 value: 20.0,
245 },
246 ],
247 count: 100,
248 sum: 1000.0,
249 },
250 )
251 .with_timestamp(Some(Utc.timestamp_nanos(1_000_000_000))),
252 );
253
254 assert_eq!(metric.clone(), round_trip_metric(metric));
255 }
256
257 #[test]
258 fn unsupported_metric_values_return_err() {
259 let mut serializer = OtlpSerializer::new().unwrap();
260 let mut buffer = BytesMut::new();
261
262 let set_metric = Metric::new(
263 "unique_users",
264 MetricKind::Incremental,
265 MetricValue::Set {
266 values: std::iter::once("a".to_string()).collect(),
267 },
268 );
269 assert!(
270 serializer
271 .encode(Event::Metric(set_metric), &mut buffer)
272 .is_err()
273 );
274
275 let distribution_metric = Metric::new(
276 "latencies",
277 MetricKind::Incremental,
278 MetricValue::Distribution {
279 samples: Vec::new(),
280 statistic: vector_core::event::metric::StatisticKind::Histogram,
281 },
282 );
283 assert!(
284 serializer
285 .encode(Event::Metric(distribution_metric), &mut buffer)
286 .is_err()
287 );
288 }
289}