Add semantic conventions, structured inputs/outputs, custom attributes, events, exceptions, and prompt templates to your spans
Make traces useful for your app. Auto-instrumentation captures the basics — inputs, outputs, tokens, latency. But your app has context that matters: customer tier, A/B test variant, prompt template version, error details. This page covers all the ways to add it.Start with the standard attribute names Arize AX expects:
OpenInference Semantic Conventions are the standardized attribute names that Arize AX uses to render your trace data correctly — model name, messages, token counts, span kinds, and more. When you use these attributes, your data shows up in the right places in the UI.
Python
JS/TS
Java
Go
Install the semantic conventions package:
pip install openinference-semantic-conventions
Use SpanAttributes to set standardized attribute names on your spans:
from openinference.semconv.trace import SpanAttributes, MessageAttributesspan.set_attribute(SpanAttributes.OUTPUT_VALUE, response)span.set_attribute( f"{SpanAttributes.LLM_OUTPUT_MESSAGES}.0.{MessageAttributes.MESSAGE_ROLE}", "assistant",)span.set_attribute( f"{SpanAttributes.LLM_OUTPUT_MESSAGES}.0.{MessageAttributes.MESSAGE_CONTENT}", response,)
Signal whether a span succeeded or failed. Every span carries a status — OK, ERROR, or UNSET.
Python
JS/TS
Go
from opentelemetry import tracefrom opentelemetry.trace import Status, StatusCodecurrent_span = trace.get_current_span()current_span.set_status(Status(StatusCode.OK))# or Status(StatusCode.ERROR) on failure
import ( "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace")currentSpan := trace.SpanFromContext(ctx)currentSpan.SetStatus(codes.Ok, "")// or codes.Error with a message on failure
Customer tier, environment, feature flags, A/B test variants — custom attributes let you attach this app-specific data to spans so you can filter, group, and analyze by it in Arize AX.Best practice: vendor your attributes (e.g., mycompany.) so they don’t clash with semantic conventions.
Python
JS/TS
Java
Go
Get the current span and set your custom attributes:
from opentelemetry import tracecurrent_span = trace.get_current_span()current_span.set_attribute("mycompany.customer_tier", "enterprise")current_span.set_attribute("mycompany.ab_variant", "v2")current_span.set_attribute("mycompany.feature_flag", "new-retrieval")
Set attributes when creating a span or on an active span:
Custom attribute — attached to a single span, each one a distinct filterable field in the UI. Use for values you filter or group by: customer tier, A/B variant, feature flag.
Metadata (via using_metadata in Python, setMetadata in JS/TS, or WithMetadata in Go) — propagates to every child span in a context and is stored as a single JSON field. Use for request-wide context: request ID, experiment name, pipeline version.
import { getAttributesFromContext } from "@arizeai/openinference-core";import { context, trace } from "@opentelemetry/api"const contextAttributes = getAttributesFromContext(context.active())const span = trace.getTracer("example").startSpan("example span")span.setAttributes(contextAttributes)span.end();
go get github.com/Arize-ai/openinference/go/openinference-instrumentation
The helpers stash values on context.Context (via unexported keys, not OTel baggage), so they flow through your in-process call graph but never leak out as baggage HTTP headers on downstream requests. The per-provider instrumentors (openinference-instrumentation-openai-go, openinference-instrumentation-anthropic-sdk-go) read them back and apply them to every LLM span descended from the context, even when the call is several layers deep.
WithMetadata takes a pre-serialized JSON string — Go has no native dict literal, so callers json.Marshal their own map. (Python using_metadata and JS setMetadata take dicts/objects because both languages have built-in JSON encoding for them.)
Go has no combined setter analogous to Python’s using_attributes — chain the individual With* helpers when you need to set multiple values. The order doesn’t matter; each returns a derived context.
using_tags / setTags set tag.tags on spans. For project- or dataset-level tags (a separate platform feature), see Tags.
Prompt templates have their own dedicated propagation helper:
Instrument prompt templates so you can experiment with changes in the Prompt Playground.
Recommended for LLM spans only.
Python
JS/TS
Go
from openinference.instrumentation import using_prompt_templatefrom openai import OpenAIclient = OpenAI()prompt_template = "Please describe the best activity for me to do in {city} on {date}"prompt_template_variables = {"city": "Johannesburg", "date": "July 11"}with using_prompt_template( template=prompt_template, variables=prompt_template_variables, version="v1.0",): response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": prompt_template.format(**prompt_template_variables)}], )
import { context } from "@opentelemetry/api"import { setPromptTemplate } from "@arizeai/openinference-core"context.with( setPromptTemplate(context.active(), { template: "Please describe the best activity for me to do in {{city}}", variables: { city: "Johannesburg" }, version: "v1.0" }), () => { /* spans get prompt template attributes */ })
There’s no context helper for prompt templates in Go — set the attributes directly on the LLM span using semantic-conventions constants:
import ( "encoding/json" "go.opentelemetry.io/otel/attribute" semconv "github.com/Arize-ai/openinference/go/openinference-semantic-conventions")variables := map[string]string{"city": "Johannesburg", "date": "July 11"}varsJSON, err := json.Marshal(variables)if err != nil { span.RecordError(err)}span.SetAttributes( attribute.String(semconv.LLMPromptTemplate, "Please describe the best activity for me to do in {city} on {date}"), attribute.String(semconv.LLMPromptTemplateVersion, "v1.0"), attribute.String(semconv.LLMPromptTemplateVariables, string(varsJSON)),)
Prompt templates are set at span-creation time. For data that arrives later — review status, corrections, labels added after generation — patch the span after it’s been ingested:
Useful when your system enriches data after generation time — for example, adding review status, corrections, or labels that weren’t available when the trace was created.