Skip to content

Add OTEL_SEMCONV_EXCEPTION_SIGNAL_OPT_IN support to record_exception#5323

Open
RKest wants to merge 1 commit into
open-telemetry:mainfrom
RKest:record-exception-signal-opt-in
Open

Add OTEL_SEMCONV_EXCEPTION_SIGNAL_OPT_IN support to record_exception#5323
RKest wants to merge 1 commit into
open-telemetry:mainfrom
RKest:record-exception-signal-opt-in

Conversation

@RKest

@RKest RKest commented Jun 18, 2026

Copy link
Copy Markdown

Description

Span.record_exception currently always records exceptions as span
events
. The semantic conventions for
exceptions in logs
and the span-event API deprecation plan
(OTEP 4430,
see also the
Deprecating Span Events
blog post) introduce the OTEL_SEMCONV_EXCEPTION_SIGNAL_OPT_IN environment
variable so instrumentations can migrate exceptions from span events to logs.

This PR makes the SDK's Span.record_exception (and therefore the
__exit__ / use_span paths that call it) honor
OTEL_SEMCONV_EXCEPTION_SIGNAL_OPT_IN:

Value Behavior
(unset) Span events only — existing behavior (default).
logs Record the exception as a log only.
logs/dup Record the exception as both a log and a span event (phased rollout).

Any unrecognized value is treated as unset, preserving today's behavior.

Implementation notes:

  • The exception log is emitted via the global LoggerProvider, correlated
    with the originating span's context (trace_id / span_id), at ERROR
    severity, with event_name="exception".
  • The log carries exception.type, exception.message and
    exception.stacktrace. The deprecated exception.escaped attribute is
    intentionally omitted from the log representation; it is still set on the
    span event when one is recorded (logs/dup or default).
  • The log is emitted under the span's own instrumentation scope.
  • OTEL_SEMCONV_EXCEPTION_SIGNAL_OPT_IN is an experimental, transitional
    opt-in, so it is kept private to opentelemetry.sdk.trace rather than
    exported from opentelemetry.sdk.environment_variables.

The default behavior is unchanged, so this is fully backwards compatible and
opt-in.

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

A new test module opentelemetry-sdk/tests/trace/test_record_exception_logs.py
covers all modes end to end using an in-memory span exporter and a captured
logger:

  • unset / unrecognized values record a span event and emit no log
  • logs records a log only (no span event)
  • logs/dup records both a log and a span event
  • the emitted log is correlated with the originating span
    (trace_id / span_id)
  • the log uses the span's instrumentation scope
  • exception.type / exception.message / exception.stacktrace are
    populated and extra attributes are forwarded
  • the deprecated exception.escaped attribute is not set on the log

The existing record_exception tests in
opentelemetry-sdk/tests/trace/test_trace.py continue to pass unchanged.

uv run pytest opentelemetry-sdk/tests/trace/test_record_exception_logs.py
uv run pytest opentelemetry-sdk/tests/trace/test_trace.py -k record_exception

Does This PR Require a Contrib Repo Change?

  • Yes. - Link to PR:
  • No. The record_exception signature is unchanged; the new behavior is
    gated behind an environment variable and defaults to today's behavior.

Checklist:

  • Followed the style guidelines of this project (ruff check / ruff format)
  • Changelogs have been updated (.changelog/5318.added — rename to match
    the assigned PR number when opening the PR)
  • Unit tests have been added
  • Documentation has been updated (env var docstring + record_exception
    docstring)

@RKest RKest requested a review from a team as a code owner June 18, 2026 12:38
@aabmass aabmass requested a review from Copilot June 24, 2026 20:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds opt-in support for recording exceptions as logs (and optionally duplicating to span events) in the SDK’s Span.record_exception, enabling migration away from span events per the exception-as-logs semantic conventions and the span-event deprecation plan.

Changes:

  • Update Span.record_exception to honor OTEL_SEMCONV_EXCEPTION_SIGNAL_OPT_IN and emit an exception LogRecord correlated with the span when configured.
  • Add end-to-end unit tests covering unset/unrecognized/logs/logs-dup behaviors, correlation, scope usage, and attribute mapping.
  • Add a changelog entry documenting the new opt-in behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
opentelemetry-sdk/src/opentelemetry/sdk/trace/init.py Adds env-var gated branching in record_exception and a helper to emit correlated exception logs.
opentelemetry-sdk/tests/trace/test_record_exception_logs.py New tests validating exception-as-logs behavior across all opt-in modes.
.changelog/5318.added Notes the new env-var controlled behavior for Span.record_exception.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
Comment thread opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py Outdated
@RKest RKest force-pushed the record-exception-signal-opt-in branch from 828822d to e039edd Compare June 29, 2026 18:03
@RKest RKest requested a review from Copilot June 29, 2026 18:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py Outdated
`Span.record_exception` now honors the `OTEL_SEMCONV_EXCEPTION_SIGNAL_OPT_IN`
environment variable, per the semantic conventions for exceptions in logs
(https://opentelemetry.io/docs/specs/semconv/exceptions/exceptions-logs/):

- unset: record exceptions as span events only (unchanged default)
- `logs`: record exceptions as logs only
- `logs/dup`: record exceptions as both logs and span events

Exception logs are emitted at ERROR severity, correlated with the
originating span's context, and use the span's instrumentation scope.
@RKest RKest force-pushed the record-exception-signal-opt-in branch from e039edd to 01425f1 Compare July 6, 2026 13:37
@RKest RKest requested a review from Copilot July 6, 2026 14:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment on lines 1094 to +1106
@@ -1074,6 +1099,19 @@ def record_exception(
if module and module != "builtins"
else qualname
)

signal = _exception_signal_opt_in()
if signal in (_EXCEPTION_SIGNAL_LOGS, _EXCEPTION_SIGNAL_LOGS_DUP):
self._record_exception_log(
exception_type=exception_type,

@RKest RKest Jul 6, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking the _check_span_ended for exception logs is not necessary.

The check is likely for spans, because an ended span, might already be exported, so modifying it (through adding span events for example) should not be allowed.

Same constraint is not present in logs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

your reasoning makes sense to me

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

LogRecord(
timestamp=timestamp if timestamp is not None else time_ns(),
context=trace_api.set_span_in_context(self),
event_name="exception",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://opentelemetry.io/docs/specs/semconv/exceptions/exceptions-logs/#event-name -- should we allow an event name to be passed through here via record_exception ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xrmx xrmx moved this to Reviewed PRs that need fixes in Python PR digest Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Reviewed PRs that need fixes

Development

Successfully merging this pull request may close these issues.

4 participants