Understand OpenTelemetry and OpenInference, configure span processors, resource attributes, and multi-project routing
Take full control of OpenTelemetry. The getting started pages cover register() and OpenInference integrations — this page is for when you need more: batch processing for production, routing spans to multiple projects, or configuring resource attributes directly via the OpenTelemetry SDK.
OpenInference provides auto-instrumentors for popular frameworks. Install the package for your provider and attach it once — .instrument() in Python, registerInstrumentations({...}) / package-specific setup in JS/TS, or option.WithMiddleware(...) on the SDK client in Go — and every call is traced automatically.
The quickest setup is a single helper that creates an OpenTelemetry TracerProvider wired to Arize AX, attaches the span exporter, and sets it as the global provider. Each SDK reads your space, API key, and project from the environment, so the minimal call needs no explicit credentials. For the full Python SDK reference, see OpenTelemetry Tracing.
Python
JS/TS
Go
arize-otel’s register() returns a TracerProvider you hand to each OpenInference instrumentor’s .instrument(tracer_provider=...) call. All parameters are keyword-only:
Parameter
Default
Description
space_id
ARIZE_SPACE_ID env var
Arize space the spans are sent to.
api_key
ARIZE_API_KEY env var
Arize API key used to authenticate.
project_name
ARIZE_PROJECT_NAME env var, else "default"
Project the spans are grouped under. This is the only project/name argument — register() has no model_id or model_version parameter.
endpoint
ARIZE_COLLECTOR_ENDPOINT env var, else Arize
OTLP collector endpoint spans are exported to.
transport
Transport.GRPC
Export transport: Transport.GRPC, Transport.HTTP, or Transport.HTTPS.
batch
True
Use a BatchSpanProcessor (production) rather than a SimpleSpanProcessor.
headers
None
Extra headers to include on requests to the collector.
set_global_tracer_provider
True
Set the returned provider as the global OpenTelemetry default.
verbose
True
Print the tracing configuration to stdout on startup.
log_to_console
False
Also export spans to the console, useful for debugging.
auto_instrument
False
Automatically instrument every installed OpenInference library.
span_processors
None
Additional span processors to run before the Arize exporter.
from arize.otel import registertracer_provider = register( space_id="your-space-id", # or the ARIZE_SPACE_ID env var api_key="your-api-key", # or the ARIZE_API_KEY env var project_name="my-llm-app", # or the ARIZE_PROJECT_NAME env var)
The JavaScript SDK has no single register() helper — you configure an OpenTelemetry provider (or a framework hook such as Vercel’s registerOTel) and route spans to a project with the project-name resource attribute. The settings that matter:
Setting
Where
Description
SEMRESATTRS_PROJECT_NAME
Resource attribute
Project the spans are grouped under. Import from @arizeai/openinference-semantic-conventions; without it spans land in default. There is no model_id argument.
arize-otel-go’s arizeotel.Register(ctx, arizeotel.Options{...}) sets the required openinference.project.name resource attribute, installs otel.SetTracerProvider, defaults to a BatchSpanProcessor, and falls back to environment variables when the matching Options fields are unset:
Field
Default
Description
SpaceID
$ARIZE_SPACE_ID
Required.
APIKey
$ARIZE_API_KEY
Required.
ProjectName
$ARIZE_PROJECT_NAME or "default"
Sets the openinference.project.name resource attribute. There is no model_id field.
Endpoint
$ARIZE_COLLECTOR_ENDPOINT or otlp.arize.com
Use arizeotel.EndpointArizeEurope for EU spaces.
ExtraHeaders
none
Merged into OTLP request headers alongside the required space_id / api_key.
ExtraResourceAttributes
none
Appended to the OTel Resource.
Insecure
false
Disables TLS — only for on-prem / local collectors.
SimpleProcessor
false (batched)
Synchronous export. Useful for tests and short CLIs.
SkipSetGlobal
false
Skip otel.SetTracerProvider. Set if you manage the global yourself.
tp, err := arizeotel.Register(ctx, arizeotel.Options{ ProjectName: "your-project-name", // or the ARIZE_PROJECT_NAME env var})
These helpers cover most apps — but when you need more control over the tracer itself, configure OpenTelemetry directly:
import { registerInstrumentations } from "@opentelemetry/instrumentation";import { OpenAIInstrumentation } from "@arizeai/openinference-instrumentation-openai";import { ConsoleSpanExporter } from "@opentelemetry/sdk-trace-base";import { NodeTracerProvider, BatchSpanProcessor } from "@opentelemetry/sdk-trace-node";import { resourceFromAttributes } from "@opentelemetry/resources";import { OTLPTraceExporter as GrpcOTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc";import { Metadata } from "@grpc/grpc-js";const metadata = new Metadata();metadata.set("arize-space-id", "your-space-id");metadata.set("arize-api-key", "your-api-key");const provider = new NodeTracerProvider({ resource: resourceFromAttributes({ "model_id": "your-project-name", "model_version": "v1", }), spanProcessors: [ new BatchSpanProcessor(new ConsoleSpanExporter()), new BatchSpanProcessor( new GrpcOTLPTraceExporter({ url: "https://otlp.arize.com/v1", metadata, }) ), ],});registerInstrumentations({ instrumentations: [new OpenAIInstrumentation({})],});provider.register();
arize-otel-go’s arizeotel.Register (see Set up your tracer) covers most apps in one call. When you need multiple exporters (Arize + a local console for debugging, or Arize + Datadog), drop to raw sdktrace.NewTracerProvider:
import ( "context" "os" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace")ctx := context.Background()arizeExporter, _ := otlptracehttp.New(ctx, otlptracehttp.WithEndpoint("otlp.arize.com"), otlptracehttp.WithHeaders(map[string]string{ "space_id": os.Getenv("ARIZE_SPACE_ID"), "api_key": os.Getenv("ARIZE_API_KEY"), }),)consoleExporter, _ := stdouttrace.New(stdouttrace.WithPrettyPrint())// openinference.project.name is required so the collector can route spans to the right project.res, _ := resource.New(ctx, resource.WithAttributes( attribute.String("openinference.project.name", "your-project-name"), attribute.String("model.version", "v1"),))tp := sdktrace.NewTracerProvider( sdktrace.WithBatcher(arizeExporter), // production export to Arize sdktrace.WithBatcher(consoleExporter), // local debugging — drop in production sdktrace.WithResource(res),)otel.SetTracerProvider(tp)defer tp.Shutdown(ctx) // flushes batched spans before exit
Resource attributes describe the source of telemetry (service, model, environment). Set once on the TracerProvider.
Span attributes describe a single span. Set per-span in your code.
Span processors filter, batch, and perform operations on spans before export.
Project name resource attribute. The canonical OpenInference key is openinference.project.name — exposed as ResourceAttributes.PROJECT_NAME from openinference.semconv.resource in Python, SEMRESATTRS_PROJECT_NAME from @arizeai/openinference-semantic-conventions in JS/TS, and set automatically by arize-otel-go’s Options.ProjectName in Go. The Arize collector also accepts model_id as a legacy alias (shown in some older Python/JS examples on this page); both route spans to the same project.
OTLP auth headers. The Arize collector accepts both space_id / api_key (canonical, what arize-otel-go sends) and the arize-space-id / arize-api-key aliases shown in the older Python/JS examples. If you copy the raw-OTel Go snippet below into a stack that already uses the arize- prefixed form, pick one and stay consistent.
The most important processor decision for production is which span processor to use:
To route traces from one application to multiple Arize spaces or projects, use register_with_routing from arize-otel:
pip install arize-otel
from arize.otel import register_with_routing, set_routing_context# Register once with a single API key — routing happens per-contexttracer_provider = register_with_routing( api_key="your-api-key",)# Route specific operations to a different space + projectwith set_routing_context(space_id="other-space-id", project_name="other-project"): # Spans created in this block are routed to "other-space-id" / "other-project" ...
register_with_routing uses ARIZE_API_KEY from your environment if api_key isn’t passed. Both space_id and project_name must be set inside set_routing_context — otherwise routing won’t be applied.Python-only today. For JS/TS or Go apps — or more complex routing (e.g., by span attribute) — route at the OTel Collector layer instead. See OTEL Collector deployment patterns.
If you operate a centralized OpenTelemetry Collector serving many teams or spaces, see the shared-collector pattern that forwards arize-space-id from inbound request metadata — avoids redeploying the collector each time a new space is added.