Skip to content

Add logger exception support for logs API/SDK - #4908

Merged
xrmx merged 35 commits into
open-telemetry:mainfrom
iblancasa:4907
Apr 13, 2026
Merged

xrmx merged 35 commits into
open-telemetry:mainfrom
iblancasa:4907

Conversation

@iblancasa

@iblancasa iblancasa commented Feb 11, 2026 •

Copy link
Copy Markdown
Member

Description

Fixes #4907

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration

  • Unit tests

Does This PR Require a Contrib Repo Change?

  • Yes. - Link to PR:
  • No.

Checklist:

  • Followed the style guidelines of this project
  • Changelogs have been updated
  • Unit tests have been added

@iblancasa
iblancasa requested a review from a team as a code owner February 11, 2026 17:28
@iblancasa
iblancasa marked this pull request as draft February 11, 2026 17:34
Signed-off-by: Israel Blancas <iblancasa@gmail.com>
@xrmx

xrmx commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

Haven't looked in detail at the PR but we are usually cautious on adding in development stuff to the api / sdk.

@trask

trask commented Feb 13, 2026

Copy link
Copy Markdown
Member

hey @xrmx! check out open-telemetry/opentelemetry-specification#4886

if you can give this PR a basic review and "lgtm" without approving/merging it, then we can treat it as a "qualifying prototype" for marking it stable in the spec. we already have 3 qualifying prototypes which is the min bar for stabilizing the spec, but the more the better!

@MikeGoldsmith MikeGoldsmith left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

github-merge-queue Bot pushed a commit to open-telemetry/opentelemetry-specification that referenced this pull request Feb 18, 2026
Fixes #4858

Implementations / Prototypes:

