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