How GreptimeDB OpenTelemetry Integration Supports Metrics, Logs, and Traces Ingestion
GreptimeDB exposes a unified OpenTelemetry (OTEL) ingestion API that accepts metrics, logs, and traces over HTTP using protobuf, converting them into optimized row insertions via the OpenTelemetryProtocolHandler trait.
The OpenTelemetry integration in greptimeteam/greptimedb enables seamless observability data ingestion through three dedicated HTTP endpoints. This implementation decodes OTEL protobuf messages and transforms them into GreptimeDB's internal storage format, supporting both direct insertion and configurable pipeline processing for telemetry normalization.
HTTP Entry Points and Protocol Routing
The ingestion flow begins at the axum HTTP layer in src/servers/src/http/otlp.rs. Three async handlers register distinct routes for each signal type:
/v1/otlp/v1/metrics→metrics()handler/v1/otlp/v1/traces→traces()handler/v1/otlp/v1/logs→logs()handler
Each handler validates the Content-Type header, rejects JSON payloads (enforcing binary protobuf), and initializes a protocol context via ProtocolCtx::OtlpMetric. The request body undergoes prost::Message::decode conversion (lines 90-95) to transform raw bytes into OTEL gRPC structs: ExportMetricsServiceRequest, ExportTraceServiceRequest, or ExportLogsServiceRequest.
The decoded requests pass to the OpenTelemetryProtocolHandler trait implementation in src/frontend/src/instance/otlp.rs, where the Instance struct handles authorization, request-level interceptor plugins, and signal-specific routing.
Metrics Ingestion Pipeline
Metrics conversion occurs in src/servers/src/otlp/metrics.rs through the to_grpc_insert_requests function. The processor iterates the nested structure ResourceMetrics → ScopeMetrics → Metric, extracting time-series data into RowInsertRequests.
Key normalization steps include:
- Resource-attribute promotion: Elevates curated resource attributes (service → job/instance) or all attributes when
promote_all_resource_attrsis enabled - Name normalization: Applies
legacy_normalize_otlp_nameto sanitize metric names - Type mapping: Converts OTEL gauges to value columns, sums to
_count/_sumtable variants, and handles unit suffixes viaUNIT_MAPandPER_UNIT_MAP
The Instance::metrics method (lines 40-88) determines execution path based on OtlpMetricCtx flags. When is_legacy is false and with_metric_engine is true, requests route to the optimized metric engine via handle_metric_row_inserts; otherwise, standard row insertion applies.
Logs Ingestion and Pipeline Processing
Logs ingestion in src/servers/src/otlp/logs.rs supports dual processing modes via the PipelineWay enum:
PipelineWay::OtlpLogDirect: Builds a singleRowInsertRequesttargeting theopentelemetry_logstable (constantLOG_TABLE_NAME) using an identity schemaPipelineWay::Pipeline: Parses logs into a VRL array and executes user-defined pipelines viarun_pipelinefor custom transformation
The to_grpc_insert_requests function (lines 60-73) dispatches based on pipeline configuration, enabling either immediate storage or structured preprocessing before insertion.
Traces Ingestion: v0 and v1 Models
Traces ingestion supports schema evolution through versioned converters in src/servers/src/otlp/trace.rs. The dispatcher to_grpc_insert_requests (lines 67-94) selects between:
- v0 model (
v0_to_grpc_insert_requests): Legacy flat table structure for backward compatibility - v1 model (
v1_to_grpc_insert_requests): Modern column-rich schema implemented insrc/servers/src/otlp/trace/v1.rs
The v1 converter writes three distinct tables per trace dataset:
- Primary span table (one row per span)
trace_servicestable (deduplicated service names)trace_operationstable (service and operation combinations)
Configuration via HTTP Headers
GreptimeDB exposes fine-grained control through custom HTTP headers parsed in src/servers/src/http/otlp.rs and stored in OtlpMetricCtx:
x-greptime-otlp-metric-promote-scope-attrs– Promote all scope attributes as columnsx-greptime-otlp-metric-promote-all-resource-attrs– Promote every resource attribute (not just curated defaults)x-greptime-otlp-metric-ignore-resource-attrs– Exclude specific attributes from promotionx-greptime-otlp-legacy-mode– Force legacy metric handling with disabled metric-engine and attribute promotion
Query-string parameters like pipeline and db further customize request routing and target database selection.
Practical Code Examples
Sending Metrics with otel-cli
otel-cli metric add \
--name "http_requests_total" \
--type counter \
--value 1 \
--attrs "service.name=web,job=frontend" \
--endpoint http://localhost:4000/v1/otlp/v1/metrics \
--proto
The metrics() handler decodes this into an ExportMetricsServiceRequest, then otlp::metrics::to_grpc_insert_requests generates rows for tables named http_requests_total (or _count/_sum variants for cumulative metrics).
Ingesting Logs via cURL
cat logs.pb | curl -X POST \
-H "Content-Type: application/x-protobuf" \
--data-binary @- \
http://localhost:4000/v1/otlp/v1/logs?db=public
When no pipeline parameter is specified, logs() creates direct insertions to the opentelemetry_logs table. Adding ?pipeline=my_pipeline triggers VRL processing before storage.
Configuring OpenTelemetry Collector for Traces
exporters:
otlp/greptime:
endpoint: "http://localhost:4000/v1/otlp/v1/traces"
compression: none
The collector exports ExportTraceServiceRequest protobuf to the traces endpoint. By default, the v1 converter creates the three-table schema (spans, services, operations) in src/servers/src/otlp/trace/v1.rs.
Summary
- Unified HTTP API: Three endpoints (
/v1/otlp/v1/metrics,/v1/otlp/v1/logs,/v1/otlp/v1/traces) insrc/servers/src/http/otlp.rshandle all OTEL signals via protobuf decoding - Pluggable Processing: The
OpenTelemetryProtocolHandlertrait insrc/frontend/src/instance/otlp.rsdelegates to signal-specific converters for metrics, logs, and traces - Flexible Logs: Supports both direct insertion to
opentelemetry_logsand VRL pipeline processing viaPipelineWayconfiguration - Versioned Traces: Automatic dispatch between v0 (legacy flat) and v1 (normalized multi-table) trace models based on request context
- Configurable Ingestion: HTTP headers control resource attribute promotion, legacy mode toggling, and metric-engine selection
Frequently Asked Questions
Does GreptimeDB support JSON payloads for OpenTelemetry ingestion?
No. According to the source code in src/servers/src/http/otlp.rs, the HTTP handlers explicitly reject JSON payloads and require Content-Type: application/x-protobuf. All OTEL requests must use binary protobuf encoding via prost::Message::decode.
How does the metric engine differ from standard row insertion for OTEL metrics?
When x-greptime-otlp-metric-promote-all-resource-attrs headers indicate modern mode (non-legacy) and with_metric_engine is true, the Instance::metrics method routes requests through handle_metric_row_inserts. This path optimizes storage for high-cardinality time-series data compared to the standard row insertion fallback used when legacy mode is enabled.
Can I customize which log fields become columns versus attributes?
Yes. For logs sent through the pipeline path (PipelineWay::Pipeline), you can define VRL (Vector Remap Language) transformations that parse the OTEL log record and map specific fields to columns. The direct path (PipelineWay::OtlpLogDirect) uses a fixed identity schema for the opentelemetry_logs table.
What tables are created when ingesting OpenTelemetry traces?
For the default v1 model, the trace converter in src/servers/src/otlp/trace/v1.rs creates three tables: the primary span table (containing full trace data), a trace_services table with deduplicated service names, and a trace_operations table tracking service and operation combinations. The legacy v0 model uses a single flat table structure instead.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →