How to add custom spans in your code

This guide shows you how to trace your own code by opening spans from an add-on or project. Use it when a request spends time in code that plone.observability does not already instrument, and you want that time to show up in your traces.

For the helper signature, see Tracing.

Open a span around your code

Import start_span and wrap the work in a with block.

from plone.observability.spans import start_span


def rebuild_report(items):
    with start_span("myaddon.rebuild_report"):
        for item in items:
            process(item)

The span nests automatically under whatever span is currently active, usually the ZPublisher.publish span for the request. It appears in your trace backend as a child, with its own start time and duration.

Attach attributes

Pass a dictionary of attributes to record context alongside the span.

with start_span("myaddon.rebuild_report", {"myaddon.item_count": len(items)}):
    for item in items:
        process(item)

Keep attribute values bounded in the same way you keep metric labels bounded: a count or a category is fine, an unbounded id list is not.

Add attributes or events during the span

start_span yields the span object, so you can enrich it as the work proceeds. Without the opentelemetry extra installed the helper yields None, so guard any use of the yielded value.

with start_span("myaddon.rebuild_report") as span:
    result = run()
    if span is not None:
        span.set_attribute("myaddon.rows_written", result.rows)

Nest spans

Spans opened inside another start_span block nest under it, so you can break a large operation into named phases.

with start_span("myaddon.import"):
    with start_span("myaddon.import.parse"):
        data = parse(source)
    with start_span("myaddon.import.store"):
        store(data)

Leave it in production code

start_span is dependency-optional: when the opentelemetry extra is not installed it is a no-op context manager that yields None. You can call it unconditionally in shipped code without adding a hard dependency on OpenTelemetry, and it costs nothing when tracing is off.

Name your spans with a stable, low-cardinality prefix such as your add-on name, for example myaddon.import.store. Never put ids or other unbounded values in the span name.

See also