kubit-otel sends OpenTelemetry spans from a Python app to Kubit. It has two entry points: configure() when Kubit is your only exporter, and attach() when your app already creates a TracerProvider. This page covers both, plus the settings to check before production.
Before you start
- Python 3.9 or later
- OpenTelemetry Python SDK 1.20 or later.
kubit-otelinstallsopentelemetry-sdk>=1.20.0andopentelemetry-exporter-otlp-proto-http>=1.20.0,<2if they’re missing. - A Kubit API key in
KUBIT_API_KEY
KUBIT_API_KEY environment variable. Each key belongs to one workspace.
The app shows a new key only once:
This is the only time we'll show you this key. Copy it now. Copy it, or click Download for a .env file, then click I’ve Saved It. The downloaded file names the key KUBIT_OTEL_API_KEY, so rename it to KUBIT_API_KEY.
Availability: API Keys shows only to the Owner and Admin roles, and only in Agent and User workspaces.
Prompt for your coding agent
Install
Choose configure() or attach()
You have no OpenTelemetry setup
Createkubit_instrumentation.py and import it first in your entrypoint.
kubit_instrumentation.py
main.py
configure() parameters
configure() has no batch or sampling parameters. It sends spans through KubitSpanProcessor with its default batch settings: a queue of 2048 spans, a batch every 5000 ms, up to 512 spans per export and a 30000 ms export timeout.
You already have a TracerProvider
Callattach() after trace.set_tracer_provider(), in the module that creates the provider:
tracing.py
attach() finds the global provider and adds KubitSpanProcessor to it. Your provider’s resource (service.name, service.version) applies to the spans Kubit receives.
attach() parameters
Availability:
attach() must run after trace.set_tracer_provider(). Called earlier, it raises RuntimeError and adds nothing to any provider.
Trace LLM and tool calls
KubitSpanProcessor sends only LLM spans by default: spans from the kubit-sdk tracer, spans with any gen_ai.* attribute and spans from a known LLM instrumentation scope. To get one span per LLM call, add the OpenTelemetry GenAI instrumentor for your client after configure() or attach():
kubit_instrumentation.py
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true puts prompt and response text on the spans. Polaris labels spans from that text.
For tool calls, wrap each tool in its own span so it shows up as a step in the execution path:
tools.py
Add user and session IDs
Kubit groups traces into sessions and joins them to users from IDs on your spans. Setuser.id and session.id on the root span of each request. The gen_ai.operation.name attribute keeps your own span past the default filter, which drops spans with no gen_ai.* attribute:
handler.py
enduser.id or user.id for the user, and session.id or gen_ai.conversation.id for the session. To read other keys, edit the mapping as described in Check the extraction mapping.
Mask sensitive content
Themask option redacts span content before the span leaves your process. KubitSpanProcessor runs your function on each span that passes the filter, before the span is queued for export. mask needs kubit-otel 0.7.3 or later and works with configure(), attach() and KubitSpanProcessor.
kubit_instrumentation.py
gen_ai.prompt and drops gen_ai.user.message events, where newer GenAI instrumentors record the prompt.
The helpers in kubit_otel.mask are the supported way to change a span inside mask:
Constraint: When
mask raises or returns None, the SDK sends a tombstone in place of the span. The tombstone keeps the trace and span IDs, parent, timing and kind. Its attributes and events are removed, its status is ERROR with the description kubit-otel mask failed, and its kubit.sdk.mask_error attribute holds the exception class name or returned_none. Unmasked data is never sent. The SDK logs one of these lines, with the traceback for a raised exception:
Output
KubitExporter that you wrap in your own span processor applies neither mask nor the default filter. Apply both in your processor.
Before production
Verify
Send a test span with debug logging on:force_flush() sends the span now. Without it, the span waits up to 5000 ms in the batch.
With the configure() file from You have no OpenTelemetry setup, the output on stderr includes these lines, among other debug lines:
Output
endpoint=<from OTEL_EXPORTER_OTLP_*> is the label kubit-otel prints when you don’t pass endpoint. The SDK still sends to KUBIT_OTEL_ENDPOINT or https://otel.kubit.ai/v1/traces. With attach(), the first line is [kubit-otel info] kubit_otel attached endpoint=<from OTEL_EXPORTER_OTLP_*>. KUBIT_OTEL_LOG_LEVEL takes debug, info, warn or error, and the default is info.
Then open Agent > Activity, set Time Range to Last 1 day and click Execute. The hello-kubit trace is in the list. Validate in Activity shows what to check in the trace.
If it fails
Next steps
- Quickstart: check the extraction mapping and validate in Activity.
- Is my data flowing?: step-by-step checks.