OpenInference is an open standard for AI observability developed by Arize AI.
It defines its own semantic conventions (llm.model_name, llm.token_count.*, and others) which differ from the gen_ai.* attributes that Dynatrace AI Observability natively understands.
This guide shows you how to instrument your AI application with OpenInference, and then normalize the OpenInference attributes to the gen_ai.* format using one of two approaches: An OTel Collector with a transform processor, or Dynatrace OpenPipeline.
This getting started guide is for:
By following this guide, you'll learn:
gen_ai.* using an OTel Collector or Dynatrace OpenPipeline.To follow this guide, you need:
openTelemetryTrace.ingest scope, see Dynatrace API - Tokens and authentication.It's helpful to have some basic knowledge of:
OpenInference captures and transmits AI model or agent KPIs as OpenTelemetry spans but uses its own semantic conventions rather than the gen_ai.* standard.
Because Dynatrace AI Observability requires gen_ai.* attributes, you need an intermediate normalization step.
Two equivalent approaches are available: the OTel Collector, or OpenPipeline, as described in the table below.
Both approaches produce identical results in
AI Observability.
| OTel Collector | OpenPipeline | |
|---|---|---|
Where transforms run | In the Collector process, locally | Server-side, in your Dynatrace tenant |
Requires Docker? | Yes | No |
Requires Dynatrace configuration? | No | Yes; one-time deployment |
Good for | Full pipeline control; no Dynatrace configuration needed | Simpler operations; no Collector to manage |
The table below shows the gen_ai.* attributes produced after normalization:
| Source attribute | gen_ai.* attribute | Notes |
|---|---|---|
|
|
|
|
| LLM spans |
|
| Embedding spans |
|
| Reranker spans |
|
| Mirrored; OpenInference has no separate response model field |
|
|
|
— |
| Set to |
— |
| Hardcoded |
|
| |
|
| |
|
|
Guard prevents false positives (Azure emits |
|
| |
|
| |
|
| |
|
| Renamed directly; source attribute removed |
|
| Only when |
|
| |
|
| |
|
| |
|
|
|
|
| |
|
| Interim fallback; source removed |
|
| Interim fallback; source removed |
— |
| Hardcoded |
This section describes how to start ingesting OpenInference attributes into Dynatrace, which can then be normalized into gen_ai.* attributes.
Ctrl+K and search for Access tokens.openTelemetryTrace.ingest scope.For more information, see Access tokens and permissions.
The sample app and scripts read credentials from environment variables.
Create a .env file in your project directory:
DT_ENDPOINT=https://<your-environment-id>.live.dynatrace.comDT_API_TOKEN=dt0c01.<token-value>OPENAI_API_KEY=<your-openai-api-key>OPENAI_API_BASE=https://your-endpoint.openai.azure.com/MODEL=gpt-5.5OPENAI_API_VERSION=2024-07-01-preview
DT_ENDPOINT is your base environment URL, not the /api/v2/otlp path.
For more information, see Dynatrace OTLP endpoints.
pip install -r requirements.txt
The key packages are openinference-instrumentation-openai and opentelemetry-sdk.
Use the OpenInference auto-instrumentation for OpenAI. The instrumentation patches the OpenAI client and records spans with OpenInference semantic conventions automatically.
from openinference.instrumentation.openai import OpenAIInstrumentorfrom opentelemetry.sdk.resources import Resource, SERVICE_NAMEfrom opentelemetry.sdk.trace import TracerProviderfrom opentelemetry.sdk.trace.export import BatchSpanProcessorfrom opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporterimport openaiimport os# Configure the tracer providerservice_name = os.getenv("OTEL_SERVICE_NAME", "openinferenceapp")tracer_provider = TracerProvider(resource=Resource.create({SERVICE_NAME: service_name,}))# Configure the OTLP exporter—endpoint and auth depend on your chosen option (see below)exporter = OTLPSpanExporter(endpoint=os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"],headers={"Authorization": f"Api-Token {os.environ['DT_API_TOKEN']}"},)tracer_provider.add_span_processor(BatchSpanProcessor(exporter))# Instrument the OpenAI clientOpenAIInstrumentor().instrument(tracer_provider=tracer_provider)client = openai.OpenAI(api_key=os.environ["OPENAI_API_KEY"])response = client.chat.completions.create(model=os.environ.get("MODEL", "gpt-5.5"),messages=[{"role": "user", "content": "Write a haiku about observability."}],)print(response.choices[0].message.content)
Once you've set up your app to export data to Dynatrace, you can normalize your attributes to the gen_ai.* format in one of two ways, see the sections below.
The OTel Collector intercepts spans and applies all attribute mappings before forwarding to Dynatrace. You don't need to configure Dynatrace:
DT_ENDPOINT and DT_API_TOKEN.http://localhost:4318 and doesn't need Dynatrace credentials.To use the OTel Collector:
Start the OTel Collector.
Use the Dynatrace OTel Collector image with the otel-collector-config.yaml from the sample repository:
macOS
source .envdocker run -d \--name otel-collector \-p 4318:4318 \-v $(pwd)/otel-collector-config.yaml:/etc/otelcol/otel-collector-config.yaml:ro \-e DT_ENDPOINT=$DT_ENDPOINT \-e DT_API_TOKEN=$DT_API_TOKEN \ghcr.io/dynatrace/dynatrace-otel-collector/dynatrace-otel-collector:0.48.0 \--config=/etc/otelcol/otel-collector-config.yaml
Windows CMD
set DT_ENDPOINT=https://abc12345.live.dynatrace.comset DT_API_TOKEN=dt0c01.*****docker run -d ^--name otel-collector ^-p 4318:4318 ^-v %cd%/otel-collector-config.yaml:/etc/otelcol/otel-collector-config.yaml:ro ^-e DT_ENDPOINT=%DT_ENDPOINT% ^-e DT_API_TOKEN=%DT_API_TOKEN% ^ghcr.io/dynatrace/dynatrace-otel-collector/dynatrace-otel-collector:0.48.0 ^--config=/etc/otelcol/otel-collector-config.yaml
The Collector:
Listens on port 4318 for incoming OTLP/HTTP spans from the app.
Applies the transform/openinference processor to rename and map attributes.
Forwards processed spans to $DT_ENDPOINT/api/v2/otlp authenticated with the API token.
Run the app
Point the app to the local Collector. The Collector handles the Dynatrace authentication.
source .envOTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318OTEL_EXPORTER_OTLP_HEADERS=""python3 app.py
To tail the Collector logs:
docker logs -f otel-collector
To stop the Collector:
docker stop otel-collector && docker rm otel-collector
OpenPipeline is a server-side processing pipeline in Dynatrace that applies the same attribute mappings before Dynatrace stores them. The app sends spans directly to Dynatrace, no Collector needed.
This is a one-time setup per environment.
In Dynatrace, select Ctrl+K and search for OpenPipeline.
Select Spans.
Select Add pipeline and name it openinference-ai-spans.
Add processors that match the definitions in openpipeline-openinference.yaml from the sample repository.
Go to the Routing tab and add an entry.
isNotNull(openinference.span.kind)openinference-ai-spansUse isNotNull(openinference.span.kind) as the routing matcher. OpenPipeline routing evaluates span attributes only, not OTLP scope-level fields, so otel.scope.name is not accessible at routing time. The openinference.span.kind attribute is set on every span by all OpenInference instrumentors.
Point the app directly to the Dynatrace OTLP endpoint. OpenPipeline intercepts and transforms spans before Dynatrace stores them.
source .envOTEL_EXPORTER_OTLP_ENDPOINT=$DT_ENDPOINT/api/v2/otlp \OTEL_EXPORTER_OTLP_HEADERS="Authorization=Api-Token $DT_API_TOKEN" \python3 app.py
Once you've normalized the OpenInference attributes to the gen_ai.* format, you can observe your spans in Dynatrace.
Ctrl+K and search for AI Observability.gen_ai.* attributes.You can also view spans in Distributed Tracing.
Now that you've set up your AI app to send observability data to Dynatrace, you can: