Tracing#
OpenTelemetry tracing is an optional extra. Install it with:
pip install "plone.observability[opentelemetry]"
Tracing is OTel-native: it honors the standard OTEL_* environment variables and activates when the extra is installed and an OTLP endpoint is configured.
The environment variables are listed in Configuration.
Emitted spans#
Span |
Emitted |
Key attributes |
|---|---|---|
root request span |
per request, from the WSGI filter |
standard HTTP attributes |
|
once per request |
|
|
per catalog query |
|
|
per catalog query |
|
|
per ZODB transaction completion |
— |
|
per response-transform phase |
|
|
per transform, child of |
|
subrequest span |
per |
— |
The catalog.* spans cover both standard Plone and plone-pgcatalog.
The transformchain spans are emitted only when plone.transformchain is installed, and the subrequest span only when plone.subrequest is installed.
The root request span requires the opentelemetry WSGI filter.
Without it you still get the publishing, catalog, and commit spans, which are registered through ZCML, but not the outer WSGI span.
See How to enable OpenTelemetry tracing.
Per-span ZODB attributes#
The ZPublisher.publish span (request total), each subrequest span (per-tile delta), and each render span carry per-span ZODB object-load counts:
Attribute |
Meaning |
|---|---|
|
objects materialised (loaded) within the span |
|
objects stored within the span |
|
time spent materialising objects (round-trip + decode) |
|
objects served from zodb-pgjsonb’s shared (L2) cache |
|
objects fetched from PostgreSQL |
|
PostgreSQL round-trips (queries) |
The last three require zodb-pgjsonb (>= 1.16.0 for load_pg_queries); on other storages they read as 0 (best-effort, no dependency).
load_pg_objects / load_pg_queries is objects-per-round-trip: ≈ 1 signals an N+1 pattern, ≫ 1 signals well-batched loads (load_multiple/prefetch), so prefetch adoption is visible without a wall-time A/B.
Custom spans#
Application code can open child spans with a dependency-optional helper. It is a no-op when the extra is not installed.
from plone.observability.spans import start_span
with start_span("myapp.expensive_step", {"items": n}):
do_work()
start_span(name, attributes=None) is a context manager.
It opens a child of the currently active span and yields the span object, or None when the opentelemetry extra is not installed.
See also
How to add custom spans in your code walks through instrumenting your own code.
About tracing explains why the built-in spans come from Zope events and how tracing relates to metrics.