- Java:
[`ExtendedLogRecordBuilder.setException(Throwable)`](https://github.com/open-telemetry/opentelemetry-java/blob/main/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogRecordBuilder.java#L137)
- Builder method called before `emit()`
  - Merged and available to users
- .NET:
[`LogRecordAttributeList.RecordException(Exception)`](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Api/Logs/LogRecordAttributeList.cs#L191-L198)
- Attribute list method called before `EmitLog()`
- JavaScript:
[opentelemetry-js#6385](open-telemetry/opentelemetry-js#6385)
- Field on LogRecord passed to `emit()`
- Python:
[opentelemetry-python#4908](open-telemetry/opentelemetry-python#4908)
- Parameter on LogRecord passed to `emit()`

(only the Python prototype doesn't yet meet the definition of
[qualifying
prototype](https://github.com/open-telemetry/opentelemetry-specification/blob/main/CONTRIBUTING.md#proposing-a-change)
yet)

Co-authored-by: Robert Pająk <pellared@hotmail.com>
@xrmx

xrmx commented Feb 18, 2026

Copy link
Copy Markdown
Contributor

hey @xrmx! check out open-telemetry/opentelemetry-specification#4886

if you can give this PR a basic review and "lgtm" without approving/merging it, then we can treat it as a "qualifying prototype" for marking it stable in the spec. we already have 3 qualifying prototypes which is the min bar for stabilizing the spec, but the more the better!

Missed this comment but already approved the other PR 😅

@nagkumar91 nagkumar91 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nice implementation — spec compliance looks solid. A couple of nits below.

Signed-off-by: Israel Blancas <iblancasa@gmail.com>
@iblancasa
iblancasa requested a review from nagkumar91 March 17, 2026 20:11
@MikeGoldsmith

Copy link
Copy Markdown
Member

Looks like there are some merge conflicts that need fixing - otherwise I think this is ready for review / merge by a @open-telemetry/python-maintainers 👍🏻

@MikeGoldsmith MikeGoldsmith moved this from Ready for review to Approved PRs in Python PR digest Apr 8, 2026

@xrmx xrmx 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.

With something like this, tests should still pass:

diff --git a/opentelemetry-api/src/opentelemetry/_logs/_internal/__init__.py b/opentelemetry-api/src/opentelemetry/_logs/_internal/__init__.py
index 6264b6eca..12a8cc08f 100644
--- a/opentelemetry-api/src/opentelemetry/_logs/_internal/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/_logs/_internal/__init__.py
@@ -167,8 +167,6 @@ class Logger(ABC):
     def emit(
         self,
         record: LogRecord,
-        *,
-        exception: BaseException | None = None,
     ) -> None: ...
 
     @abstractmethod
@@ -214,8 +212,6 @@ class NoOpLogger(Logger):
     def emit(  # pylint:disable=arguments-differ
         self,
         record: LogRecord,
-        *,
-        exception: BaseException | None = None,
     ) -> None: ...
 
     def emit(
@@ -284,8 +280,6 @@ class ProxyLogger(Logger):
     def emit(  # pylint:disable=arguments-differ
         self,
         record: LogRecord,
-        *,
-        exception: BaseException | None = None,
     ) -> None: ...
 
     def emit(
@@ -303,7 +297,7 @@ class ProxyLogger(Logger):
         exception: BaseException | None = None,
     ) -> None:
         if record:
-            self._logger.emit(record, exception=exception)
+            self._logger.emit(record)
         else:
             self._logger.emit(
                 timestamp=timestamp,
diff --git a/opentelemetry-api/tests/logs/test_proxy.py b/opentelemetry-api/tests/logs/test_proxy.py
index f7744e5f3..1b5d0c22c 100644
--- a/opentelemetry-api/tests/logs/test_proxy.py
+++ b/opentelemetry-api/tests/logs/test_proxy.py
@@ -77,15 +77,12 @@ class TestProxy(LoggingGlobalsTest, unittest.TestCase):
         real_logger = provider.get_logger("proxy-test")
         self.assertIsInstance(real_logger, LoggerTest)
 
-    def test_proxy_logger_forwards_exception_with_record(self):
+    def test_proxy_logger_forwards_record_with_exception(self):
         logger = _logs_internal.ProxyLogger("proxy-test")
         logger._real_logger = Mock(spec=LoggerTest("proxy-test"))
-        record = _logs.LogRecord()
-        exception = ValueError("boom")
+        record = _logs.LogRecord(exception=ValueError("boom"))
 
         self.assertIsNotNone(logger._real_logger)
-        logger.emit(record, exception=exception)
+        logger.emit(record)
 
-        logger._real_logger.emit.assert_called_once_with(
-            record, exception=exception
-        )
+        logger._real_logger.emit.assert_called_once_with(record)
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py
index 3633a308c..4ed043e67 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py
@@ -793,19 +793,12 @@ class Logger(APILogger):
             return
         # If a record is provided, use it directly
         if record is not None:
-            record_exception = exception or getattr(record, "exception", None)
-            if record_exception is None and isinstance(
-                record, ReadWriteLogRecord
-            ):
-                record_exception = getattr(
-                    record.log_record, "exception", None
-                )
             if not isinstance(record, ReadWriteLogRecord):
-                if record_exception is not None:
+                if record.exception is not None:
                     record = _copy_log_record(
                         record,
                         _get_attributes_with_exception(
-                            record.attributes, record_exception
+                            record.attributes, record.exception
                         ),
                     )
                 # pylint:disable=protected-access
@@ -816,7 +809,7 @@ class Logger(APILogger):
                 )
             else:
                 record.log_record.attributes = _get_attributes_with_exception(
-                    record.log_record.attributes, record_exception
+                    record.log_record.attributes, record.log_record.exception
                 )
                 writable_record = record
         else:

iblancasa and others added 9 commits April 10, 2026 12:51
Signed-off-by: Israel Blancas <iblancasa@gmail.com>
Signed-off-by: Israel Blancas <iblancasa@gmail.com>
Signed-off-by: Israel Blancas <iblancasa@gmail.com>
Signed-off-by: Israel Blancas <iblancasa@gmail.com>
Signed-off-by: Israel Blancas <iblancasa@gmail.com>
@xrmx
xrmx enabled auto-merge (squash) April 13, 2026 15:35
@xrmx
xrmx merged commit 0eedb27 into open-telemetry:main Apr 13, 2026
465 of 466 checks passed
@github-project-automation github-project-automation Bot moved this from Approved PRs to Done in Python PR digest Apr 13, 2026
chimchim89 pushed a commit to chimchim89/opentelemetry-python that referenced this pull request Apr 15, 2026
* Add logger exception support for logs API/SDK

Signed-off-by: Israel Blancas <iblancasa@gmail.com>

* Apply changes requested in code review

Signed-off-by: Israel Blancas <iblancasa@gmail.com>

* Fix CI

Signed-off-by: Israel Blancas <iblancasa@gmail.com>

* Fix ci

Signed-off-by: Israel Blancas <iblancasa@gmail.com>

* Apply feedback from code review

Signed-off-by: Israel Blancas <iblancasa@gmail.com>

* Fix lint

Signed-off-by: Israel Blancas <iblancasa@gmail.com>

* Remove unrelated entry from changelog

Signed-off-by: Israel Blancas <iblancasa@gmail.com>

* Fix lint

Signed-off-by: Israel Blancas <iblancasa@gmail.com>

* Fix lint

Signed-off-by: Israel Blancas <iblancasa@gmail.com>

---------

Signed-off-by: Israel Blancas <iblancasa@gmail.com>
Co-authored-by: Riccardo Magliocchetti <riccardo.magliocchetti@gmail.com>
Co-authored-by: Mike Goldsmith <goldsmith.mike@gmail.com>
pull Bot pushed a commit to thompson-tomo/opentelemetry-specification that referenced this pull request Sep 11, 2026
…y#5190)

## Changes

Updated the spec compliance entries for Python. Most of the work of
finding the corresponding PRs/evidence was performed by Codex of which I
checked for correctness.

For non-trivial changes, follow the [change proposal
process](https://github.com/open-telemetry/opentelemetry-specification/blob/main/CONTRIBUTING.md#proposing-a-change).

cc: @open-telemetry/python-maintainers @open-telemetry/python-approvers 

* [ ] Related issues #
* [ ] Related [OTEP(s)](https://github.com/open-telemetry/oteps) #
* [ ] Links to the prototypes (when adding or changing features)
* [ ]
[`CHANGELOG.md`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/CHANGELOG.md)
file updated for non-trivial changes
* For trivial changes, include `[chore]` in the PR title to skip the
changelog check
* [x] [Spec compliance
matrix](https://github.com/open-telemetry/opentelemetry-specification/blob/main/spec-compliance-matrix/template.yaml)
updated if necessary
* [ ] [Declarative config data
model](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/configuration/data-model.md#overview)
is updated if SDK config surface is changed

## Trace

- **W3C Trace Context Level 2 randomness**:
`RandomIdGenerator.is_trace_id_random()` and
`TraceFlags.RANDOM_TRACE_ID` are implemented and tested. PR:
[open-telemetry#4854](open-telemetry/opentelemetry-python#4854).
Issue: none found.

## Metrics

- **Duplicate instrument registration uses first-seen stream name**:
`Meter._register_instrument()` stores first advisory data and reports
conflicts using the existing advisory. PR:
[open-telemetry#4361](open-telemetry/opentelemetry-python#4361).
Issues:
[open-telemetry#3042](open-telemetry/opentelemetry-python#3042),
[open-telemetry#4140](open-telemetry/opentelemetry-python#4140).
- **Advisory ExplicitBucketBoundaries parameter**: histogram advisory
boundaries are accepted, stored, and consumed by explicit bucket
aggregation. PR:
[open-telemetry#4361](open-telemetry/opentelemetry-python#4361).
Issues:
[open-telemetry#3042](open-telemetry/opentelemetry-python#3042),
[open-telemetry#4140](open-telemetry/opentelemetry-python#4140).
- **MeterProvider methods safe for concurrent calls**: locks protect
meter creation, reader registration, and global reader tracking. PRs:
[open-telemetry#4913](open-telemetry/opentelemetry-python#4913),
[open-telemetry#4863](open-telemetry/opentelemetry-python#4863).
Issues:
[open-telemetry#4892](open-telemetry/opentelemetry-python#4892),
[open-telemetry#4818](open-telemetry/opentelemetry-python#4818).
- **Meter methods safe for concurrent calls**:
`_instrument_registration_lock` protects instrument registration;
concurrent instrument creation is tested. PR:
[open-telemetry#4913](open-telemetry/opentelemetry-python#4913).
Issue:
[open-telemetry#4892](open-telemetry/opentelemetry-python#4892).
- **Instrument methods safe for concurrent calls**: aggregation state
and per-attribute aggregation creation are lock-protected. PR:
[open-telemetry#4913](open-telemetry/opentelemetry-python#4913).
Issue:
[open-telemetry#4892](open-telemetry/opentelemetry-python#4892).
- **View configures exemplar reservoir**: `View` accepts
`exemplar_reservoir_factory`; defaults select histogram-aligned or
fixed-size reservoirs. PR:
[#4094](open-telemetry/opentelemetry-python#4094).
Issues:
[open-telemetry#2407](open-telemetry/opentelemetry-python#2407),
[opentelemetry-python-contrib
open-telemetry#2158](open-telemetry/opentelemetry-python-contrib#2158).
- **Metric exporter export is not called concurrently**:
`PeriodicExportingMetricReader` holds `_export_lock` around exporter
calls. PR:
[open-telemetry#2873](open-telemetry/opentelemetry-python#2873).
Issue:
[open-telemetry#2859](open-telemetry/opentelemetry-python#2859).
- **Metric exporter export does not block indefinitely**: export paths
pass timeout/deadline information through reader and exporter calls. PR:
[open-telemetry#2653](open-telemetry/opentelemetry-python#2653).
Issue:
[#2402](open-telemetry/opentelemetry-python#2402).
- **Metric exporter shutdown does not block indefinitely**: shutdown
computes deadlines and passes remaining timeout to readers/exporters.
PR:
[open-telemetry#2653](open-telemetry/opentelemetry-python#2653).
Issue:
[#2402](open-telemetry/opentelemetry-python#2402).
- **SDK samples Exemplars from measurements**: measurement consumers
invoke exemplar filters and aggregations offer measurements to
reservoirs. PR:
[#4094](open-telemetry/opentelemetry-python#4094).
Issue:
[open-telemetry#2407](open-telemetry/opentelemetry-python#2407).
- **Exemplar sampling can be disabled**: `AlwaysOffExemplarFilter` and
`OTEL_METRICS_EXEMPLAR_FILTER=always_off` are implemented and tested.
PR:
[#4094](open-telemetry/opentelemetry-python#4094).
Issue:
[open-telemetry#2407](open-telemetry/opentelemetry-python#2407).
- **SDK-wide exemplar filter configuration**:
`MeterProvider(exemplar_filter=...)` and `OTEL_METRICS_EXEMPLAR_FILTER`
select exemplar filters. PR:
[#4094](open-telemetry/opentelemetry-python#4094).
Issue:
[open-telemetry#2407](open-telemetry/opentelemetry-python#2407).
- **TraceBased, AlwaysOn, and AlwaysOff exemplar filters**: all three
filter classes are publicly exported by the metrics SDK. PR:
[#4094](open-telemetry/opentelemetry-python#4094).
Issue:
[open-telemetry#2407](open-telemetry/opentelemetry-python#2407).
- **Exemplars retain filtered measurement attributes**: exemplar
collection stores measurement attributes not present on the data point.
PR:
[#4094](open-telemetry/opentelemetry-python#4094).
Issue:
[open-telemetry#2407](open-telemetry/opentelemetry-python#2407).
- **Exemplars contain active trace ID and span ID**: exemplar offers
read the current span from context and store IDs. PR:
[#4094](open-telemetry/opentelemetry-python#4094).
Issue:
[open-telemetry#2407](open-telemetry/opentelemetry-python#2407).
- **Exemplars contain measurement timestamp**: `Exemplar.time_unix_nano`
is populated from measurement timestamps. PR:
[#4094](open-telemetry/opentelemetry-python#4094).
Issue:
[open-telemetry#2407](open-telemetry/opentelemetry-python#2407).
- **ExemplarReservoir extension point and offer method**:
`ExemplarReservoir` defines `offer(value, time_unix_nano, attributes,
context)` and `collect()`. PR:
[#4094](open-telemetry/opentelemetry-python#4094).
Issue:
[open-telemetry#2407](open-telemetry/opentelemetry-python#2407).
- **SimpleFixedSizeExemplarReservoir default**: the SDK exposes the
reservoir and uses it for non-explicit-bucket aggregations. PR:
[#4094](open-telemetry/opentelemetry-python#4094).
Issue:
[open-telemetry#2407](open-telemetry/opentelemetry-python#2407).
- **AlignedHistogramBucketExemplarReservoir default for explicit bucket
histograms**: the default factory returns the aligned histogram
reservoir for explicit bucket histograms. PR:
[#4094](open-telemetry/opentelemetry-python#4094).
Issue:
[open-telemetry#2407](open-telemetry/opentelemetry-python#2407).
- **Per-timeseries cumulative start timestamps**: aggregation state
keeps per-series start timestamps and integration tests cover alignment.
PR:
[open-telemetry#2679](open-telemetry/opentelemetry-python#2679).
Issue:
[#2677](open-telemetry/opentelemetry-python#2677).

## Logs

- **Logger.Emit(LogRecord) with Exception parameter**: logs API accepts
`exception`; SDK records exception attributes and tests cover emitted
exceptions. PR:
[open-telemetry#4908](open-telemetry/opentelemetry-python#4908).
Issue:
[open-telemetry#4907](open-telemetry/opentelemetry-python#4907).
- **LogRecord.Set EventName**: API and SDK readable records expose
`event_name`; export and serialization are tested. PR:
[open-telemetry#4652](open-telemetry/opentelemetry-python#4652).
Issue:
[open-telemetry#4644](open-telemetry/opentelemetry-python#4644).
- **OTLP File exporter**: `opentelemetry-exporter-otlp-json-file`
provides trace, metric, and log exporters with tests. PR:
[open-telemetry#5207](open-telemetry/opentelemetry-python#5207).
Issue: none found.

## Environment Variables

- **OTEL_BLRP_***: `BatchLogRecordProcessor` reads BLRP environment
settings and tests cover valid/invalid values. PR:
[open-telemetry#4535](open-telemetry/opentelemetry-python#4535).
Issue: none found.
- **OTEL_METRICS_EXEMPLAR_FILTER**: the env var is defined and selects
`always_on`, `trace_based`, or `always_off` filters in tests. PR:
[#4094](open-telemetry/opentelemetry-python#4094).
Issue:
[open-telemetry#2407](open-telemetry/opentelemetry-python#2407).

## Exporters

- **OTLP honors user-agent spec**: HTTP tests assert `User-Agent`; gRPC
tests assert `grpc.primary_user_agent`. PRs:
[open-telemetry#2959](open-telemetry/opentelemetry-python#2959),
[open-telemetry#3009](open-telemetry/opentelemetry-python#3009),
[open-telemetry#3128](open-telemetry/opentelemetry-python#3128),
[open-telemetry#4658](open-telemetry/opentelemetry-python#4658).
Issue: none found.
- **Zipkin InstrumentationScope mapping**: Zipkin encoding emits
`otel.scope.name` and `otel.scope.version` tags from
`span.instrumentation_scope`. PR:
[open-telemetry#2583](open-telemetry/opentelemetry-python#2583).
Issue: none found.

## Prometheus

- **Metadata Deduplication**: exporter groups metric families by family
ID and tests avoid duplicate HELP/TYPE declarations. PR:
[open-telemetry#4869](open-telemetry/opentelemetry-python#4869).
Issue: none found.
- **Unit Full Words**: unit mapping converts UCUM/SI units to full
Prometheus unit names with tests. PR:
[open-telemetry#3924](open-telemetry/opentelemetry-python#3924).
Issue: none found.
- **otel_scope_name and otel_scope_version labels**: scope labels are
emitted by default and tested in text output. PR:
[open-telemetry#5123](open-telemetry/opentelemetry-python#5123).
Issue:
[open-telemetry#5112](open-telemetry/opentelemetry-python#5112).
- **otel_scope_[attribute] labels**: `_build_scope_attrs()` prefixes
scope attributes with `otel_scope_`; tests cover output. PR:
[open-telemetry#5123](open-telemetry/opentelemetry-python#5123).
Issue:
[open-telemetry#5112](open-telemetry/opentelemetry-python#5112).
- **otel_scope labels can be disabled**:
`PrometheusMetricReader(scope_info_enabled=False)` omits scope labels
and is documented/tested. PR:
[open-telemetry#5123](open-telemetry/opentelemetry-python#5123).
Issue:
[open-telemetry#5112](open-telemetry/opentelemetry-python#5112).

## OpenTracing Compatibility

- **Create OpenTracing Shim**: `opentelemetry-opentracing-shim` exposes
`create_tracer`. PRs:
[#211](open-telemetry/opentelemetry-python#211),
[open-telemetry#1878](open-telemetry/opentelemetry-python#1878).
Issue:
[#1848](open-telemetry/opentelemetry-python#1848).
- **Tracer**: `TracerShim` is implemented and tested. PRs:
[#211](open-telemetry/opentelemetry-python#211),
[open-telemetry#1878](open-telemetry/opentelemetry-python#1878).
Issue:
[#1848](open-telemetry/opentelemetry-python#1848).
- **Span**: `SpanShim` is implemented and tested. PRs:
[#211](open-telemetry/opentelemetry-python#211),
[open-telemetry#1878](open-telemetry/opentelemetry-python#1878).
Issue:
[#1848](open-telemetry/opentelemetry-python#1848).
- **SpanContext**: `SpanContextShim` is implemented and tested. PRs:
[#211](open-telemetry/opentelemetry-python#211),
[open-telemetry#1878](open-telemetry/opentelemetry-python#1878).
Issue:
[#1848](open-telemetry/opentelemetry-python#1848).
- **ScopeManager**: `ScopeManagerShim` is implemented and tested. PRs:
[#211](open-telemetry/opentelemetry-python#211),
[open-telemetry#1878](open-telemetry/opentelemetry-python#1878).
Issue:
[#1848](open-telemetry/opentelemetry-python#1848).
- **Error mapping for attributes/events**: shim tests cover error and
exception mapping. PRs:
[#211](open-telemetry/opentelemetry-python#211),
[open-telemetry#1878](open-telemetry/opentelemetry-python#1878).
Issue:
[#1848](open-telemetry/opentelemetry-python#1848).
- **Migration to OpenTelemetry guide**: package docs frame the shim as
migration support. PRs:
[#211](open-telemetry/opentelemetry-python#211),
[open-telemetry#1878](open-telemetry/opentelemetry-python#1878).
Issue:
[#1848](open-telemetry/opentelemetry-python#1848).

---------

Co-authored-by: Carlos Alberto Cortez <calberto.cortez@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Add log exception support to Logger API/SDK

8 participants