Try it free

Get started with OpenInference and AI Observability

  • Latest Dynatrace
  • Getting started guide
  • 5-min read

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 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.

Who is this for?

This getting started guide is for:

  • AI engineering teams building agent- and LLM-powered applications and services.
  • Site Reliability Engineers responsible for monitoring AI workloads in production.
  • Platform engineers integrating OpenTelemetry (OTel) data from AI apps into Dynatrace.

What will you learn?

By following this guide, you'll learn:

  • How to instrument an AI application with OpenInference.
  • How to emit or normalize OpenInference attributes as gen_ai.* by using native GenAI semantic-convention support or an OTel Collector.
  • How to view model name, token usage, and message content in AI Observability AI Observability.

Before you begin

Prerequisites

To follow this guide, you need:

  • A running AI application, or use one of the OpenInference sample apps:

    • OpenAI
    • Amazon Bedrock
    • LangGraph
    • Anthropic
    • Cohere
    • Groq
    • Mistral
    • Ollama
    • Haystack
  • 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 OTel Collector option: Docker, and an OTel Collector distribution that bundles the genainormalizer processor. Either of the following works:

    • Dynatrace Collector version 0.53.1 or later.
    • Dynatrace Bindplane OTel Collector version 1.1.2 or later.

OpenInference samples

Choose the sample that matches your provider or framework. Each README contains the provider-specific prerequisites, instrumentation, and processing configuration.

TechnologyOpenInference instrumentorData preparation in the sample

OpenAI

openinference-instrumentation-openai

Native gen_ai.* emission, with sample processing for metric derivation

Amazon Bedrock

openinference-instrumentation-bedrock

OTel Collector normalization and provider-specific message transforms

LangGraph

openinference-instrumentation-langchain

OTel Collector normalization

Anthropic

openinference-instrumentation-anthropic

OTel Collector normalization, with a provider-specific output-message transform

Cohere

openinference-instrumentation-cohere

Native gen_ai.* emission, with sample processing for metric derivation

Groq

openinference-instrumentation-groq

OTel Collector normalization

Mistral

openinference-instrumentation-mistralai

OTel Collector normalization

Ollama

openinference-instrumentation-ollama

OTel Collector normalization

Haystack

openinference-instrumentation-haystack

OTel Collector normalization

Prior knowledge

It's helpful to have some basic knowledge of:

  • Python.
  • OTel concepts like SDKs, spans, exporters, and Collectors.
  • Dynatrace permissions and data ingestion.

About OpenInference and AI observability

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. For more information, see Use OpenTelemetry GenAI semantic conventions when available and Normalize your OpenInference attributes.

Get started with OpenInference and Dynatrace

This section describes how to start ingesting OpenInference attributes into Dynatrace, which can then be normalized into gen_ai.* attributes.

1. Create a Dynatrace token

To authenticate, create one of the following:

  • Platform token:
    1. Go to My platform tokens.
    2. Create a platform token with the required scope (listed below). See Platform tokens for details.
  • Classic access token:
    1. Go to Access tokens Access Tokens.
    2. Select Generate new token.
    3. Enter a name for your token.
    4. Select the required scopes listed below.
    5. Select Generate token.
    6. Copy the generated token. Store it securely — you can only access it once.

Required scopes:

  • Platform token: openpipeline:traces:ingest
  • Classic access token: Ingest OpenTelemetry traces (openTelemetryTrace.ingest)

For more information, see Tokens and permissions.

2. Set environment variables

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.com
DT_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.5
OPENAI_API_VERSION=2024-07-01-preview
OPENINFERENCE_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.

3. Install dependencies

pip install -r requirements.txt

The key packages are openinference-instrumentation-openai and opentelemetry-sdk.

4. Instrument your application

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 OTel 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 OpenAIInstrumentor
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
import openai
import os
# Configure the tracer provider
service_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 client
OpenAIInstrumentor().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.

from openinference.instrumentation.openai import OpenAIInstrumentor
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
import openai
import os
# Configure the tracer provider
service_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"Api-Token {os.environ['DT_API_TOKEN']}"},
)
tracer_provider.add_span_processor(BatchSpanProcessor(exporter))
# Instrument the OpenAI client
OpenAIInstrumentor().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 Ingest OpenTelemetry traces (openTelemetryTrace.ingest).

Control message-content capture

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.

Use OpenTelemetry GenAI semantic conventions when available

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 OTel Collector genainormalizer and transform processors, as described below.

Normalize your OpenInference attributes

If your instrumentor doesn't emit every attribute that Dynatrace needs as gen_ai.*, normalize the remaining OpenInference attributes with an OTel Collector that includes the genainormalizer processor.

OTel Collector with the genainormalizer processor

The OTel 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:

  • Dynatrace Collector: ghcr.io/dynatrace/dynatrace-otel-collector/dynatrace-otel-collector:0.53.1
  • Dynatrace Bindplane OTel Collector: ghcr.io/dynatrace/dynatrace-bindplane-otel-collector:1.1.2

The OTel Collector:

  1. Listens on port 4318 for incoming OTLP/HTTP spans from the app. The app only sends spans to http://localhost:4318 and doesn't need Dynatrace credentials.
  2. Applies the 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.
  3. Applies the transform/response_model processor to mirror gen_ai.request.model to gen_ai.response.model.
  4. Authenticates with Dynatrace using 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 OTel Collector.

