Unomiq uses the unomiq.unit span attribute to link traces to a unit — a logical entity in your application such as a customer ID, app ID, agent ID, or tenant ID. This allows you to view and filter traces by unit in the Unomiq dashboard or Unomiq Engine API.
You can also optionally set unomiq.parent_unit to create a hierarchy of entities, such as linking an app to the organization it belongs to.
This guide shows how to attach the unomiq.unit and unomiq.parent_unit attributes to your spans.
What is a Unit?
A unit represents the entity that a trace belongs to. Common examples:
| Use case | Unit value |
|---|
| Multi-tenant SaaS | Tenant ID or customer ID |
| Agentic System | Agent ID |
| Per-user tracking | User ID |
| Per-application tracking | Application ID |
The value should be a stable identifier that uniquely represents the entity across requests.
You can optionally set unomiq.parent_unit alongside unomiq.unit to express a parent-child relationship between entities. For example, in a multi-tenant SaaS application you might set unomiq.unit to an app ID and unomiq.parent_unit to the tenant or organization that app belongs to.
With unomiq-sdk
The unomiq-sdk package handles unit attachment automatically alongside OAuth-authenticated OTLP export. Pass resolve_unit and resolve_parent_unit callables to Unomiq.init() and the SDK attaches unomiq.unit and unomiq.parent_unit to every span.
Global: Automatic via resolve_unit / resolve_parent_unit
Static values:
from unomiq.sdk import Unomiq
Unomiq.init(
app_name="my-service",
resolve_unit=lambda: "my-unit-id",
resolve_parent_unit=lambda: "my-org-id",
resource_attributes={"service.name": "my-service"},
)
Dynamic per-request (Flask example):
from flask import request
from unomiq.sdk import Unomiq
FALLBACK_UNIT = "default-unit"
def resolve_unit_from_request():
try:
# From a path parameter
customer_id = (request.view_args or {}).get("customer_id")
if customer_id is not None:
return str(customer_id)
# Or from a request header
return request.headers.get("X-Customer-Id") or FALLBACK_UNIT
except RuntimeError:
return FALLBACK_UNIT
Unomiq.init(
app_name="my-service",
resolve_unit=resolve_unit_from_request,
resolve_parent_unit=lambda: "my-org-id",
resource_attributes={"service.name": "my-service"},
)
Manual: Per-span via set_unit / set_parent_unit
For full control, skip the global resolve_unit config and set attributes directly in route handlers:
from opentelemetry import trace
from unomiq.sdk import set_unit, set_parent_unit
@app.route('/customers/<int:customer_id>', methods=['GET'])
def get_customer(customer_id):
span = trace.get_current_span()
set_unit(span, str(customer_id))
set_parent_unit(span, "my-org-id")
# ... rest of handler
Disabling Unit Tracking
Omit resolve_unit (or pass None) and the SDK won’t attach unit attributes:
Unomiq.init(
app_name="my-service",
resource_attributes={"service.name": "my-service"},
)
Without unomiq-sdk
If you are not using unomiq-sdk, you can attach unit attributes directly using the OpenTelemetry SDK.
Setting unomiq.unit on Individual Spans
The most straightforward approach is to set the attribute directly on a span:
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("process-request") as span:
span.set_attribute("unomiq.unit", user_id)
span.set_attribute("unomiq.parent_unit", tenant_id) # optional: parent entity
# ... your code here
unomiq.parent_unit is optional. Set it when you want to place the unit within a broader entity hierarchy.
This works with both manual and automatic instrumentation — as long as you have access to a span, you can set the attribute.
Setting unomiq.unit Automatically with a SpanProcessor
To attach the unit to every span without modifying each call site, use a custom SpanProcessor. This is useful when the unit is available from a request context (e.g., an HTTP header, JWT claim, or path parameter).
from opentelemetry.sdk.trace import SpanProcessor
class UnitSpanProcessor(SpanProcessor):
"""Attaches unomiq.unit to every span from a context resolver."""
def __init__(self, resolve_unit):
"""
Args:
resolve_unit: A callable that returns the current unit value,
or None if no unit is available.
"""
self._resolve_unit = resolve_unit
def on_start(self, span, parent_context=None):
unit = self._resolve_unit()
if unit:
span.set_attribute("unomiq.unit", unit)
def on_end(self, span):
pass
def shutdown(self):
pass
def force_flush(self, timeout_millis=None):
pass
Example: Resolving Unit from a Flask Request
from flask import request, Flask
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
app = Flask(__name__)
def resolve_unit_from_request():
"""Extract the unit from the current request context."""
try:
# Option 1: From a request header
return request.headers.get("X-Customer-Id")
# Option 2: From a JWT claim (after auth middleware)
# return g.current_user.get("tenant_id")
# Option 3: From a path parameter
# return request.view_args.get("customer_id")
except RuntimeError:
# Outside of a request context
return None
# Register the processor with the tracer provider
provider = TracerProvider()
provider.add_span_processor(UnitSpanProcessor(resolve_unit_from_request))
trace.set_tracer_provider(provider)
Example: Resolving Unit from Django
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
# Use threading.local or contextvars to store the unit per-request
import contextvars
_current_unit = contextvars.ContextVar("current_unit", default=None)
# In your Django middleware:
class UnitMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
_current_unit.set(request.headers.get("X-Customer-Id"))
return self.get_response(request)
# Register the processor
provider = TracerProvider()
provider.add_span_processor(UnitSpanProcessor(lambda: _current_unit.get()))
trace.set_tracer_provider(provider)
Summary
| Method | When to use |
|---|
Unomiq.init() with resolve_unit | Recommended. Handles unit attachment, OAuth, and export in one step |
set_unit() / set_parent_unit() | Per-span override when using unomiq-sdk |
span.set_attribute() | Without unomiq-sdk. One-off spans where you have the unit value at hand |
UnitSpanProcessor | Without unomiq-sdk. Automatically attach unit to all spans from request context |
unomiq.unit must be set as a span attribute. Resource attributes and collector-level processors are not supported for this field.