OpenInference is an open standard for AI observability developed by Arize AI.
OpenInference defines semantic conventions such as llm.model_name and llm.token_count.*, which differ from the gen_ai.* attributes that
AI Observability natively understands. Supported instrumentors can also emit OpenTelemetry GenAI semantic-convention attributes alongside OpenInference attributes.
Learn how to instrument your AI application with OpenInference, enable native gen_ai.* attributes where available, and normalize remaining OpenInference attributes with an OTel Collector or Dynatrace OpenPipeline.
This getting started guide is for:
By following this guide, you'll learn:
gen_ai.* by using native GenAI semantic-convention support, an OTel Collector, or Dynatrace OpenPipeline.
AI Observability.To follow this guide, you need:
A running AI application, or use one of the OpenInference sample apps:
Dynatrace SaaS with a Dynatrace Platform Subscription (DPS) license that has Traces powered by Grail enabled.
OTLP ingestion enabled, see OpenTelemetry and Dynatrace.
A platform token with the openpipeline:traces:ingest scope, or a Classic access token with the Ingest OpenTelemetry traces (openTelemetryTrace.ingest) scope.
Any credentials required by your selected sample.
For the Collector option: Docker, and an OTel Collector distribution that bundles the genainormalizer processor. Either of the following works:
Choose the sample that matches your provider or framework. Each README contains the provider-specific prerequisites, instrumentation, and processing configuration.
| Technology | OpenInference instrumentor | Data preparation in the sample |
|---|---|---|
| Native | |
| Collector normalization and provider-specific message transforms | |
| Collector normalization | |
| Collector or OpenPipeline normalization, with a provider-specific output-message transform | |
| Native | |
| Collector or OpenPipeline normalization | |
| Collector or OpenPipeline normalization | |
| Collector or OpenPipeline normalization | |
| Collector or OpenPipeline normalization |
It's helpful to have some basic knowledge of:
OpenInference captures and transmits AI model or agentic-workflow KPIs as OpenTelemetry spans. Its instrumentors emit OpenInference attributes and can, depending on the instrumentor, also emit OpenTelemetry gen_ai.* attributes.
Dynatrace AI Observability requires gen_ai.* attributes. Enable native GenAI semantic-convention emission when the instrumentor supports it. Otherwise, normalize the OpenInference attributes with an OTel Collector or OpenPipeline.
For more information, see Use OpenTelemetry GenAI semantic conventions when available and Normalize your OpenInference attributes.
This section describes how to start ingesting OpenInference attributes into Dynatrace, which can then be normalized into gen_ai.* attributes.
To authenticate, create one of the following:
Access Tokens.Required scopes:
openpipeline:traces:ingestopenTelemetryTrace.ingest)For more information, see 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=dt0XXX.<your-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-previewOPENINFERENCE_ENABLE_GENAI_SEMCONV=true
Set DT_API_TOKEN to your platform token or Classic access token.
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. Each example's README identifies its instrumentor package, dependencies, application entry point, and Collector configuration. Follow that README because providers and frameworks don't all emit the same attributes or use the same configuration filenames.
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 with the appropriate endpoint and credentials (see the option you've chosen below)exporter = OTLPSpanExporter(endpoint=os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"],headers={"Authorization": f"Bearer {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)
The required scope is openpipeline:traces:ingest.
OpenInference Python instrumentors that use TraceConfig capture content by default. Prompts and completions can contain sensitive data, so review the content that your application sends before exporting spans to Dynatrace.
Use TraceConfig to suppress content programmatically with hide_inputs, hide_outputs, hide_input_messages, and hide_output_messages. You can set the equivalent environment variables to true: OPENINFERENCE_HIDE_INPUTS, OPENINFERENCE_HIDE_OUTPUTS, OPENINFERENCE_HIDE_INPUT_MESSAGES, and OPENINFERENCE_HIDE_OUTPUT_MESSAGES.
If you intentionally want message content sent to Dynatrace, ensure that the applicable controls are false or unset. These controls can't restore data that the instrumentation didn't capture.
Set the following environment variable when using an instrumentor that supports OpenTelemetry GenAI semantic conventions:
OPENINFERENCE_ENABLE_GENAI_SEMCONV=true
OPENINFERENCE_ENABLE_GENAI_SEMCONV emits gen_ai.* attributes alongside OpenInference attributes. The OpenAI and Cohere examples use this setting. Other instrumentors might not emit complete gen_ai.* coverage, so coverage varies by provider and framework. For those instrumentors, normalize attributes to gen_ai.* with the Collector genainormalizer and transform processors or with Dynatrace OpenPipeline, as described below.
If your instrumentor doesn't emit every attribute that Dynatrace needs as gen_ai.*, normalize the remaining OpenInference attributes in one of two ways.
OTel Collector + genainormalizer | 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 |
Full message history | Reconstructs | Uses interim fallback from |
Good for | Full pipeline control and complete message history when source attributes are available | Simpler operations; no Collector to manage |
Collector image | Any distribution bundling | Not applicable |
genainormalizer processor The Collector intercepts spans and normalizes OpenInference attributes to gen_ai.* with the genainormalizer processor before forwarding them to Dynatrace.
No Dynatrace configuration is needed, and you can use any OTel Collector distribution that bundles the genainormalizer processor, for example:
ghcr.io/dynatrace/dynatrace-otel-collector/dynatrace-otel-collector:0.53.1ghcr.io/observiq/bindplane-agent:1.104.0The Collector:
4318 for incoming OTLP/HTTP spans from the app.
The app only sends spans to http://localhost:4318 and doesn't need Dynatrace credentials.genainormalizer processor (source: openinference, remove_originals: true) to map OpenInference attributes to gen_ai.* and reconstruct message history.
When the instrumentor emits sufficient indexed per-message attributes, the genainormalizer processor reconstructs the full gen_ai.input.messages / gen_ai.output.messages.transform/response_model processor to mirror gen_ai.request.model to gen_ai.response.model.DT_ENDPOINT and DT_API_TOKEN. It forwards processed spans to $DT_ENDPOINT/api/v2/otlp authenticated with your platform token or Classic access token.The following steps describe how to normalize attributes with the Collector.
Run your chosen Collector image with the configuration file supplied by your sample. The OpenAI sample uses otel-collector-config.yaml. The Amazon Bedrock and Cohere samples use otelcol-config.yaml. The remaining listed samples use otel-collector-config.yaml.
These examples use the Dynatrace Collector distro.
To use the Bindplane distro instead, set COLLECTOR_IMAGE to ghcr.io/observiq/bindplane-agent:1.104.0.
macOS/Linux:
source .envCOLLECTOR_IMAGE=ghcr.io/dynatrace/dynatrace-otel-collector/dynatrace-otel-collector:0.53.1docker run -d \--name otel-collector \-p 4318:4318 \-v $(pwd)/otel-collector-config.yaml:/etc/otel/config.yaml:ro \-e DT_ENDPOINT=$DT_ENDPOINT \-e DT_API_TOKEN=$DT_API_TOKEN \$COLLECTOR_IMAGE \--config=/etc/otel/config.yaml
Windows CMD:
set DT_ENDPOINT=https://abc12345.live.dynatrace.comset DT_API_TOKEN=dt0XXX.<your-token-value>set COLLECTOR_IMAGE=ghcr.io/dynatrace/dynatrace-otel-collector/dynatrace-otel-collector:0.53.1docker run -d ^--name otel-collector ^-p 4318:4318 ^-v %cd%/otel-collector-config.yaml:/etc/otel/config.yaml:ro ^-e DT_ENDPOINT=%DT_ENDPOINT% ^-e DT_API_TOKEN=%DT_API_TOKEN% ^%COLLECTOR_IMAGE% ^--config=/etc/otel/config.yaml
Point the app to the local Collector.
source .envOTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318OTEL_EXPORTER_OTLP_HEADERS=""python3 app.py
Useful commands:
# Tail Collector logsdocker logs -f otel-collector# Stop the Collectordocker 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.
The following steps describe how to normalize attributes with OpenPipeline.
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 the sample repository's openpipeline-openinference.yaml file.
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=Bearer $DT_API_TOKEN" \python3 app.py
The required scope is openpipeline:traces:ingest.
The table below summarizes the mappings in the sample Collector and OpenPipeline configurations. The genainormalizer processor performs the core mappings. The sample transform processors add optional fields, as described in Known limitations. For differences with OpenPipeline, see the Notes column.
| Source attribute | gen_ai.* attribute | Notes |
|---|---|---|
|
|
|
|
| LLM spans |
|
| Embedding spans |
|
| Reranker spans |
|
| Mirrored; OpenInference has no separate response model field |
|
| If |
|
|
|
— |
| Set to |
— |
| Hardcoded |
|
| |
|
| |
|
|
Guard prevents false positives (Azure emits |
|
| |
|
| |
|
| |
|
| Renamed directly; source attribute removed |
|
| Only when |
|
| |
|
| |
|
| |
|
| The Anthropic OpenPipeline sample renames this unnamespaced attribute for queryability |
|
| |
|
| Reconstructed into full message history JSON including roles, content, and tool calls when sufficient indexed attributes are available. Source attributes removed. (OpenPipeline uses interim fallback from |
|
| Reconstructed into full message history JSON including roles, content, and tool calls when sufficient indexed attributes are available. Source attributes removed. (OpenPipeline uses interim fallback from |
— |
| Hardcoded |
OpenInference supports GUARDRAIL as a span kind but doesn't define a standardized rich guardrail attribute set. The genainormalizer processor copies this value to gen_ai.operation.name unchanged. The Anthropic OpenPipeline sample maps it to guardrail in gen_ai.operation.kind and renames an emitted validator_name source attribute to gen_ai.guardrail.name. Provider- or framework-specific guardrail data can be unavailable, and downstream transforms can't reconstruct data that instrumentation discarded.
Attribute coverage and message reconstruction depend on the instrumentor and the attributes it emits. The genainormalizer processor maps the supported OpenInference attributes it receives, but some optional fields can require custom transforms:
gen_ai.request.temperature, gen_ai.request.top_p, gen_ai.request.max_tokensgen_ai.response.finish_reasonsgen_ai.prompt_caching and gen_ai.cache.typegen_ai.system (however, it does set gen_ai.provider.name)To add these fields:
With the Collector and genainormalizer processor, extend the sample's Collector configuration by adding statements to its transform processor. Use the filename supplied by that sample, such as otel-collector-config.yaml for OpenAI or otelcol-config.yaml for Amazon Bedrock. These mappings are candidates for upstream contribution to the genainormalizer processor.
With OpenPipeline, include optional field attributes server-side using Dynatrace Query Language (DQL) transforms. OpenPipeline can't reconstruct the full message history because DQL can't iterate over indexed per-message attributes at transform time. It uses input.value / output.value as an interim fallback when those provider-specific values are available.
Transforms can map data that instrumentation emits, but can't reconstruct source-specific data that an instrumentor doesn't emit or has discarded.
Once your spans contain the required gen_ai.* attributes, you can observe them 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:
AI Observability to visualize your AI workloads.