1mod trace_ingest;
16pub mod trace_semconv;
17pub mod trace_types;
18
19use std::sync::Arc;
20
21use async_trait::async_trait;
22use auth::{
23 OTLP_WRITE, PermissionChecker, PermissionCheckerRef, PermissionReq, PermissionTableTarget,
24 PermissionTableTargets,
25};
26use client::Output;
27use common_catalog::consts::{trace_operations_table_name, trace_services_table_name};
28use common_error::ext::BoxedError;
29use common_query::prelude::GREPTIME_PHYSICAL_TABLE;
30use common_query::{OutputData, OutputMeta};
31use common_telemetry::{tracing, warn};
32use opentelemetry_proto::tonic::collector::logs::v1::ExportLogsServiceRequest;
33use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest;
34use operator::insert::{Inserter, admit_row_insert_batches, admit_write};
35use otel_arrow_rust::proto::opentelemetry::collector::metrics::v1::ExportMetricsServiceRequest;
36use pipeline::{GreptimePipelineParams, PipelineWay};
37use servers::error::{self, AuthSnafu, Result as ServerResult};
38use servers::http::prom_store::PHYSICAL_TABLE_PARAM;
39use servers::interceptor::{OpenTelemetryProtocolInterceptor, OpenTelemetryProtocolInterceptorRef};
40use servers::otlp;
41use servers::otlp::trace::span::TraceSpanGroup;
42use servers::query_handler::{
43 MetricsIngestOutcome, OpenTelemetryProtocolHandler, PipelineHandlerRef, TraceIngestOutcome,
44};
45use session::context::QueryContextRef;
46use snafu::ResultExt;
47use table::requests::{
48 OTLP_METRIC_COMPAT_KEY, OTLP_METRIC_COMPAT_PROM, SEMANTIC_PER_TABLE_INDEX_KEY,
49 SEMANTIC_SIGNAL_TYPE, SEMANTIC_SOURCE, SIGNAL_TYPE_LOG, SIGNAL_TYPE_METRIC,
50 SOURCE_OPENTELEMETRY,
51};
52
53use self::trace_ingest::trace_conventions;
54use crate::instance::Instance;
55use crate::metrics::{OTLP_LOGS_ROWS, OTLP_METRICS_ROWS, OTLP_RESOURCE_INFO_WRITE_ERRORS};
56
57fn trace_permission_targets(
58 table_name: &str,
59 groups: &[TraceSpanGroup],
60 ctx: &QueryContextRef,
61) -> PermissionTableTargets {
62 let catalog = ctx.current_catalog();
63 let schema = ctx.current_schema();
64 if catalog.is_empty() || schema.is_empty() || table_name.is_empty() {
65 return PermissionTableTargets::Unresolved;
66 }
67
68 let has_spans = groups.iter().any(|group| !group.spans.is_empty());
69 if !has_spans {
70 return PermissionTableTargets::resolved(Vec::new());
71 }
72
73 let mut targets = vec![PermissionTableTarget::new(catalog, &schema, table_name)];
74 if groups
75 .iter()
76 .flat_map(|group| &group.spans)
77 .any(|span| span.service_name.is_some())
78 {
79 targets.extend([
80 PermissionTableTarget::new(catalog, &schema, trace_services_table_name(table_name)),
81 PermissionTableTarget::new(catalog, &schema, trace_operations_table_name(table_name)),
82 ]);
83 }
84
85 PermissionTableTargets::resolved(targets)
86}
87
88#[async_trait]
89impl OpenTelemetryProtocolHandler for Instance {
90 #[tracing::instrument(skip_all)]
91 async fn metrics(
92 &self,
93 request: ExportMetricsServiceRequest,
94 ctx: QueryContextRef,
95 ) -> ServerResult<MetricsIngestOutcome> {
96 self.plugins
97 .get::<PermissionCheckerRef>()
98 .as_ref()
99 .check_permission(ctx.current_user(), PermissionReq::Action(OTLP_WRITE))
100 .context(AuthSnafu)?;
101
102 let interceptor_ref = self
103 .plugins
104 .get::<OpenTelemetryProtocolInterceptorRef<servers::error::Error>>();
105 interceptor_ref.pre_execute(ctx.clone())?;
106 let ctx = Arc::new(ctx.fork());
107
108 let input_names = request
109 .resource_metrics
110 .iter()
111 .flat_map(|r| r.scope_metrics.iter())
112 .flat_map(|s| s.metrics.iter().map(|m| m.name.clone()))
113 .collect::<Vec<_>>();
114
115 let is_legacy = self.check_otlp_legacy(&input_names, &ctx).await?;
117
118 let mut metric_ctx = ctx
119 .protocol_ctx()
120 .get_otlp_metric_ctx()
121 .cloned()
122 .unwrap_or_default();
123 metric_ctx.is_legacy = is_legacy;
124 metric_ctx.resource_info = self.otlp_resource_info;
125
126 let otlp::metrics::MetricsConversion {
127 requests,
128 rows,
129 semantic_index,
130 resource_info,
131 mut outcome,
132 } = otlp::metrics::to_grpc_insert_requests(request, &mut metric_ctx)?;
133 if outcome.rejected_data_points > 0 {
134 warn!(
135 "Rejected {} OTLP metrics data points: {}",
136 outcome.rejected_data_points,
137 outcome.error_message.as_deref().unwrap_or_default()
138 );
139 }
140 if outcome.accepted_data_points == 0 {
141 return Ok(outcome);
142 }
143
144 self.check_row_insert_permission(&requests, &ctx, PermissionReq::Action(OTLP_WRITE))
145 .context(AuthSnafu)?;
146 let ctx = admit_write(rows as u64, &ctx)
147 .await
148 .map_err(BoxedError::new)
149 .context(error::ExecuteGrpcQuerySnafu)?;
150 self.cache_otlp_legacy(&input_names, &ctx, is_legacy)?;
151 OTLP_METRICS_ROWS.inc_by(rows as u64);
152
153 let ctx = {
154 let mut c = (*ctx).clone();
155 c.set_extension(SEMANTIC_SIGNAL_TYPE, SIGNAL_TYPE_METRIC);
156 c.set_extension(SEMANTIC_SOURCE, SOURCE_OPENTELEMETRY);
157 if let Some(index) = semantic_index.encode(&c.current_schema()) {
161 c.set_extension(SEMANTIC_PER_TABLE_INDEX_KEY, index);
162 }
163 if !is_legacy {
164 c.set_extension(OTLP_METRIC_COMPAT_KEY, OTLP_METRIC_COMPAT_PROM.to_string());
165 }
166 Arc::new(c)
167 };
168
169 let physical_table = ctx
170 .extension(PHYSICAL_TABLE_PARAM)
171 .unwrap_or(GREPTIME_PHYSICAL_TABLE)
172 .to_string();
173 let batcher = self.logical_batcher().filter(|_| {
174 ctx.logical_batching_enabled() && !metric_ctx.is_legacy && metric_ctx.with_metric_engine
175 });
176 let batcher = if batcher.is_some()
177 && self
178 .inserter
179 .can_batch_metric_rows(&requests, &ctx, &physical_table)
180 .await
181 .map_err(BoxedError::new)
182 .context(error::ExecuteGrpcQuerySnafu)?
183 {
184 batcher
185 } else {
186 None
187 };
188
189 let output = if let Some(batcher) = batcher {
191 let (rows, cost) = batcher
192 .submit_with(requests, ctx.clone(), |mut requests| {
193 let ctx = ctx.clone();
194 async move {
195 Inserter::meter_row_inserts(&mut requests, &ctx)
196 .await
197 .map_err(BoxedError::new)
198 .context(error::ExecuteGrpcQuerySnafu)
199 }
200 })
201 .await?;
202 Output::new(
203 OutputData::AffectedRows(rows as usize),
204 OutputMeta::new_with_cost(cost as _),
205 )
206 } else if metric_ctx.is_legacy || !metric_ctx.with_metric_engine {
207 self.handle_row_inserts(requests, ctx.clone(), false, true)
208 .await
209 .map_err(BoxedError::new)
210 .context(error::ExecuteGrpcQuerySnafu)?
211 } else {
212 self.handle_metric_row_inserts(requests, ctx.clone(), physical_table)
213 .await
214 .map_err(BoxedError::new)
215 .context(error::ExecuteGrpcQuerySnafu)?
216 };
217 outcome.write_cost = output.meta.cost;
218
219 if let Some(resource_info) = resource_info {
223 let written = match self.check_row_insert_permission(
224 &resource_info,
225 &ctx,
226 PermissionReq::Action(OTLP_WRITE),
227 ) {
228 Ok(_) => self
229 .handle_row_inserts(resource_info, ctx, false, false)
230 .await
231 .map_err(BoxedError::new)
232 .map_err(|e| e.to_string()),
233 Err(e) => Err(e.to_string()),
234 };
235 match written {
236 Ok(descriptor_output) => outcome.write_cost += descriptor_output.meta.cost,
237 Err(e) => {
238 OTLP_RESOURCE_INFO_WRITE_ERRORS.inc();
239 warn!("Failed to write the OTLP resource descriptor table: {e}");
240 outcome.error_message.get_or_insert(format!(
241 "metric data was accepted, but writing the resource \
242 descriptor table `{}` failed: {e}",
243 otlp::metrics::OTEL_RESOURCE_INFO_TABLE_NAME
244 ));
245 }
246 }
247 }
248
249 Ok(outcome)
250 }
251
252 #[tracing::instrument(skip_all)]
253 async fn traces(
254 &self,
255 _pipeline_handler: PipelineHandlerRef,
256 request: ExportTraceServiceRequest,
257 pipeline: PipelineWay,
258 _pipeline_params: GreptimePipelineParams,
259 table_name: String,
260 ctx: QueryContextRef,
261 ) -> ServerResult<TraceIngestOutcome> {
262 self.plugins
263 .get::<PermissionCheckerRef>()
264 .as_ref()
265 .check_permission(ctx.current_user(), PermissionReq::Action(OTLP_WRITE))
266 .context(AuthSnafu)?;
267
268 let interceptor_ref = self
269 .plugins
270 .get::<OpenTelemetryProtocolInterceptorRef<servers::error::Error>>();
271 interceptor_ref.pre_execute(ctx.clone())?;
272 let ctx = Arc::new(ctx.fork());
273
274 let conventions = trace_conventions(&request);
276 let spans = otlp::trace::span::parse(request);
277 let targets = trace_permission_targets(&table_name, &spans, &ctx);
278 self.check_table_permission(&ctx, PermissionReq::Action(OTLP_WRITE), targets)
279 .context(AuthSnafu)?;
280 let rows = spans.iter().map(|group| group.spans.len() as u64).sum();
282 let ctx = admit_write(rows, &ctx)
283 .await
284 .map_err(BoxedError::new)
285 .context(error::ExecuteGrpcQuerySnafu)?;
286 self.ingest_trace_spans(&pipeline, table_name, spans, &conventions, ctx)
287 .await
288 }
289
290 #[tracing::instrument(skip_all)]
291 async fn logs(
292 &self,
293 pipeline_handler: PipelineHandlerRef,
294 request: ExportLogsServiceRequest,
295 pipeline: PipelineWay,
296 pipeline_params: GreptimePipelineParams,
297 table_name: String,
298 ctx: QueryContextRef,
299 ) -> ServerResult<Vec<Output>> {
300 self.plugins
301 .get::<PermissionCheckerRef>()
302 .as_ref()
303 .check_permission(ctx.current_user(), PermissionReq::Action(OTLP_WRITE))
304 .context(AuthSnafu)?;
305
306 let interceptor_ref = self
307 .plugins
308 .get::<OpenTelemetryProtocolInterceptorRef<servers::error::Error>>();
309 interceptor_ref.pre_execute(ctx.clone())?;
310 let ctx = Arc::new(ctx.fork());
311
312 let ctx = {
315 let mut c = (*ctx).clone();
316 c.set_extension(SEMANTIC_SIGNAL_TYPE, SIGNAL_TYPE_LOG);
317 c.set_extension(SEMANTIC_SOURCE, SOURCE_OPENTELEMETRY);
318 Arc::new(c)
319 };
320
321 let opt_req = otlp::logs::to_grpc_insert_requests(
322 request,
323 pipeline,
324 pipeline_params,
325 table_name,
326 &ctx,
327 pipeline_handler,
328 )
329 .await?;
330
331 let mut batches = opt_req.as_req_iter(ctx).collect::<Vec<_>>();
332 for (temp_ctx, requests) in &batches {
333 self.check_row_insert_permission(requests, temp_ctx, PermissionReq::Action(OTLP_WRITE))
334 .context(AuthSnafu)?;
335 }
336 admit_row_insert_batches(&mut batches)
337 .await
338 .map_err(BoxedError::new)
339 .context(error::ExecuteGrpcQuerySnafu)?;
340
341 let mut outputs = Vec::with_capacity(batches.len());
342 for (temp_ctx, requests) in batches {
343 let cnt = requests
344 .inserts
345 .iter()
346 .filter_map(|r| r.rows.as_ref().map(|r| r.rows.len()))
347 .sum::<usize>();
348
349 let o = self
350 .handle_log_inserts(requests, temp_ctx)
351 .await
352 .inspect(|_| OTLP_LOGS_ROWS.inc_by(cnt as u64))
353 .map_err(BoxedError::new)
354 .context(error::ExecuteGrpcQuerySnafu)?;
355 outputs.push(o);
356 }
357
358 Ok(outputs)
359 }
360}
361
362#[cfg(test)]
363mod tests {
364 use std::sync::Arc;
365
366 use auth::{PermissionTableTarget, PermissionTableTargets};
367 use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest;
368 use opentelemetry_proto::tonic::common::v1::{AnyValue, KeyValue, any_value};
369 use opentelemetry_proto::tonic::resource::v1::Resource;
370 use opentelemetry_proto::tonic::trace::v1::{ResourceSpans, ScopeSpans, Span};
371 use session::context::QueryContext;
372
373 use super::trace_permission_targets;
374
375 #[test]
376 fn test_trace_permission_targets() {
377 let request = ExportTraceServiceRequest {
378 resource_spans: vec![ResourceSpans {
379 resource: Some(Resource {
380 attributes: vec![KeyValue {
381 key: "service.name".to_string(),
382 value: Some(AnyValue {
383 value: Some(any_value::Value::StringValue("frontend".to_string())),
384 }),
385 ..Default::default()
386 }],
387 ..Default::default()
388 }),
389 scope_spans: vec![ScopeSpans {
390 spans: vec![Span::default()],
391 ..Default::default()
392 }],
393 ..Default::default()
394 }],
395 };
396 let groups = servers::otlp::trace::span::parse(request);
397 let ctx = Arc::new(QueryContext::with("greptime", "public"));
398
399 assert_eq!(
400 PermissionTableTargets::Resolved(vec![
401 PermissionTableTarget::new("greptime", "public", "traces"),
402 PermissionTableTarget::new("greptime", "public", "traces_services"),
403 PermissionTableTarget::new("greptime", "public", "traces_operations"),
404 ]),
405 trace_permission_targets("traces", &groups, &ctx)
406 );
407 assert_eq!(
408 PermissionTableTargets::Unresolved,
409 trace_permission_targets("", &groups, &ctx)
410 );
411 }
412}