From 1146d0eb0b02463c46a39f0136dc5a93606b9a1c Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Wed, 7 Jan 2026 21:39:47 +0100 Subject: [PATCH 01/28] update exception recording guidelines to not use span events --- .github/copilot-instructions.md | 2 +- docs/general/recording-errors.md | 29 ++++++++++++++++------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 5d354e73b9..192a42cfca 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -96,7 +96,7 @@ these requirements: - **Spans**: Set status to Error, populate `error.type`, set description when helpful - **Metrics**: Include `error.type` attribute for filtering and analysis -- **Exceptions**: Record as span events or log records using SDK APIs +- **Exceptions**: Record as log records - **Consistency**: Same `error.type` across spans and metrics for same operation ## Common Issues to Flag diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index 6e12b17548..aaf31941a4 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -83,14 +83,14 @@ include it if the operation succeeded. ## Recording exceptions -When an instrumented operation fails with an exception, instrumentation SHOULD record -this exception as a [span event](/docs/exceptions/exceptions-spans.md) or a [log record](/docs/exceptions/exceptions-logs.md). +When an instrumented operation throws an exception, instrumentation SHOULD +record this exception as a [log record](/docs/exceptions/exceptions-logs.md). -It's RECOMMENDED to use the `Span.recordException` API or logging library API that takes exception instance -instead of providing individual attributes. This enables the OpenTelemetry SDK to -control what information is recorded based on application configuration. +When the instrumented operation has not succeeded due to an exception, +metrics associated with this operation SHOULD include `error.type` attribute. It's NOT RECOMMENDED to record the same exception more than once. + It's NOT RECOMMENDED to record exceptions that are handled by the instrumented library. For example, in this code-snippet, `ResourceAlreadyExistsException` is handled and the corresponding @@ -100,22 +100,25 @@ to the caller should be recorded (or logged) once. ```java public boolean createIfNotExists(String resourceId) throws IOException { Span span = startSpan(); + long startTime = System.nanoTime(); try { create(resourceId); + recordMetric("acme.resource.create.duration", System.nanoTime() - startTime); return true; } catch (ResourceAlreadyExistsException e) { - // not recording exception and not setting span status to error - exception is handled - // but we can set attributes that capture additional details + // not setting span status to error - as the exception is not an error + // but we still log and set attributes that capture additional details + logger.debug(e); span.setAttribute(AttributeKey.stringKey("acme.resource.create.status"), "already_exists"); + recordMetric("acme.resource.create.duration", System.nanoTime() - startTime); return false; } catch (IOException e) { - // recording exception here (assuming it was not recorded inside `create` method) - span.recordException(e); - // or - // logger.warn(e); - - span.setAttribute(AttributeKey.stringKey("error.type"), e.getClass().getCanonicalName()) + logger.error(e); + String errorType = e.getClass().getCanonicalName(); + span.setAttribute(AttributeKey.stringKey("error.type"), errorType); span.setStatus(StatusCode.ERROR, e.getMessage()); + recordMetric("acme.resource.create.duration", System.nanoTime() - startTime, + AttributeKey.stringKey("error.type"), errorType); throw e; } } From 138771413cd2ee93197b58ae0a03e813a438c706 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Wed, 7 Jan 2026 21:56:18 +0100 Subject: [PATCH 02/28] Clarify guidance on capturing exception details in metrics and spans --- docs/general/recording-errors.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index aaf31941a4..5575e48eff 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -87,7 +87,9 @@ When an instrumented operation throws an exception, instrumentation SHOULD record this exception as a [log record](/docs/exceptions/exceptions-logs.md). When the instrumented operation has not succeeded due to an exception, -metrics associated with this operation SHOULD include `error.type` attribute. +refer to the [recording errors on spans](#recording-errors-on-spans) +and to the [recording errors on metrics](#recording-errors-on-metrics) +on capturing exception details on these signals. It's NOT RECOMMENDED to record the same exception more than once. From f9869db8f46bd998a6efd203908ebc3a295e7e8f Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Wed, 7 Jan 2026 22:09:01 +0100 Subject: [PATCH 03/28] Add changelog entry --- .chloggen/3256.yaml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .chloggen/3256.yaml diff --git a/.chloggen/3256.yaml b/.chloggen/3256.yaml new file mode 100644 index 0000000000..5f38e3d952 --- /dev/null +++ b/.chloggen/3256.yaml @@ -0,0 +1,22 @@ +# Use this changelog template to create an entry for release notes. +# +# If your change doesn't affect end users you should instead start +# your pull request title with [chore] or use the "Skip Changelog" label. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the area of concern in the attributes-registry, (e.g. http, cloud, db) +component: general + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Update exception recording guidelines to not use span events. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +# The values here must be integers. +issues: [3256] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: From 095f87602e99721b7f64f5476964ed3aa40d272b Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Wed, 7 Jan 2026 22:21:29 +0100 Subject: [PATCH 04/28] Deprecate event.exception and update documentation to use logs for recording exceptions --- docs/exceptions/exceptions-spans.md | 2 +- .../{events.yaml => deprecated/events-deprecated.yaml} | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) rename model/exceptions/{events.yaml => deprecated/events-deprecated.yaml} (85%) diff --git a/docs/exceptions/exceptions-spans.md b/docs/exceptions/exceptions-spans.md index d83b212b90..efaf92217d 100644 --- a/docs/exceptions/exceptions-spans.md +++ b/docs/exceptions/exceptions-spans.md @@ -23,7 +23,7 @@ exceptions associated with spans. -**Status:** ![Stable](https://img.shields.io/badge/-stable-lightgreen) +**Status:** ![Deprecated](https://img.shields.io/badge/-deprecated-red)
Use logs to record exceptions instead. The event name MUST be `exception`. diff --git a/model/exceptions/events.yaml b/model/exceptions/deprecated/events-deprecated.yaml similarity index 85% rename from model/exceptions/events.yaml rename to model/exceptions/deprecated/events-deprecated.yaml index 10d9691ebe..4e7c760f5d 100644 --- a/model/exceptions/events.yaml +++ b/model/exceptions/deprecated/events-deprecated.yaml @@ -2,6 +2,9 @@ groups: - id: event.exception name: exception stability: stable + deprecated: + reason: obsoleted + note: Use logs to record exceptions instead. type: event brief: > This event describes a single exception. From 5a37bb0532ffac573b517e84ba02780e3d26ba7d Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Wed, 7 Jan 2026 22:22:55 +0100 Subject: [PATCH 05/28] Update component designation to 'exceptions' in changelog entry --- .chloggen/3256.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.chloggen/3256.yaml b/.chloggen/3256.yaml index 5f38e3d952..42ff867290 100644 --- a/.chloggen/3256.yaml +++ b/.chloggen/3256.yaml @@ -7,7 +7,7 @@ change_type: enhancement # The name of the area of concern in the attributes-registry, (e.g. http, cloud, db) -component: general +component: exceptions # A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). note: Update exception recording guidelines to not use span events. From 353b6603684156dda799e4c545bb9bdafdc9c487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Paj=C4=85k?= Date: Wed, 7 Jan 2026 23:15:08 +0100 Subject: [PATCH 06/28] Update .chloggen/3256.yaml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .chloggen/3256.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.chloggen/3256.yaml b/.chloggen/3256.yaml index 42ff867290..cfbfdf6f58 100644 --- a/.chloggen/3256.yaml +++ b/.chloggen/3256.yaml @@ -4,7 +4,7 @@ # your pull request title with [chore] or use the "Skip Changelog" label. # One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' -change_type: enhancement +change_type: deprecation # The name of the area of concern in the attributes-registry, (e.g. http, cloud, db) component: exceptions From c16c40c1406af57d8dacb06bd27f44f898c7f2c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Paj=C4=85k?= Date: Thu, 8 Jan 2026 17:14:23 +0100 Subject: [PATCH 07/28] Apply suggestions from code review --- .github/copilot-instructions.md | 2 +- docs/general/recording-errors.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 192a42cfca..086421b541 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -96,7 +96,7 @@ these requirements: - **Spans**: Set status to Error, populate `error.type`, set description when helpful - **Metrics**: Include `error.type` attribute for filtering and analysis -- **Exceptions**: Record as log records +- **Exceptions**: Record as event records - **Consistency**: Same `error.type` across spans and metrics for same operation ## Common Issues to Flag diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index 5575e48eff..ecba2dff24 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -84,7 +84,7 @@ include it if the operation succeeded. ## Recording exceptions When an instrumented operation throws an exception, instrumentation SHOULD -record this exception as a [log record](/docs/exceptions/exceptions-logs.md). +record this exception as an [event](/docs/exceptions/exceptions-logs.md). When the instrumented operation has not succeeded due to an exception, refer to the [recording errors on spans](#recording-errors-on-spans) From 370ec7c6bf4f2e312e7eea2350a1a40334ca5d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Paj=C4=85k?= Date: Mon, 12 Jan 2026 16:47:24 +0100 Subject: [PATCH 08/28] Update docs/general/recording-errors.md Co-authored-by: Liudmila Molkova --- docs/general/recording-errors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index ecba2dff24..11738af28a 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -83,7 +83,7 @@ include it if the operation succeeded. ## Recording exceptions -When an instrumented operation throws an exception, instrumentation SHOULD +When instrumented code throws an exception, instrumentation SHOULD record this exception as an [event](/docs/exceptions/exceptions-logs.md). When the instrumented operation has not succeeded due to an exception, From a5272a5241ce19497f31935b1bcdc66039fa06ed Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 12 Jan 2026 17:08:38 +0100 Subject: [PATCH 09/28] Deprecate Semantic conventions for exceptions on spans document --- areas.yaml | 2 +- docs/exceptions/exceptions-spans.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/areas.yaml b/areas.yaml index de54794d0c..d86c3eb9b3 100644 --- a/areas.yaml +++ b/areas.yaml @@ -190,7 +190,7 @@ areas: - name: "semconv-log-approvers" github: semconv-log-approvers project: "N/A" - board: "N/A" + board: "https://github.com/orgs/open-telemetry/projects/65" labels: - area:log - area:event diff --git a/docs/exceptions/exceptions-spans.md b/docs/exceptions/exceptions-spans.md index efaf92217d..35264247ae 100644 --- a/docs/exceptions/exceptions-spans.md +++ b/docs/exceptions/exceptions-spans.md @@ -4,7 +4,8 @@ linkTitle: Spans # Semantic conventions for exceptions on spans -**Status**: [Stable][DocumentStatus] +**Status**: [Deprecated](https://img.shields.io/badge/-deprecated-red)
+See [Recording exceptions](../general/recording-errors.md#recording-exceptions) This document defines semantic conventions for recording application exceptions associated with spans. From f71de5ce4a629afc4467fded723a4bcc667855a6 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 12 Jan 2026 17:30:44 +0100 Subject: [PATCH 10/28] revert 'event' to 'log record' --- docs/general/recording-errors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index 11738af28a..a105e1156b 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -84,7 +84,7 @@ include it if the operation succeeded. ## Recording exceptions When instrumented code throws an exception, instrumentation SHOULD -record this exception as an [event](/docs/exceptions/exceptions-logs.md). +record this exception as a [log record](/docs/exceptions/exceptions-logs.md). When the instrumented operation has not succeeded due to an exception, refer to the [recording errors on spans](#recording-errors-on-spans) From e8b8f4fda63866b6d2c0015c1f06a6fa6406d8b0 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 12 Jan 2026 17:39:56 +0100 Subject: [PATCH 11/28] update recording exceptions example --- docs/general/recording-errors.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index a105e1156b..5951313bb3 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -105,20 +105,31 @@ public boolean createIfNotExists(String resourceId) throws IOException { long startTime = System.nanoTime(); try { create(resourceId); + recordMetric("acme.resource.create.duration", System.nanoTime() - startTime); + return true; } catch (ResourceAlreadyExistsException e) { // not setting span status to error - as the exception is not an error // but we still log and set attributes that capture additional details - logger.debug(e); + logger.withEventName("acme.resource.create.error") + .withAttribute("acme.resource.create.status", "already_exists") + .withException(e) + .debug() + span.setAttribute(AttributeKey.stringKey("acme.resource.create.status"), "already_exists"); + recordMetric("acme.resource.create.duration", System.nanoTime() - startTime); + return false; } catch (IOException e) { - logger.error(e); - String errorType = e.getClass().getCanonicalName(); - span.setAttribute(AttributeKey.stringKey("error.type"), errorType); + logger.withEventName("acme.resource.create.error") + .withException(e) + .error() + span.setStatus(StatusCode.ERROR, e.getMessage()); + + String errorType = e.getClass().getCanonicalName(); recordMetric("acme.resource.create.duration", System.nanoTime() - startTime, AttributeKey.stringKey("error.type"), errorType); throw e; From ac1e486842eba36dd28ea201655243050140a5f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Paj=C4=85k?= Date: Mon, 12 Jan 2026 19:31:55 +0100 Subject: [PATCH 12/28] Update recording-errors.md --- docs/general/recording-errors.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index 5951313bb3..c72cb46263 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -127,9 +127,11 @@ public boolean createIfNotExists(String resourceId) throws IOException { .withException(e) .error() + String errorType = e.getClass().getCanonicalName(); + + span.setAttribute(AttributeKey.stringKey("error.type"), errorType) span.setStatus(StatusCode.ERROR, e.getMessage()); - String errorType = e.getClass().getCanonicalName(); recordMetric("acme.resource.create.duration", System.nanoTime() - startTime, AttributeKey.stringKey("error.type"), errorType); throw e; From c9cef10ceb11737d8c05000f16170403f5cb3bb7 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Mon, 12 Jan 2026 19:37:51 +0100 Subject: [PATCH 13/28] lint --- AREAS.md | 2 +- docs/exceptions/exceptions-spans.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/AREAS.md b/AREAS.md index 0b78b5f191..e3d06ce2d9 100644 --- a/AREAS.md +++ b/AREAS.md @@ -39,7 +39,7 @@ their owners, related project (and project board) as well as its current status. | Semantic Conventions: Database | [semconv-db-approvers](https://github.com/orgs/open-telemetry/teams/semconv-db-approvers) | https://github.com/open-telemetry/community/blob/main/projects/completed-projects/database-client-semconv.md | https://github.com/orgs/open-telemetry/projects/73 | `area:db` | `inactive` | The SIG is inactive. Bugs and bugfixes are welcome. For substantial changes, follow the [new project process](https://github.com/open-telemetry/community/blob/main/project-management.md) | | Semantic Conventions: FaaS | [specs-semconv-maintainers](https://github.com/orgs/open-telemetry/teams/specs-semconv-maintainers) | https://github.com/open-telemetry/community/blob/main/projects/completed-projects/faas.md | N/A | `area:faas` | `inactive` | The SIG is inactive. Bugs and bugfixes are welcome. For substantial changes, follow the [new project process](https://github.com/open-telemetry/community/blob/main/project-management.md) | | Semantic Conventions: JVM | [semconv-jvm-approvers](https://github.com/orgs/open-telemetry/teams/semconv-jvm-approvers) | N/A | https://github.com/orgs/open-telemetry/projects/49 | `area:jvm` | `inactive` | The SIG is inactive. Bugs and bugfixes are welcome. For substantial changes, follow the [new project process](https://github.com/open-telemetry/community/blob/main/project-management.md) | -| Semantic Conventions: Logs | [semconv-log-approvers](https://github.com/orgs/open-telemetry/teams/semconv-log-approvers) | N/A | N/A | `area:log`, `area:event`, `area:exception` | `accepting_contributions`, `active` | The SIG is looking for contributions! | +| Semantic Conventions: Logs | [semconv-log-approvers](https://github.com/orgs/open-telemetry/teams/semconv-log-approvers) | N/A | https://github.com/orgs/open-telemetry/projects/65 | `area:log`, `area:event`, `area:exception` | `accepting_contributions`, `active` | The SIG is looking for contributions! | | Semantic Conventions: Mainframe | [sig-mainframe-approvers](https://github.com/orgs/open-telemetry/teams/sig-mainframe-approvers) | https://github.com/open-telemetry/community/blob/main/projects/mainframe.md | N/A | `area:mainframe`, `area:zos` | `accepting_contributions`, `active` | The SIG is looking for contributions! | | Semantic Conventions: Profiling | [profiling-approvers](https://github.com/orgs/open-telemetry/teams/profiling-approvers) | N/A | N/A | `area:profile`, `area:pprof` | `accepting_contributions`, `active` | The SIG is looking for contributions! | | Semantic Conventions: .NET | [semconv-dotnet-approver](https://github.com/orgs/open-telemetry/teams/semconv-dotnet-approver) | N/A | N/A | `area:dotnet`, `area:aspnetcore`, `area:signalr`, `area:kestrel` | `accepting_contributions`, `active` | SIG is driven by members of the .NET runtime team. Contributions are welcomed but must be aligned with the .NET runtime features/roadmap | diff --git a/docs/exceptions/exceptions-spans.md b/docs/exceptions/exceptions-spans.md index 35264247ae..d2de2917bb 100644 --- a/docs/exceptions/exceptions-spans.md +++ b/docs/exceptions/exceptions-spans.md @@ -80,4 +80,3 @@ grained information from a stacktrace, if necessary. [telemetry-sdk-resource]: ../resource/README.md#telemetry-sdk [erlang-stacktrace]: https://www.erlang.org/doc/apps/stdlib/erl_error.html#format_exception/3 [elixir-stacktrace]: https://hexdocs.pm/elixir/1.14.3/Exception.html#format/3 -[DocumentStatus]: https://opentelemetry.io/docs/specs/otel/document-status From 97b607ce5f6de00b8185557876f7285589d71b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Paj=C4=85k?= Date: Tue, 13 Jan 2026 19:07:27 +0100 Subject: [PATCH 14/28] Update .github/copilot-instructions.md --- .github/copilot-instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 086421b541..192a42cfca 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -96,7 +96,7 @@ these requirements: - **Spans**: Set status to Error, populate `error.type`, set description when helpful - **Metrics**: Include `error.type` attribute for filtering and analysis -- **Exceptions**: Record as event records +- **Exceptions**: Record as log records - **Consistency**: Same `error.type` across spans and metrics for same operation ## Common Issues to Flag From f3bec71e6a291dcd969559fd1da5fd50b00aa789 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Tue, 13 Jan 2026 19:31:08 +0100 Subject: [PATCH 15/28] Update exceptions-spans.md to reference semantic conventions for exceptions in logs --- docs/exceptions/exceptions-spans.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/exceptions/exceptions-spans.md b/docs/exceptions/exceptions-spans.md index d2de2917bb..1a93d78665 100644 --- a/docs/exceptions/exceptions-spans.md +++ b/docs/exceptions/exceptions-spans.md @@ -5,7 +5,7 @@ linkTitle: Spans # Semantic conventions for exceptions on spans **Status**: [Deprecated](https://img.shields.io/badge/-deprecated-red)
-See [Recording exceptions](../general/recording-errors.md#recording-exceptions) +Use [Semantic conventions for exceptions in logs](exceptions-logs.md) instead. This document defines semantic conventions for recording application exceptions associated with spans. From a49a36492dbcdbd8fb763af0a16cc2c384979f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Paj=C4=85k?= Date: Tue, 27 Jan 2026 17:07:05 +0100 Subject: [PATCH 16/28] Apply suggestion from @lmolkova Co-authored-by: Liudmila Molkova --- docs/general/recording-errors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index c72cb46263..94e487ad9f 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -125,7 +125,7 @@ public boolean createIfNotExists(String resourceId) throws IOException { } catch (IOException e) { logger.withEventName("acme.resource.create.error") .withException(e) - .error() + .warn() // this exception is expected to be handled by the caller and could be a transient error String errorType = e.getClass().getCanonicalName(); From ef4f629565713a81300b909aafa45270963b0b5c Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Tue, 27 Jan 2026 20:21:53 +0100 Subject: [PATCH 17/28] instrumented code -> instrumented operation --- docs/general/recording-errors.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index 94e487ad9f..a416964842 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -20,8 +20,8 @@ Individual semantic conventions are encouraged to provide additional guidance. An operation SHOULD be considered as failed if any of the following is true: -- an exception is thrown by the instrumented method (API, block of code, or another instrumented unit) -- the instrumented method returns an error in another way, for example, via an error code +- an exception is thrown by the instrumented operation (API, block of code, or another instrumented unit) +- the instrumented operation returns an error in another way, for example, via an error code Semantic conventions that define domain-specific status codes SHOULD specify which status codes should be reported as errors by a general-purpose instrumentation. @@ -83,7 +83,7 @@ include it if the operation succeeded. ## Recording exceptions -When instrumented code throws an exception, instrumentation SHOULD +When an instrumented operation throws an exception, instrumentation SHOULD record this exception as a [log record](/docs/exceptions/exceptions-logs.md). When the instrumented operation has not succeeded due to an exception, From f0d31ea12cd2e22e84efffd4571278ab28dd80a7 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Tue, 27 Jan 2026 20:24:15 +0100 Subject: [PATCH 18/28] revert deprecation of Exception event --- docs/exceptions/exceptions-spans.md | 2 +- .../{deprecated/events-deprecated.yaml => events.yaml} | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) rename model/exceptions/{deprecated/events-deprecated.yaml => events.yaml} (85%) diff --git a/docs/exceptions/exceptions-spans.md b/docs/exceptions/exceptions-spans.md index 1a93d78665..dc919086bf 100644 --- a/docs/exceptions/exceptions-spans.md +++ b/docs/exceptions/exceptions-spans.md @@ -24,7 +24,7 @@ exceptions associated with spans. -**Status:** ![Deprecated](https://img.shields.io/badge/-deprecated-red)
Use logs to record exceptions instead. +**Status:** ![Stable](https://img.shields.io/badge/-stable-lightgreen) The event name MUST be `exception`. diff --git a/model/exceptions/deprecated/events-deprecated.yaml b/model/exceptions/events.yaml similarity index 85% rename from model/exceptions/deprecated/events-deprecated.yaml rename to model/exceptions/events.yaml index 4e7c760f5d..10d9691ebe 100644 --- a/model/exceptions/deprecated/events-deprecated.yaml +++ b/model/exceptions/events.yaml @@ -2,9 +2,6 @@ groups: - id: event.exception name: exception stability: stable - deprecated: - reason: obsoleted - note: Use logs to record exceptions instead. type: event brief: > This event describes a single exception. From e28a4645ccab55aa574a7e402ae0c6e1470d1a69 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Tue, 27 Jan 2026 20:54:14 +0100 Subject: [PATCH 19/28] Clarify error recording guidelines for instrumented operations and exceptions --- docs/general/recording-errors.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index a416964842..80b38738de 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -86,18 +86,19 @@ include it if the operation succeeded. When an instrumented operation throws an exception, instrumentation SHOULD record this exception as a [log record](/docs/exceptions/exceptions-logs.md). -When the instrumented operation has not succeeded due to an exception, -refer to the [recording errors on spans](#recording-errors-on-spans) -and to the [recording errors on metrics](#recording-errors-on-metrics) +When the instrumented operation failed due to an exception: + +- instrumentation SHOULD record this exception as a [log record](/docs/exceptions/exceptions-logs.md), +- instrumentation SHOULD follow [recording errors on spans](#recording-errors-on-spans) +and [recording errors on metrics](#recording-errors-on-metrics) on capturing exception details on these signals. It's NOT RECOMMENDED to record the same exception more than once. - It's NOT RECOMMENDED to record exceptions that are handled by the instrumented library. -For example, in this code-snippet, `ResourceAlreadyExistsException` is handled and the corresponding -native instrumentation should not record it. Exceptions which are propagated -to the caller should be recorded (or logged) once. +For example, in this code-snippet, `ResourceAlreadyExistsException` is handled +and the corresponding native instrumentation should not record it. +Exceptions which are propagated to the caller should be recorded (or logged) at most once. ```java public boolean createIfNotExists(String resourceId) throws IOException { From 34508f8cfbb971bb0eb2fdbd461a5c258082893a Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Tue, 27 Jan 2026 20:57:13 +0100 Subject: [PATCH 20/28] Remove redundant instruction for recording exceptions in the documentation --- docs/general/recording-errors.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index 80b38738de..e615137980 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -83,9 +83,6 @@ include it if the operation succeeded. ## Recording exceptions -When an instrumented operation throws an exception, instrumentation SHOULD -record this exception as a [log record](/docs/exceptions/exceptions-logs.md). - When the instrumented operation failed due to an exception: - instrumentation SHOULD record this exception as a [log record](/docs/exceptions/exceptions-logs.md), From 7519ccd13c8601115653c7e9c0b4b77ef4a77529 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Tue, 27 Jan 2026 20:58:42 +0100 Subject: [PATCH 21/28] revert guidance on recording handled exceptions in documentation --- docs/general/recording-errors.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index e615137980..9b26536fd1 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -93,9 +93,9 @@ on capturing exception details on these signals. It's NOT RECOMMENDED to record the same exception more than once. It's NOT RECOMMENDED to record exceptions that are handled by the instrumented library. -For example, in this code-snippet, `ResourceAlreadyExistsException` is handled -and the corresponding native instrumentation should not record it. -Exceptions which are propagated to the caller should be recorded (or logged) at most once. +For example, in this code-snippet, `ResourceAlreadyExistsException` is handled and the corresponding +native instrumentation should not record it. Exceptions which are propagated +to the caller should be recorded (or logged) once. ```java public boolean createIfNotExists(String resourceId) throws IOException { From 4848c59105ef23329e0a2f6eec0eefd10f49e5bc Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Tue, 27 Jan 2026 21:00:51 +0100 Subject: [PATCH 22/28] Update status link for deprecated exceptions spans documentation --- docs/exceptions/exceptions-spans.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/exceptions/exceptions-spans.md b/docs/exceptions/exceptions-spans.md index dc919086bf..e2eadbd87d 100644 --- a/docs/exceptions/exceptions-spans.md +++ b/docs/exceptions/exceptions-spans.md @@ -4,7 +4,7 @@ linkTitle: Spans # Semantic conventions for exceptions on spans -**Status**: [Deprecated](https://img.shields.io/badge/-deprecated-red)
+**Status**: [Deprecated][DocumentStatus]
Use [Semantic conventions for exceptions in logs](exceptions-logs.md) instead. This document defines semantic conventions for recording application @@ -80,3 +80,4 @@ grained information from a stacktrace, if necessary. [telemetry-sdk-resource]: ../resource/README.md#telemetry-sdk [erlang-stacktrace]: https://www.erlang.org/doc/apps/stdlib/erl_error.html#format_exception/3 [elixir-stacktrace]: https://hexdocs.pm/elixir/1.14.3/Exception.html#format/3 +[DocumentStatus]: https://opentelemetry.io/docs/specs/otel/document-status From ec62ed5180abf9b4fe4993d6b6dd44b26247fe9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Paj=C4=85k?= Date: Tue, 27 Jan 2026 21:05:59 +0100 Subject: [PATCH 23/28] Update deprecation note in changelog --- .chloggen/3256.yaml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.chloggen/3256.yaml b/.chloggen/3256.yaml index cfbfdf6f58..04de72b336 100644 --- a/.chloggen/3256.yaml +++ b/.chloggen/3256.yaml @@ -1,22 +1,4 @@ -# Use this changelog template to create an entry for release notes. -# -# If your change doesn't affect end users you should instead start -# your pull request title with [chore] or use the "Skip Changelog" label. - -# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' change_type: deprecation - -# The name of the area of concern in the attributes-registry, (e.g. http, cloud, db) component: exceptions - -# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). note: Update exception recording guidelines to not use span events. - -# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. -# The values here must be integers. issues: [3256] - -# (Optional) One or more lines of additional information to render under the primary note. -# These lines will be padded with 2 spaces and then inserted directly into the document. -# Use pipe (|) for multiline entries. -subtext: From 3e0fbbd4c108ebe59f05ace6ee1d877f6fe79758 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Tue, 27 Jan 2026 21:24:49 +0100 Subject: [PATCH 24/28] add semicolons --- docs/general/recording-errors.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index 9b26536fd1..f79c2508b2 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -113,7 +113,7 @@ public boolean createIfNotExists(String resourceId) throws IOException { logger.withEventName("acme.resource.create.error") .withAttribute("acme.resource.create.status", "already_exists") .withException(e) - .debug() + .debug(); span.setAttribute(AttributeKey.stringKey("acme.resource.create.status"), "already_exists"); @@ -123,11 +123,11 @@ public boolean createIfNotExists(String resourceId) throws IOException { } catch (IOException e) { logger.withEventName("acme.resource.create.error") .withException(e) - .warn() // this exception is expected to be handled by the caller and could be a transient error + .warn(); // this exception is expected to be handled by the caller and could be a transient error String errorType = e.getClass().getCanonicalName(); - span.setAttribute(AttributeKey.stringKey("error.type"), errorType) + span.setAttribute(AttributeKey.stringKey("error.type"), errorType); span.setStatus(StatusCode.ERROR, e.getMessage()); recordMetric("acme.resource.create.duration", System.nanoTime() - startTime, From f9e3275a149407cddef316cb80c126818aa263f2 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Tue, 27 Jan 2026 21:25:34 +0100 Subject: [PATCH 25/28] format --- docs/general/recording-errors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index f79c2508b2..0d0b9b9dab 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -87,8 +87,8 @@ When the instrumented operation failed due to an exception: - instrumentation SHOULD record this exception as a [log record](/docs/exceptions/exceptions-logs.md), - instrumentation SHOULD follow [recording errors on spans](#recording-errors-on-spans) -and [recording errors on metrics](#recording-errors-on-metrics) -on capturing exception details on these signals. + and [recording errors on metrics](#recording-errors-on-metrics) + on capturing exception details on these signals. It's NOT RECOMMENDED to record the same exception more than once. It's NOT RECOMMENDED to record exceptions that are handled by the instrumented library. From fec7b72f0c2a31df30345a036e6915cc342e6324 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Tue, 27 Jan 2026 21:29:59 +0100 Subject: [PATCH 26/28] Clarify comments regarding error handling in createIfNotExists method --- docs/general/recording-errors.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index 0d0b9b9dab..35696370b3 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -108,7 +108,8 @@ public boolean createIfNotExists(String resourceId) throws IOException { return true; } catch (ResourceAlreadyExistsException e) { - // not setting span status to error - as the exception is not an error + // not setting span status to error - as the exception is not an error, + // thus we do not set the "error.type" attribute, // but we still log and set attributes that capture additional details logger.withEventName("acme.resource.create.error") .withAttribute("acme.resource.create.status", "already_exists") @@ -121,9 +122,10 @@ public boolean createIfNotExists(String resourceId) throws IOException { return false; } catch (IOException e) { + // this exception is expected to be handled by the caller and could be a transient error logger.withEventName("acme.resource.create.error") .withException(e) - .warn(); // this exception is expected to be handled by the caller and could be a transient error + .warn(); String errorType = e.getClass().getCanonicalName(); From 14f8c7b22fc81d7c49169fd9732cd559201c1689 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Tue, 27 Jan 2026 21:30:51 +0100 Subject: [PATCH 27/28] Clarify comments on span status handling for ResourceAlreadyExistsException --- docs/general/recording-errors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index 35696370b3..c2d0696bad 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -108,8 +108,8 @@ public boolean createIfNotExists(String resourceId) throws IOException { return true; } catch (ResourceAlreadyExistsException e) { - // not setting span status to error - as the exception is not an error, - // thus we do not set the "error.type" attribute, + // we do not set span status to error and the "error.type" attribute + // as the exception is not an error, // but we still log and set attributes that capture additional details logger.withEventName("acme.resource.create.error") .withAttribute("acme.resource.create.status", "already_exists") From ee0ba3ed858c67c0bb65f4283127011d7f0f5d00 Mon Sep 17 00:00:00 2001 From: Robert Pajak Date: Tue, 27 Jan 2026 21:31:14 +0100 Subject: [PATCH 28/28] format --- docs/general/recording-errors.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/general/recording-errors.md b/docs/general/recording-errors.md index c2d0696bad..e42423182a 100644 --- a/docs/general/recording-errors.md +++ b/docs/general/recording-errors.md @@ -122,7 +122,8 @@ public boolean createIfNotExists(String resourceId) throws IOException { return false; } catch (IOException e) { - // this exception is expected to be handled by the caller and could be a transient error + // this exception is expected to be handled by the caller + // and could be a transient error logger.withEventName("acme.resource.create.error") .withException(e) .warn();