Skip to content

Collector: OpenTelemetry Compatibility

The Aires collector can ingest OpenTelemetry data alongside native Aires events. This enables incremental migration — keep your existing OTel instrumentation while adding Aires-native events for richer observability.

The collector accepts OTLP (OpenTelemetry Protocol) data over gRPC on the same port as native Aires ingestion. OTel-instrumented applications can point their OTLP exporter directly at the Aires collector.

Configure your OpenTelemetry SDK to export to the Aires collector:

Node.js (OTel JS):

import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc"
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-grpc"

const traceExporter = new OTLPTraceExporter({
  url: "http://localhost:4317",
})

const logExporter = new OTLPLogExporter({
  url: "http://localhost:4317",
})

Python (OTel Python):

from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.exporter.otlp.proto.grpc.log_exporter import OTLPLogExporter

trace_exporter = OTLPSpanExporter(endpoint="localhost:4317", insecure=True)
log_exporter = OTLPLogExporter(endpoint="localhost:4317", insecure=True)

Go (OTel Go):

import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"

exporter, err := otlptracegrpc.New(ctx,
    otlptracegrpc.WithEndpoint("localhost:4317"),
    otlptracegrpc.WithInsecure(),
)

Environment variable (any OTel SDK):

export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317"
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"

When the collector receives an OTLP ExportTraceServiceRequest, each span is converted to an Aires Event:

OTel Span FieldAires Event FieldNotes
traceIdtrace_idHex-encoded to string
spanIdspan_idHex-encoded to string
parentSpanIdparent_span_idHex-encoded to string
namemessageSpan name becomes the message
kindkindMapped: SPAN_KIND_SERVER"server", etc.
startTimeUnixNanotimestamp_nsDirect mapping
endTimeUnixNanoUsed to compute http_duration_ms(end - start) / 1_000_000
status.codeseverityOKinfo, ERRORerror
status.messageerror_messageIf status is ERROR
attributesattributesString values mapped directly
Resource service.nameserviceFrom OTel resource attributes
Resource deployment.environmentenvironmentFrom OTel resource attributes
Resource host.namehostFrom OTel resource attributes

OTel HTTP spans using semantic conventions are automatically mapped:

OTel AttributeAires Field
http.request.method or http.methodhttp_method
url.path or http.targethttp_path
http.response.status_code or http.status_codehttp_status_code

The collector sets category = "http" for spans with HTTP attributes.

OTLP log records are converted similarly:

OTel Log FieldAires Event Field
timeUnixNanotimestamp_ns
severityNumberseverity (mapped: 1-4 → trace, 5-8 → debug, 9-12 → info, 13-16 → warn, 17-20 → error, 21-24 → fatal)
severityTextUsed as fallback for severity
body (string)message
traceIdtrace_id
spanIdspan_id
attributesattributes
Resource service.nameservice

Firehose mode passes OTel data through with minimal transformation — it preserves the original attribute names and values without mapping to Aires-specific fields. This is useful when you want raw OTel data in ClickHouse for custom analysis.

Enable firehose mode via environment variable:

export AIRES_OTEL_FIREHOSE=true

In firehose mode:

  • All OTel attributes are stored as-is in the attributes map
  • No semantic convention mapping is applied
  • The kind field is set to "otel-span" or "otel-log" for easy filtering
  • Original span/log bytes can be stored in the body field
-- Find all OTel spans
SELECT *
FROM events
WHERE kind = 'otel-span'
  AND timestamp > now() - INTERVAL 1 HOUR;

-- Query using original OTel attribute names
SELECT
    message,
    attributes['http.request.method'] AS method,
    attributes['url.path'] AS path,
    attributes['http.response.status_code'] AS status
FROM events
WHERE kind = 'otel-span'
  AND attributes['http.request.method'] != '';

You can send both native Aires events and OTel data to the same collector. They’ll coexist in the same ClickHouse table:

-- See the mix of native and OTel events
SELECT
    kind,
    sdk_name,
    count() AS event_count
FROM events
WHERE timestamp > now() - INTERVAL 1 HOUR
GROUP BY kind, sdk_name;

Native Aires events will have sdk_name = "aires-sdk-rust" (or "aires-sdk-ts", "aires-sdk-python"), while OTel events will have sdk_name = "opentelemetry".

  1. Phase 1: Point your OTel exporters at the Aires collector. All existing instrumentation continues working. Query OTel data in ClickHouse.

  2. Phase 2: Add Aires-native instrumentation alongside OTel for richer data (agent tracking, structured data fields, display text). Both coexist.

  3. Phase 3: Gradually replace OTel instrumentation with native Aires SDKs where the richer event model adds value. Keep OTel for libraries and frameworks that only support OTel.

  • OTel Metrics: OTLP metrics ingestion is not yet supported. Use Aires-native metric() calls for metrics, or use a separate OTel metrics backend (Prometheus).
  • Baggage propagation: The collector doesn’t propagate W3C Baggage. Use Aires SDK trace context propagation for cross-service correlation.
  • Span links: OTel span links are stored in the data field as JSON, not as first-class fields.