Skip to content

enhancement: record span status and exceptions on the lifecycle telemetry span #392

Description

@codeforester

Problem

finish_telemetry() records rich attributes on the lifecycle span but never sets the span's
status and never records the exception (lib/python/base_cli/integrations.py:127-162). It calls
set_attribute for base_cli.outcome, base_cli.status, base_cli.exit_code, and
base_cli.duration_ms, adds a base_cli.run.finished event, then end.

An OpenTelemetry span whose status is never set stays UNSET. Every observability backend —
Jaeger, Tempo, Honeycomb, Datadog, Cloud Trace — drives its error rates, error-highlighting, and
alerting off span status, not off arbitrary attributes. So a base-cli CLI that fails produces a span
that looks successful in every standard view. For a framework whose stated audience is
operations/SRE, and whose whole value proposition is a consistent operational contract, that
inverts the most important signal.

span.record_exception() is likewise never called, so failed runs carry no exception type,
message, or stack in the trace even though outcome_from_exception() has already classified the
failure precisely.

Verified evidence

Reviewed 2026-09-30 at a58ec109349fa3f3d03eae5b0de078b39ea361a2.

$ grep -n "set_status\|record_exception\|StatusCode" lib/python/base_cli/integrations.py
(no matches)

The available outcome data is unambiguous — InvocationOutcome.kind is already one of success,
usage_error, nonzero_return, interrupted, aborted, click_error, system_exit,
unexpected_error — so the mapping to StatusCode.OK / StatusCode.ERROR is mechanical.

Proposal

  1. Set span status from the outcome: StatusCode.OK for success, StatusCode.ERROR otherwise,
    with description set to the outcome kind. Keep the existing attributes.
  2. Record the exception when one is available, using span.record_exception().
  3. Route both through the existing _safe_span_call() helper so a missing method on a minimal
    test tracer stays non-fatal, and import StatusCode lazily inside the try like trace already is.
  4. Decide deliberately whether a user interrupt (SIGINT / Ctrl+C) is an error span. Either
    choice is defensible; record it. Treating a deliberate cancellation as ERROR generates alert
    noise, so UNSET or OK with an attribute may be better.
  5. Document the status mapping in docs/integrations.md so adopters can build dashboards on it.

Acceptance criteria

  • A failing command produces a span with StatusCode.ERROR; a succeeding command produces OK.
  • An unexpected exception is attached to the span via record_exception().
  • The interrupt policy is documented and tested.
  • A tracer that implements only start_span/end still works (existing best-effort guarantee holds).
  • Ordinary exporter failures still cannot change the command's exit code.

Non-goals

  • Do not make OpenTelemetry a required dependency.
  • Do not add metrics or log-signal export in this issue.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or product improvement

Type

No type

Projects

  • Status
    In Progress

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions