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