diff --git a/.chloggen/exception-group-id.yaml b/.chloggen/exception-group-id.yaml
new file mode 100644
index 0000000000..707083d36f
--- /dev/null
+++ b/.chloggen/exception-group-id.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: exception
+
+# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
+note: "Add `exception.group_id` attribute for grouping structurally equivalent exceptions."
+
+# 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: [3443]
+
+# (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:
diff --git a/docs/exceptions/exceptions-logs.md b/docs/exceptions/exceptions-logs.md
index 7a00fe58e9..8fd7da5a0e 100644
--- a/docs/exceptions/exceptions-logs.md
+++ b/docs/exceptions/exceptions-logs.md
@@ -65,6 +65,7 @@ The table below indicates which attributes should be added to the
| [`exception.message`](/docs/registry/attributes/exception.md) |  | `Conditionally Required` [1] | string | The exception message. [2] | `Division by zero`; `Can't convert 'int' object to str implicitly` |
| [`exception.type`](/docs/registry/attributes/exception.md) |  | `Conditionally Required` [3] | string | The type of the exception (its fully-qualified class name, if applicable). The dynamic type of the exception should be preferred over the static type in languages that support it. | `java.net.ConnectException`; `OSError` |
| [`exception.stacktrace`](/docs/registry/attributes/exception.md) |  | `Recommended` | string | A stacktrace as a string in the natural representation for the language runtime. The representation is to be determined and documented by each language SIG. | `Exception in thread "main" java.lang.RuntimeException: Test exception\n at com.example.GenerateTrace.methodB(GenerateTrace.java:13)\n at com.example.GenerateTrace.methodA(GenerateTrace.java:9)\n at com.example.GenerateTrace.main(GenerateTrace.java:5)` |
+| [`exception.group_id`](/docs/registry/attributes/exception.md) |  | `Opt-In` | string | An identifier for the exception group, used to correlate exceptions that are considered structurally equivalent. [4] | `518d34582b6189db69b36414336c47ec6e4144f8` |
**[1] `exception.message`:** Required if `exception.type` is not set, recommended otherwise.
@@ -76,6 +77,19 @@ The table below indicates which attributes should be added to the
**[3] `exception.type`:** Required if `exception.message` is not set, recommended otherwise.
+**[4] `exception.group_id`:** Instrumentations SHOULD produce the same `exception.group_id` for
+exceptions that share the same type and stacktrace structure,
+ignoring high-cardinality parts such as the exception message.
+The exact algorithm for computing the value is left to the
+instrumentation, but it MUST ensure that structurally equivalent
+exceptions produce the same value.
+
+For Java, the `exception.group_id` SHOULD be derived from the
+exception's fully-qualified class name (`exception.type`) and the
+structural elements of the stacktrace (e.g. class names and method
+names of each frame), excluding the exception
+message.
+
diff --git a/docs/exceptions/exceptions-spans.md b/docs/exceptions/exceptions-spans.md
index 122a072941..e4acf1b7e8 100644
--- a/docs/exceptions/exceptions-spans.md
+++ b/docs/exceptions/exceptions-spans.md
@@ -56,6 +56,7 @@ This event describes a single exception.
| [`exception.type`](/docs/registry/attributes/exception.md) |  | `Conditionally Required` [3] | string | The type of the exception (its fully-qualified class name, if applicable). The dynamic type of the exception should be preferred over the static type in languages that support it. | `java.net.ConnectException`; `OSError` |
| [`exception.escaped`](/docs/registry/attributes/exception.md) | 
It's no longer recommended to record exceptions that are handled and do not escape the scope of a span. | `Recommended` | boolean | Indicates that the exception is escaping the scope of the span. | |
| [`exception.stacktrace`](/docs/registry/attributes/exception.md) |  | `Recommended` | string | A stacktrace as a string in the natural representation for the language runtime. The representation is to be determined and documented by each language SIG. | `Exception in thread "main" java.lang.RuntimeException: Test exception\n at com.example.GenerateTrace.methodB(GenerateTrace.java:13)\n at com.example.GenerateTrace.methodA(GenerateTrace.java:9)\n at com.example.GenerateTrace.main(GenerateTrace.java:5)` |
+| [`exception.group_id`](/docs/registry/attributes/exception.md) |  | `Opt-In` | string | An identifier for the exception group, used to correlate exceptions that are considered structurally equivalent. [4] | `518d34582b6189db69b36414336c47ec6e4144f8` |
**[1] `exception.message`:** Required if `exception.type` is not set, recommended otherwise.
@@ -67,6 +68,19 @@ This event describes a single exception.
**[3] `exception.type`:** Required if `exception.message` is not set, recommended otherwise.
+**[4] `exception.group_id`:** Instrumentations SHOULD produce the same `exception.group_id` for
+exceptions that share the same type and stacktrace structure,
+ignoring high-cardinality parts such as the exception message.
+The exact algorithm for computing the value is left to the
+instrumentation, but it MUST ensure that structurally equivalent
+exceptions produce the same value.
+
+For Java, the `exception.group_id` SHOULD be derived from the
+exception's fully-qualified class name (`exception.type`) and the
+structural elements of the stacktrace (e.g. class names and method
+names of each frame), excluding the exception
+message.
+
diff --git a/docs/registry/attributes/exception.md b/docs/registry/attributes/exception.md
index b6629f6240..e3421b5216 100644
--- a/docs/registry/attributes/exception.md
+++ b/docs/registry/attributes/exception.md
@@ -14,11 +14,25 @@ This document defines the shared attributes used to report a single exception as
| Key | Stability | Value Type | Description | Example Values |
| --- | --- | --- | --- | --- |
-| `exception.message` |  | string | The exception message. [1] | `Division by zero`; `Can't convert 'int' object to str implicitly` |
+| `exception.group_id` |  | string | An identifier for the exception group, used to correlate exceptions that are considered structurally equivalent. [1] | `518d34582b6189db69b36414336c47ec6e4144f8` |
+| `exception.message` |  | string | The exception message. [2] | `Division by zero`; `Can't convert 'int' object to str implicitly` |
| `exception.stacktrace` |  | string | A stacktrace as a string in the natural representation for the language runtime. The representation is to be determined and documented by each language SIG. | `Exception in thread "main" java.lang.RuntimeException: Test exception\n at com.example.GenerateTrace.methodB(GenerateTrace.java:13)\n at com.example.GenerateTrace.methodA(GenerateTrace.java:9)\n at com.example.GenerateTrace.main(GenerateTrace.java:5)` |
| `exception.type` |  | string | The type of the exception (its fully-qualified class name, if applicable). The dynamic type of the exception should be preferred over the static type in languages that support it. | `java.net.ConnectException`; `OSError` |
-**[1] `exception.message`:**
+**[1] `exception.group_id`:** Instrumentations SHOULD produce the same `exception.group_id` for
+exceptions that share the same type and stacktrace structure,
+ignoring high-cardinality parts such as the exception message.
+The exact algorithm for computing the value is left to the
+instrumentation, but it MUST ensure that structurally equivalent
+exceptions produce the same value.
+
+For Java, the `exception.group_id` SHOULD be derived from the
+exception's fully-qualified class name (`exception.type`) and the
+structural elements of the stacktrace (e.g. class names and method
+names of each frame), excluding the exception
+message.
+
+**[2] `exception.message`:**
> [!WARNING]
>
diff --git a/model/exceptions/events.yaml b/model/exceptions/events.yaml
index 10d9691ebe..dd901522a4 100644
--- a/model/exceptions/events.yaml
+++ b/model/exceptions/events.yaml
@@ -14,3 +14,5 @@ groups:
conditionally_required: Required if `exception.type` is not set, recommended otherwise.
- ref: exception.stacktrace
- ref: exception.escaped
+ - ref: exception.group_id
+ requirement_level: opt_in
diff --git a/model/exceptions/logs.yaml b/model/exceptions/logs.yaml
index c8b664c0bf..cfe1665040 100644
--- a/model/exceptions/logs.yaml
+++ b/model/exceptions/logs.yaml
@@ -12,3 +12,5 @@ groups:
requirement_level:
conditionally_required: Required if `exception.type` is not set, recommended otherwise.
- ref: exception.stacktrace
+ - ref: exception.group_id
+ requirement_level: opt_in
diff --git a/model/exceptions/registry.yaml b/model/exceptions/registry.yaml
index cebf6ce34e..31714784b0 100644
--- a/model/exceptions/registry.yaml
+++ b/model/exceptions/registry.yaml
@@ -35,3 +35,23 @@ groups:
at com.example.GenerateTrace.methodB(GenerateTrace.java:13)\n
at com.example.GenerateTrace.methodA(GenerateTrace.java:9)\n
at com.example.GenerateTrace.main(GenerateTrace.java:5)
+ - id: exception.group_id
+ type: string
+ stability: development
+ brief: >
+ An identifier for the exception group, used to correlate exceptions
+ that are considered structurally equivalent.
+ note: |
+ Instrumentations SHOULD produce the same `exception.group_id` for
+ exceptions that share the same type and stacktrace structure,
+ ignoring high-cardinality parts such as the exception message.
+ The exact algorithm for computing the value is left to the
+ instrumentation, but it MUST ensure that structurally equivalent
+ exceptions produce the same value.
+
+ For Java, the `exception.group_id` SHOULD be derived from the
+ exception's fully-qualified class name (`exception.type`) and the
+ structural elements of the stacktrace (e.g. class names and method
+ names of each frame), excluding the exception
+ message.
+ examples: ["518d34582b6189db69b36414336c47ec6e4144f8"]