1. Start the OTel Collector

Run your chosen OTel 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 Dynatrace Bindplane OTel Collector instead, set COLLECTOR_IMAGE to ghcr.io/dynatrace/dynatrace-bindplane-otel-collector:1.1.2.

macOS/Linux:

source .env
COLLECTOR_IMAGE=ghcr.io/dynatrace/dynatrace-otel-collector/dynatrace-otel-collector:0.53.1
docker 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.com
set DT_API_TOKEN=dt0XXX.<your-token-value>
set COLLECTOR_IMAGE=ghcr.io/dynatrace/dynatrace-otel-collector/dynatrace-otel-collector:0.53.1
docker 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
2. Run the app

Point the app to the local OTel Collector.

source .env
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_EXPORTER_OTLP_HEADERS=""
python3 app.py

Useful commands:

# Tail OTel Collector logs
docker logs -f otel-collector
# Stop the OTel Collector
docker stop otel-collector && docker rm otel-collector

Attribute mapping reference

The table below summarizes the mappings in the sample OTel Collector configurations. The genainormalizer processor performs the core mappings. The sample transform processors add optional fields, as described in Known limitations.

Source attributegen_ai.* attributeNotes

openinference.span.kind

gen_ai.operation.name

  • LLM becomes chat
  • EMBEDDING becomes embeddings
  • AGENT becomes invoke_agent
  • RETRIEVER and RERANKER become retrieval
  • TOOL becomes execute_tool
  • PROMPT becomes text_completion
  • The OTel Collector copies other values, including GUARDRAIL, unchanged

llm.model_name

gen_ai.request.model

LLM spans

embedding.model_name

gen_ai.request.model

Embedding spans

reranker.model_name

gen_ai.request.model

Reranker spans

gen_ai.request.model

gen_ai.response.model

Mirrored; OpenInference has no separate response model field

llm.provider

gen_ai.provider.name

If llm.provider is absent, llm.system is used instead

llm.system

gen_ai.provider.name

llm.system is used only when llm.provider is absent

—

gen_ai.system

Set to azure.ai.openai when provider is azure

—

gen_ai.operation.name

Hardcoded chat for LLM spans, embeddings for embedding spans

llm.token_count.prompt

gen_ai.usage.input_tokens

llm.token_count.completion

gen_ai.usage.output_tokens

llm.token_count.prompt_details.cache_read

gen_ai.prompt_caching

  • Set to read when the value is greater than 0.
  • Set to write when only cache_write is greater than 0.

Guard prevents false positives (Azure emits 0 on every span).

llm.temperature

gen_ai.request.temperature

llm.max_tokens

gen_ai.request.max_tokens

llm.top_p

gen_ai.request.top_p

llm.finish_reason

gen_ai.response.finish_reasons

Renamed directly; source attribute removed

llm.input_messages.0.message.content

gen_ai.system_instructions

Only when llm.input_messages.0.message.role == "system"

agent.name

gen_ai.agent.name

tool.name

gen_ai.tool.name

tool.description

gen_ai.tool.description

validator_name

gen_ai.guardrail.name

The Anthropic sample OTel Collector renames this unnamespaced attribute for queryability

embedding.vector_length

gen_ai.embeddings.dimension.count

llm.input_messages.N.*

gen_ai.input.messages

Reconstructed into full message history JSON including roles, content, and tool calls when sufficient indexed attributes are available. Source attributes removed.

llm.output_messages.N.*

gen_ai.output.messages

Reconstructed into full message history JSON including roles, content, and tool calls when sufficient indexed attributes are available. Source attributes removed.

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 sample OTel Collector 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.

Known limitations

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:

  • Request parameters: gen_ai.request.temperature, gen_ai.request.top_p, gen_ai.request.max_tokens
  • LLM completion finish reason: gen_ai.response.finish_reasons
  • Prompt caching indicators: gen_ai.prompt_caching and gen_ai.cache.type
  • gen_ai.system (however, it does set gen_ai.provider.name)

To add these fields, extend the sample's OTel 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.

Transforms can map data that instrumentation emits, but can't reconstruct source-specific data that an instrumentor doesn't emit or has discarded.

Observe in Dynatrace

Once your spans contain the required gen_ai.* attributes, you can observe them in Dynatrace.

  1. In Dynatrace, select Ctrl+K and search for AI Observability.
  2. Your request appears in the Explorer tab as a span with the model name, token usage, and message content.
  3. Open a span to inspect the full conversation and gen_ai.* attributes.

You can also view spans in Distributed Tracing Distributed Tracing.

Congratulations

Now that you've set up your AI app to send observability data to Dynatrace, you can:

  • Explore Distributed Tracing Distributed Tracing and the AI Observability AI Observability to visualize your AI workloads.
  • Check out the sample applications for more examples.
  • Point the OTLP endpoint to your OTel Collector or ActiveGate endpoint for more advanced setups.
  • For troubleshooting, see the openinference examples README.
  • Follow the README for your selected OpenInference sample, including its instrumentor-specific normalization and message-reconstruction steps.
Related tags
AI Observability