Use this file to discover all available pages before exploring further.
Auto-instrumentation covers supported frameworks. For everything else — custom logic, tool execution, unsupported frameworks, or fine-grained control — you create spans explicitly using the OpenTelemetry API with OpenInference Semantic Conventions.
# Ask your AI coding agent:"Wrap my custom tool functions with manual spans using OpenInference conventions"
Works with Cursor, Claude Code, Codex, and more. The skill picks the right span kinds, sets the right OpenInference attributes, and handles context propagation for you:
Set up the OpenTelemetry SDK, register a tracer provider with your Arize credentials, and create spans with OpenInference attributes.
Requires Go 1.25+. Install arize-otel-go for tracer setup, plus the OpenInference semantic-conventions and instrumentation packages — the first for typed attribute-key constants, the second for context-scoped helpers (WithSession, WithUser, WithMetadata, WithTags, WithSuppression):
go get \ github.com/Arize-ai/arize-otel-go \ github.com/Arize-ai/openinference/go/openinference-instrumentation \ github.com/Arize-ai/openinference/go/openinference-semantic-conventions
2
Get API Key & Space ID
Go to Settings > API keys to create your API key and to get the Space ID of your current space.
You can also use environment variables ARIZE_SPACE_ID, ARIZE_API_KEY, and ARIZE_PROJECT_NAME instead of passing them directly.
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";import { resourceFromAttributes } from "@opentelemetry/resources";import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";import { SEMRESATTRS_PROJECT_NAME } from "@arizeai/openinference-semantic-conventions";const COLLECTOR_ENDPOINT = "https://otlp.arize.com";const SERVICE_NAME = "manual-js-app";const provider = new NodeTracerProvider({ resource: resourceFromAttributes({ [ATTR_SERVICE_NAME]: SERVICE_NAME, [SEMRESATTRS_PROJECT_NAME]: SERVICE_NAME, }), spanProcessors: [ new SimpleSpanProcessor( new OTLPTraceExporter({ url: `${COLLECTOR_ENDPOINT}/v1/traces`, headers: { 'arize-space-id': 'your-arize-space-id', 'arize-api-key': 'your-arize-api-key', }, }) ), ],});provider.register();
arizeotel.Register returns a configured *sdktrace.TracerProvider, installs it as the global, sets the required openinference.project.name resource attribute, and reads ARIZE_SPACE_ID / ARIZE_API_KEY / ARIZE_PROJECT_NAME from the environment when the matching Options fields are unset.
package mainimport ( "context" "log" "time" arizeotel "github.com/Arize-ai/arize-otel-go" "go.opentelemetry.io/otel")func main() { ctx := context.Background() tp, err := arizeotel.Register(ctx, arizeotel.Options{ ProjectName: "manual-go-app", }) if err != nil { log.Printf("register tracer: %v", err) return } defer func() { // Never call log.Fatalf / os.Exit after a span starts — they // skip this deferred Shutdown and in-flight spans never flush. shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _ = tp.Shutdown(shutdownCtx) }() tracer := otel.Tracer("manual-go-app") _ = tracer // ... your app ...}
For EU-region spaces, pass Endpoint: arizeotel.EndpointArizeEurope. Set ARIZE_SPACE_ID and ARIZE_API_KEY as environment variables — never embed credentials in source.
4
Create spans
Use your tracer to create spans — either as a context manager (to trace a specific block) or inline.
Python
JS/TS
Go
import openaifrom opentelemetry.trace import Status, StatusCodeclient = openai.OpenAI()def run_agent(user_input: str) -> str: with tracer.start_as_current_span("run-agent") as span: span.set_attribute("openinference.span.kind", "CHAIN") span.set_attribute("input.value", user_input) response = call_llm(user_input) span.set_attribute("output.value", response) span.set_status(Status(StatusCode.OK)) return responsedef call_llm(prompt: str) -> str: with tracer.start_as_current_span("llm-completion") as span: span.set_attribute("openinference.span.kind", "LLM") span.set_attribute("input.value", prompt) span.set_attribute("llm.model_name", "gpt-4o") completion = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": prompt}], ) result = completion.choices[0].message.content or "" span.set_attribute("output.value", result) span.set_status(Status(StatusCode.OK)) return result
Use the openinference-semantic-conventions package for typed attribute-key constants — no raw strings. The example below shows the fully-manual pattern (CHAIN + LLM) for clients without a first-party instrumentor. If you’re using openai/openai-go or anthropics/anthropic-sdk-go, drop the inner llm-completion span — the middleware emits it for you — and keep only the surrounding CHAIN.
Indexed attributes — e.g. message lists — are produced by helper functions: semconv.LLMInputMessageRoleKey(0), semconv.LLMInputMessageContentKey(0), and so on. See the package reference for the full set.
Q: Do I have to use an SDK that supports OpenInference?A: No; you can use any OpenTelemetry-compatible tracer. But if you instrument using the OpenInference schema (span kinds + attributes) you’ll get better integration (analytics, visualization) in Arize AX.Q: What if I’m capturing sensitive data (PII) in spans or attributes?A: When using manual instrumentation, you must handle masking, redaction, or encryption as appropriate. See Mask and redact data.