Skip to main content
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 caseUnit value
Multi-tenant SaaSTenant ID or customer ID
Agentic SystemAgent ID
Per-user trackingUser ID
Per-application trackingApplication 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

MethodWhen to use
Unomiq.init() with resolve_unitRecommended. 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
UnitSpanProcessorWithout 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.