From d3ea8fb5e7a6a02d6cbce04504220d8596af5fa7 Mon Sep 17 00:00:00 2001 From: Pascal Senn Date: Sat, 7 Mar 2026 04:09:49 +0100 Subject: [PATCH 01/10] Adds GraphQL Semantic Convention from Otel Workgroup --- docs/graphql/README.md | 8 +- docs/graphql/graphql-events.md | 105 +++ docs/graphql/graphql-metrics.md | 652 +++++++++++++++ docs/graphql/graphql-spans.md | 1160 ++++++++++++++++++++++++++- docs/registry/attributes/graphql.md | 188 ++++- model/graphql/events.yaml | 96 +++ model/graphql/metrics.yaml | 345 ++++++++ model/graphql/registry.yaml | 433 +++++++++- model/graphql/spans.yaml | 659 +++++++++++++++ model/graphql/spans.yml | 27 - 10 files changed, 3609 insertions(+), 64 deletions(-) create mode 100644 docs/graphql/graphql-events.md create mode 100644 docs/graphql/graphql-metrics.md create mode 100644 model/graphql/events.yaml create mode 100644 model/graphql/metrics.yaml create mode 100644 model/graphql/spans.yaml delete mode 100644 model/graphql/spans.yml diff --git a/docs/graphql/README.md b/docs/graphql/README.md index 0801f5f479..4f44d0c130 100644 --- a/docs/graphql/README.md +++ b/docs/graphql/README.md @@ -6,10 +6,12 @@ linkTitle: GraphQL **Status**: [Development][DocumentStatus] -This document defines semantic conventions for GraphQL. +This document defines semantic conventions for GraphQL spans, metrics, and events. -Semantic conventions are defined for the following signals: +Semantic conventions for GraphQL are defined for the following signals: -* [Spans](graphql-spans.md) +* [GraphQL Spans](graphql-spans.md): Semantic Conventions for GraphQL client and server *spans*. +* [GraphQL Metrics](graphql-metrics.md): Semantic Conventions for GraphQL client and server *metrics*. +* [GraphQL Events](graphql-events.md): Semantic Conventions for GraphQL error *events*. [DocumentStatus]: https://opentelemetry.io/docs/specs/otel/document-status diff --git a/docs/graphql/graphql-events.md b/docs/graphql/graphql-events.md new file mode 100644 index 0000000000..1fc3c1d878 --- /dev/null +++ b/docs/graphql/graphql-events.md @@ -0,0 +1,105 @@ + + +# Semantic conventions for GraphQL events + +**Status**: [Development][DocumentStatus] + +This document defines semantic conventions for GraphQL error events. + + + +- [GraphQL error event](#graphql-error-event) + + + +## GraphQL error event + + + + + + +**Status:** ![Development](https://img.shields.io/badge/-development-blue) + +The event name MUST be `graphql.error`. + +This event describes a GraphQL error that occurred during operation execution. + +GraphQL errors can occur during various phases of request processing including parsing, validation, and execution. Errors should be recorded on the root span for simplicity and scalability. Multiple errors can be recorded as separate events on the same span. +Instrumentations SHOULD set the severity based on the impact of the error. When the response contains both `data` and `errors` (partial success), the severity SHOULD be WARN (severity number 13). When the response contains only `errors` and no `data` (complete failure), the severity SHOULD be ERROR (severity number 17). When severity cannot be determined, instrumentations SHOULD default to ERROR. +**GraphQL errors and exceptions:** When a GraphQL error is caused by an underlying exception, instrumentations SHOULD record both the GraphQL error details and the exception information on the same `graphql.error` event rather than emitting separate events. The exception attributes (`exception.type`, `exception.message`, `exception.stacktrace`) MAY be included alongside the `graphql.error.*` attributes on the same event. This avoids duplicate events for the same underlying issue and keeps the error context together. +When a GraphQL error is NOT caused by an exception (e.g., validation errors, authorization errors returned by the GraphQL layer), only the `graphql.error.*` attributes are needed. +Instrumentations SHOULD cap the number of error events per span to a configurable maximum (default: 10) to prevent excessive telemetry. When capped, the total error count SHOULD still be recorded via `graphql.error.count` on the parent span. +**Relationship to span status and `error.type`:** GraphQL error events provide detailed per-error information from the response `errors` array. They are complementary to span-level error signals: + +- `error.type` on the parent span provides a low-cardinality error classification + for the overall operation outcome. It SHOULD be set based on the `graphql.error.code` + of the first error or a general error category. +- Individual `graphql.error` events provide the structured details for each error + in the response. + +Instrumentations SHOULD set both `error.type` on the span and emit error events for each error in the response. The error events provide the detail; `error.type` and span status provide the summary. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`graphql.error.message`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The error message intended for the developer as a guide to understand and correct the error. [1] | `Cannot query field 'nonExistentField' on type 'User'`; `Variable '$id' of required type 'ID!' was not provided.` | +| [`exception.message`](/docs/registry/attributes/exception.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the error was caused by an exception. | string | The exception message. [2] | `Division by zero`; `Can't convert 'int' object to str implicitly` | +| [`exception.type`](/docs/registry/attributes/exception.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the error was caused by an exception. | 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. [3] | `java.net.ConnectException`; `OSError` | +| [`graphql.error.locations`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [4] | any | The locations in the GraphQL document associated with the error. [5] | `[{ "line": 3, "column": 7 }, { "line": 5, "column": 4 }]` | +| [`graphql.error.path`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [6] | string | The path of the response field which experienced the error. [7] | `user.friends[0].name`; `findBookById` | +| [`graphql.error.schema_coordinate`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [8] | string | The coordinate of the field in the schema which experienced the error. [9] | `User.friends`; `Query.me` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available and not empty. | string | The name of the operation being executed. [10] | `FindBookById`; `GetUserProfile` | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The type of the operation being executed. [11] | `query`; `mutation`; `subscription` | +| [`exception.stacktrace`](/docs/registry/attributes/exception.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | 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. [12] | `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)` | +| [`graphql.error.code`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | An optional error code from the extensions field. [13] | `GRAPHQL_VALIDATION_FAILED`; `UNAUTHENTICATED`; `HC00116` | + +**[1] `graphql.error.message`:** Every error must contain an entry with the key message with a string description of the error intended for the developer as a guide to understand and correct the error. +> **Warning** > This attribute has unbounded cardinality and MUST NOT be used as a metric > dimension. It is intended for span events and log records only. + +**[2] `exception.message`:** The exception message. When both `graphql.error.message` and `exception.message` are available, both SHOULD be set as they may differ (the GraphQL error message is user-facing, while the exception message is the raw internal error). + +**[3] `exception.type`:** The fully qualified class name or type of the exception that caused this GraphQL error, when applicable. + +**[4] `graphql.error.locations`:** Should be included when the error can be associated with a specific location in the GraphQL document. + +**[5] `graphql.error.locations`:** If an error can be associated to a particular point in the requested GraphQL document, it should contain an array of location objects. Each location is a JSON object with the keys `line` and `column`, both positive integers starting from 1, which describe the beginning of an associated syntax element. +The value MUST be an array of objects, where each object has the following properties: - `line` (integer, required): The line number in the GraphQL document. - `column` (integer, required): The column number in the GraphQL document. + +**[6] `graphql.error.path`:** Must be included when the error can be associated with a particular field in the GraphQL result. + +**[7] `graphql.error.path`:** If an error can be associated to a particular field in the GraphQL result, it must contain an entry with the key path that details the path of the response field which experienced the error. +This allows clients to identify whether a null result is intentional or caused by a runtime error. +The path starts from the root of the response. Field names are separated by dots and list indices are represented using bracket notation. If the error happens in an aliased field, the path should use the aliased name, since it represents a path in the response, not in the request. + +**[8] `graphql.error.schema_coordinate`:** Should be included when the error can be associated with a particular field in the GraphQL schema. + +**[9] `graphql.error.schema_coordinate`:** If an error can be associated to a particular field in the GraphQL schema, it must contain a schema coordinate in the form of `typeName.fieldName` identifying the field which experienced the error. + +**[10] `graphql.operation.name`:** Including the operation name on error events enables correlation of errors to specific operations, especially useful when error events are processed independently of spans (e.g., in log-based pipelines). + +**[11] `graphql.operation.type`:** Including the operation type on error events enables correlation and filtering of errors by operation type without requiring access to the parent span. + +**[12] `exception.stacktrace`:** The exception stacktrace, when available and the error was caused by an exception. This attribute is opt-in due to its size and potential to contain sensitive information. + +**[13] `graphql.error.code`:** This is an optional field that can be used to categorize errors. The error extension code is a recommended way to categorize errors for easier filtering and monitoring. + +--- + +`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +[DocumentStatus]: https://opentelemetry.io/docs/specs/otel/document-status diff --git a/docs/graphql/graphql-metrics.md b/docs/graphql/graphql-metrics.md new file mode 100644 index 0000000000..3053ec0b2a --- /dev/null +++ b/docs/graphql/graphql-metrics.md @@ -0,0 +1,652 @@ + + +# Semantic conventions for GraphQL metrics + +**Status**: [Development][DocumentStatus] + +This document defines semantic conventions for GraphQL client and server metrics. + + + +- [GraphQL server](#graphql-server) + - [Metric: `graphql.server.request.duration`](#metric-graphqlserverrequestduration) + - [Metric: `graphql.server.active_requests`](#metric-graphqlserveractive_requests) + - [Metric: `graphql.server.request.parse.duration`](#metric-graphqlserverrequestparseduration) + - [Metric: `graphql.server.request.validate.duration`](#metric-graphqlserverrequestvalidateduration) + - [Metric: `graphql.server.request.execute.duration`](#metric-graphqlserverrequestexecuteduration) + - [Metric: `graphql.server.request.plan.duration`](#metric-graphqlserverrequestplanduration) + - [Metric: `graphql.server.response.error_count`](#metric-graphqlserverresponseerror_count) + - [Metric: `graphql.server.subscription.active`](#metric-graphqlserversubscriptionactive) + - [Metric: `graphql.server.subscription.event.duration`](#metric-graphqlserversubscriptioneventduration) +- [GraphQL client](#graphql-client) + - [Metric: `graphql.client.request.duration`](#metric-graphqlclientrequestduration) + + + +## GraphQL server + +### Metric: `graphql.server.request.duration` + + + + + + +| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | +| -------- | --------------- | ----------- | -------------- | --------- | ------ | +| `graphql.server.request.duration` | Histogram | `s` | Duration of GraphQL server requests. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | + +**[1]:** This metric measures the end-to-end duration of processing a GraphQL +request on the server. It starts when the server begins processing +the GraphQL operation and ends when the response is complete. + +When this metric is reported alongside a GraphQL server span, the +metric value SHOULD be the same as the GraphQL server span duration. + +This metric is complementary to `http.server.request.duration` which +measures the transport-level duration. The GraphQL metric captures +application-level processing time. + +Histogram bucket boundaries SHOULD be chosen to capture expected +request durations. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the GraphQL request ended with an error. | string | Describes a class of error the operation ended with. [1] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The document identifier for trusted documents. [2] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [3] | `FindBookById`; `GetUserProfile` | + +**[1] `error.type`:** For GraphQL, `error.type` captures the category of error that caused +the request to fail. Since GraphQL can return partial data with errors +in a 200 OK response, this attribute SHOULD be set when the response +contains errors that the instrumentation considers a failure. + +The `error.type` value SHOULD be predictable and SHOULD have low +cardinality. Instrumentations SHOULD document the list of errors +they report. + +**[2] `graphql.document.id`:** The `graphql.document.id` MUST only be enabled when the set of +document identifiers has bounded cardinality, such as when using +trusted documents or persisted operations. + +**[3] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have +unbounded cardinality. It MUST only be enabled when the operation +namespace has bounded cardinality, such as when using persisted +operations or trusted documents. + +When `graphql.operation.name` is not enabled, `graphql.document.hash` +or `graphql.document.id` MAY be used as lower-cardinality alternatives. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + +--- + +`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### Metric: `graphql.server.active_requests` + + + + + + +| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | +| -------- | --------------- | ----------- | -------------- | --------- | ------ | +| `graphql.server.active_requests` | UpDownCounter | `{request}` | Number of active GraphQL server requests. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | + +**[1]:** This metric tracks the number of GraphQL requests currently being +processed by the server. It is incremented when processing begins +and decremented when processing is complete. + +Since `graphql.operation.type` is not available before parsing, +instrumentations SHOULD delay the increment until after parsing completes +and the operation type is known. This means requests that fail during +parsing will not be reflected in this metric, but it ensures consistent +attribute sets on increment and decrement. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | + +--- + +`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### Metric: `graphql.server.request.parse.duration` + + + + + + +| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | +| -------- | --------------- | ----------- | -------------- | --------- | ------ | +| `graphql.server.request.parse.duration` | Histogram | `s` | Duration of GraphQL document parsing. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | + +**[1]:** This metric measures the time spent parsing the GraphQL document +string into an AST. When reported alongside a parse span, the +metric value SHOULD match the span duration. + +Histogram bucket boundaries SHOULD be chosen to capture typical +parsing/validation times. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [1] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [2] | `FindBookById`; `GetUserProfile` | + +**[1] `error.type`:** For phase-level metrics, `error.type` captures the category of error +that caused the phase to fail. This allows monitoring error rates +per phase independently of the overall request outcome. + +**[2] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have +unbounded cardinality. It MUST only be enabled when the operation +namespace has bounded cardinality, such as when using persisted +operations or trusted documents. + +When `graphql.operation.name` is not enabled, `graphql.document.hash` +or `graphql.document.id` MAY be used as lower-cardinality alternatives. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + +--- + +`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### Metric: `graphql.server.request.validate.duration` + + + + + + +| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | +| -------- | --------------- | ----------- | -------------- | --------- | ------ | +| `graphql.server.request.validate.duration` | Histogram | `s` | Duration of GraphQL document validation. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | + +**[1]:** This metric measures the time spent validating the GraphQL document +AST against the schema. When reported alongside a validation span, +the metric value SHOULD match the span duration. + +Histogram bucket boundaries SHOULD be chosen to capture typical +parsing/validation times. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [1] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [2] | `FindBookById`; `GetUserProfile` | + +**[1] `error.type`:** For phase-level metrics, `error.type` captures the category of error +that caused the phase to fail. This allows monitoring error rates +per phase independently of the overall request outcome. + +**[2] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have +unbounded cardinality. It MUST only be enabled when the operation +namespace has bounded cardinality, such as when using persisted +operations or trusted documents. + +When `graphql.operation.name` is not enabled, `graphql.document.hash` +or `graphql.document.id` MAY be used as lower-cardinality alternatives. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + +--- + +`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### Metric: `graphql.server.request.execute.duration` + + + + + + +| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | +| -------- | --------------- | ----------- | -------------- | --------- | ------ | +| `graphql.server.request.execute.duration` | Histogram | `s` | Duration of GraphQL operation execution. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | + +**[1]:** This metric measures the time spent executing the GraphQL operation, +including field resolution and result formatting. When reported +alongside an execution span, the metric value SHOULD match the span +duration. + +Histogram bucket boundaries SHOULD be chosen to capture expected +execution durations. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [1] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [2] | `FindBookById`; `GetUserProfile` | + +**[1] `error.type`:** For phase-level metrics, `error.type` captures the category of error +that caused the phase to fail. This allows monitoring error rates +per phase independently of the overall request outcome. + +**[2] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have +unbounded cardinality. It MUST only be enabled when the operation +namespace has bounded cardinality, such as when using persisted +operations or trusted documents. + +When `graphql.operation.name` is not enabled, `graphql.document.hash` +or `graphql.document.id` MAY be used as lower-cardinality alternatives. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + +--- + +`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### Metric: `graphql.server.request.plan.duration` + + + + + + +| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | +| -------- | --------------- | ----------- | -------------- | --------- | ------ | +| `graphql.server.request.plan.duration` | Histogram | `s` | Duration of GraphQL operation planning. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | + +**[1]:** This metric measures the time spent planning the execution of a +GraphQL operation. This is relevant for federated GraphQL systems +and advanced servers that perform query optimization. + +This metric is OPTIONAL - it only applies to servers that have +a distinct planning phase. + +Histogram bucket boundaries SHOULD be chosen to capture typical +planning times. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [1] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [2] | `FindBookById`; `GetUserProfile` | + +**[1] `error.type`:** For phase-level metrics, `error.type` captures the category of error +that caused the phase to fail. This allows monitoring error rates +per phase independently of the overall request outcome. + +**[2] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have +unbounded cardinality. It MUST only be enabled when the operation +namespace has bounded cardinality, such as when using persisted +operations or trusted documents. + +When `graphql.operation.name` is not enabled, `graphql.document.hash` +or `graphql.document.id` MAY be used as lower-cardinality alternatives. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + +--- + +`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### Metric: `graphql.server.response.error_count` + + + + + + +| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | +| -------- | --------------- | ----------- | -------------- | --------- | ------ | +| `graphql.server.response.error_count` | Histogram | `{error}` | Number of errors in a GraphQL response. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | + +**[1]:** This metric records the number of errors included in the GraphQL +response `errors` array. A value of 0 indicates a successful +response with no errors. + +This is a histogram (not a counter) because it records the error +count per response, enabling analysis of error distribution across +requests. + +Histogram bucket boundaries for error counts. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [1] | `FindBookById`; `GetUserProfile` | + +**[1] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. + +--- + +`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### Metric: `graphql.server.subscription.active` + + + + + + +| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | +| -------- | --------------- | ----------- | -------------- | --------- | ------ | +| `graphql.server.subscription.active` | UpDownCounter | `{subscription}` | Number of active GraphQL subscriptions. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | + +**[1]:** This metric tracks the number of GraphQL subscriptions currently active +on the server. It is incremented when a subscription is created and +decremented when a subscription is terminated (either by client +disconnect or server-side cancellation). + +This is distinct from `graphql.server.active_requests`, which tracks +short-lived request processing (parse → execute → respond). For +subscriptions, `active_requests` only covers the initial setup request, +while this metric tracks the long-lived subscription connection that +may remain open for hours or days delivering events. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The hash of the operation document. [1] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The document identifier for trusted documents. [2] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [3] | `FindBookById`; `GetUserProfile` | + +**[1] `graphql.document.hash`:** The `graphql.document.hash` MUST only be enabled when the set of +document hashes has bounded cardinality, such as when using +persisted operations or trusted documents. + +**[2] `graphql.document.id`:** The `graphql.document.id` MUST only be enabled when the set of +document identifiers has bounded cardinality, such as when using +trusted documents or persisted operations. + +**[3] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have +unbounded cardinality. It MUST only be enabled when the operation +namespace has bounded cardinality, such as when using persisted +operations or trusted documents. + + + + + +### Metric: `graphql.server.subscription.event.duration` + + + + + + +| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | +| -------- | --------------- | ----------- | -------------- | --------- | ------ | +| `graphql.server.subscription.event.duration` | Histogram | `s` | Duration of processing a single subscription event. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | + +**[1]:** This metric measures the time spent processing an individual subscription +event, from when the event is received from the source to when the +response is sent to the subscriber. + +Histogram bucket boundaries SHOULD be chosen to capture expected +subscription event processing times. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` [1] | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The hash of the operation document. [3] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The document identifier for trusted documents. [4] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [5] | `FindBookById`; `GetUserProfile` | + +**[1] `error.type`:** If the subscription event processing ended with an error. + +**[2] `error.type`:** The `error.type` SHOULD be predictable, and SHOULD have low cardinality. + +When `error.type` is set to a type (e.g., an exception type), its +canonical class name identifying the type within the artifact SHOULD be used. + +Instrumentations SHOULD document the list of errors they report. + +The cardinality of `error.type` within one instrumentation library SHOULD be low. +Telemetry consumers that aggregate data from multiple instrumentation libraries and applications +should be prepared for `error.type` to have high cardinality at query time when no +additional filters are applied. + +If the operation has completed successfully, instrumentations SHOULD NOT set `error.type`. + +If a specific domain defines its own set of error identifiers (such as HTTP or RPC status codes), +it's RECOMMENDED to: + +- Use a domain-specific attribute +- Set `error.type` to capture all errors, regardless of whether they are defined within the domain-specific set or not. + +**[3] `graphql.document.hash`:** The `graphql.document.hash` MUST only be enabled when the set of +document hashes has bounded cardinality, such as when using +persisted operations or trusted documents. + +**[4] `graphql.document.id`:** The `graphql.document.id` MUST only be enabled when the set of +document identifiers has bounded cardinality, such as when using +trusted documents or persisted operations. + +**[5] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have +unbounded cardinality. It MUST only be enabled when the operation +namespace has bounded cardinality, such as when using persisted +operations or trusted documents. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + + + + + +## GraphQL client + +### Metric: `graphql.client.request.duration` + + + + + + +| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | +| -------- | --------------- | ----------- | -------------- | --------- | ------ | +| `graphql.client.request.duration` | Histogram | `s` | Duration of GraphQL client requests. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | + +**[1]:** This metric measures the duration of outgoing GraphQL client requests, +from when the request is initiated to when the response is fully received. + +This metric is complementary to `http.client.request.duration` which +measures the transport-level duration. + +Histogram bucket boundaries SHOULD be chosen to capture expected +request durations. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the GraphQL request ended with an error. | string | Describes a class of error the operation ended with. [1] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| [`server.port`](/docs/registry/attributes/server.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If not the default port for the scheme. | int | Server port number. [2] | `80`; `8080`; `443` | +| [`server.address`](/docs/registry/attributes/server.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Recommended` | string | Server domain name if available without reverse DNS lookup; otherwise, IP address or Unix domain socket name. [3] | `example.com`; `10.1.2.80`; `/tmp/my.sock` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The document identifier for trusted documents. [4] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [5] | `FindBookById`; `GetUserProfile` | + +**[1] `error.type`:** The `error.type` SHOULD be predictable, and SHOULD have low cardinality. + +When `error.type` is set to a type (e.g., an exception type), its +canonical class name identifying the type within the artifact SHOULD be used. + +Instrumentations SHOULD document the list of errors they report. + +The cardinality of `error.type` within one instrumentation library SHOULD be low. +Telemetry consumers that aggregate data from multiple instrumentation libraries and applications +should be prepared for `error.type` to have high cardinality at query time when no +additional filters are applied. + +If the operation has completed successfully, instrumentations SHOULD NOT set `error.type`. + +If a specific domain defines its own set of error identifiers (such as HTTP or RPC status codes), +it's RECOMMENDED to: + +- Use a domain-specific attribute +- Set `error.type` to capture all errors, regardless of whether they are defined within the domain-specific set or not. + +**[2] `server.port`:** When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD represent the server port behind any intermediaries, for example proxies, if it's available. + +**[3] `server.address`:** When observed from the client side, and when communicating through an intermediary, `server.address` SHOULD represent the server address behind any intermediaries, for example proxies, if it's available. + +**[4] `graphql.document.id`:** The `graphql.document.id` MUST only be enabled when the set of +document identifiers has bounded cardinality, such as when using +trusted documents or persisted operations. + +**[5] `graphql.operation.name`:** The `graphql.operation.name` can have unbounded cardinality. +It MUST only be enabled when the operation namespace has bounded +cardinality, such as when using persisted operations or trusted documents. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + +--- + +`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +[DocumentStatus]: https://opentelemetry.io/docs/specs/otel/document-status diff --git a/docs/graphql/graphql-spans.md b/docs/graphql/graphql-spans.md index 617d2a583d..e397512949 100644 --- a/docs/graphql/graphql-spans.md +++ b/docs/graphql/graphql-spans.md @@ -1,11 +1,36 @@ -# Semantic conventions for GraphQL server spans +# Semantic conventions for GraphQL spans **Status**: [Development][DocumentStatus] +This document defines semantic conventions for GraphQL client and server spans. + + + +- [GraphQL server](#graphql-server) + - [GraphQL server span](#graphql-server-span) + - [GraphQL document parsing span](#graphql-document-parsing-span) + - [GraphQL document validation span](#graphql-document-validation-span) + - [GraphQL variable coercion span](#graphql-variable-coercion-span) + - [GraphQL operation planning span](#graphql-operation-planning-span) + - [GraphQL operation execution span](#graphql-operation-execution-span) + - [GraphQL step execution span](#graphql-step-execution-span) + - [GraphQL field execution span](#graphql-field-execution-span) + - [GraphQL DataLoader dispatch span](#graphql-dataloader-dispatch-span) + - [GraphQL DataLoader batch span](#graphql-dataloader-batch-span) + - [GraphQL subscription event span](#graphql-subscription-event-span) +- [GraphQL client](#graphql-client) + - [GraphQL client span](#graphql-client-span) + + + +## GraphQL server + +### GraphQL server span + @@ -13,34 +38,1134 @@ linkTitle: GraphQL server **Status:** ![Development](https://img.shields.io/badge/-development-blue) -This span represents an incoming operation on a GraphQL server implementation. +This span represents an incoming GraphQL request on a server implementation. **Span name** SHOULD be of the format `{graphql.operation.type}` provided `graphql.operation.type` is available. If `graphql.operation.type` is not available, the span SHOULD be named `GraphQL Operation`. -> [!WARNING] +For operation domains with bounded cardinality (e.g. trusted documents), instrumentations MAY provide +a configuration option to enable a more descriptive span name following +the `{graphql.operation.type} {graphql.operation.name}` format when +`graphql.operation.name` is available and the operation is successfully identified +in the document. + +> **Warning** > The `graphql.operation.name` value is provided by the client and can have high -> cardinality. Using it in the GraphQL server span name (by default) is -> NOT RECOMMENDED. +> cardinality. Using it in the GraphQL server span name is NOT RECOMMENDED for +> ad-hoc operations without careful consideration of cardinality implications. +> For trusted documents, the cardinality is bounded and using the operation +> name in the span name is more acceptable. > -> Instrumentation MAY provide a configuration option to enable a more descriptive -> span name following `{graphql.operation.type} {graphql.operation.name}` format -> when `graphql.operation.name` is available. +> Implementations MUST NOT include the operation name in the span name when +> the operation was not found or could not be identified in the document. +> This prevents potential security issues and ensures span names remain meaningful. + +For subscription operations, the server span represents the initial subscription +request and setup. Individual subscription events are represented by separate +`graphql.subscription.event` spans. Long-lived subscriptions MAY end the server +span after successful setup, with subsequent events creating their own spans. + +**Span status** guidance: + +Span status SHOULD be set to `Error` when the response contains errors. +This includes parse errors, validation errors, variable coercion errors, +request errors, total execution failure (where `data` is `null` and `errors` +is non-empty), and partial success (where both `data` and `errors` are present). +Individual GraphQL errors SHOULD still be recorded as events. + +> **Note** +> Instrumentations that have additional context about specific errors MAY +> use this context to set the span status more precisely. For example, if +> an instrumentation knows that the errors only affect optional, +> non-critical fields, it MAY choose not to set `Error` status. + +**Span status description** guidance: + +Span status descriptions are optional and SHOULD only be set when they provide +additional context not already captured by `error.type` or error events. +Recommended cases for setting a description: + +- When the error is ambiguous without additional context +- To distinguish between complete failure and partial success when both set + `Error` status (e.g., "Partial success: 2 of 5 fields had errors") +- When transport-level context is needed (e.g., "WebSocket connection closed unexpectedly") + +Do NOT repeat `error.type` or `graphql.error.message` in the status description. + +**Incremental delivery (`@defer`/`@stream`):** + +When a GraphQL operation uses `@defer` or `@stream` directives, the response +is delivered incrementally. The server span SHOULD encompass the entire response +delivery, including all deferred and streamed payloads. If the instrumentation +cannot keep the span open for the full incremental delivery, it SHOULD: + +- End the span after the initial payload and document this behavior +- Record deferred/streamed errors as separate events if the span + has already ended + +**Batched operations:** + +When multiple GraphQL operations are batched in a single HTTP request, +instrumentations SHOULD create one server span per operation in the batch. +The HTTP server span serves as the parent of all GraphQL operation spans. +Each operation span SHOULD have its own `graphql.operation.type` and +`graphql.operation.name` attributes. **Span kind** SHOULD be `SERVER`. -**Span status** SHOULD follow the [Recording Errors](/docs/general/recording-errors.md) document. +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the GraphQL operation ended with an error. | string | Describes a class of error the operation ended with. [1] | `GRAPHQL_PARSE_FAILED`; `GRAPHQL_VALIDATION_FAILED`; `HC0016`; `java.lang.RuntimeException` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [2] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available and not empty. | string | The name of the operation being executed. [3] | `FindBookById`; `GetUserProfile` | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If parsing succeeded | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| [`server.port`](/docs/registry/attributes/server.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` [4] | int | Server port number. [5] | `80`; `8080`; `443` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [6] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`server.address`](/docs/registry/attributes/server.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Recommended` | string | The logical server hostname or IP address serving the GraphQL endpoint. [7] | `example.com`; `10.1.2.80`; `/tmp/my.sock` | + +**[1] `error.type`:** GraphQL errors can occur during various phases of request processing. + +If the request fails before execution begins (e.g., parsing, validation, or +variable coercion fails), `error.type` SHOULD be set to a low-cardinality +error identifier. When `graphql.error.code` is available from the error's +extensions, it SHOULD be used as the `error.type` value. + +If the request completes execution but the response contains errors, +`error.type` SHOULD be set to the `graphql.error.code` from the first +error's extensions if available, or to a low-cardinality error identifier +describing the class of error (e.g., the exception type). + +If the request completes execution with partial data (both `data` and +`errors` are present), `error.type` SHOULD still be set. + +If the request completes successfully with no errors, instrumentations +SHOULD NOT set `error.type`. + +The `error.type` value SHOULD be predictable and SHOULD have low +cardinality. Instrumentations SHOULD document the list of errors they +report. + +**[2] `graphql.document.id`:** This is a hash or identifier of the document provided by the user to identify trusted documents. + +**[3] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. + +**[4] `server.port`:** If not the default port for the scheme (e.g., not 443 for HTTPS). + +**[5] `server.port`:** When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD represent the server port behind any intermediaries, for example proxies, if it's available. + +**[6] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. +When both `graphql.document.hash` and `graphql.document.id` are available, they SHOULD be preferred over transmitting the raw GraphQL document text for telemetry purposes. This reduces payload size and avoids exposing potentially sensitive operation details. + +**[7] `server.address`:** This SHOULD be the logical server address, not a proxy or load balancer address. + +The following attributes can be important for making sampling decisions +and SHOULD be provided **at span creation time** (if provided at all): + +* [`graphql.operation.type`](/docs/registry/attributes/graphql.md) + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + +--- + +`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### GraphQL document parsing span + + + + + + +**Status:** ![Development](https://img.shields.io/badge/-development-blue) + +This span represents the time spent parsing a GraphQL document. + +**Span name** SHOULD be `GraphQL Document Parsing`. + +This span covers the parsing phase of GraphQL request processing, +where the document string is parsed into an abstract syntax tree (AST). + +**Span status** SHOULD be set to `Error` when the processing phase fails. + +**Span kind** SHOULD be `INTERNAL`. **Attributes:** | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | -| [`graphql.document`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The GraphQL document being executed. [1] | `query findBookById { bookById(id: ?) { name } }` | -| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the operation being executed. | `findBookById` | -| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `parse`. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [3] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [4] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | + +**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. + +**[2] `error.type`:** If the processing phase fails, `error.type` SHOULD be set to the +`graphql.error.code` from the error's extensions if available, or to +a low-cardinality error identifier such as the exception type. + +If the processing phase completes successfully, instrumentations +SHOULD NOT set `error.type`. + +**[3] `graphql.document.id`:** This is a hash or identifier of the document provided by the user to identify trusted documents. + +**[4] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. +When both `graphql.document.hash` and `graphql.document.id` are available, they SHOULD be preferred over transmitting the raw GraphQL document text for telemetry purposes. This reduces payload size and avoids exposing potentially sensitive operation details. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + +--- + +`graphql.processing.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for processing types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_batch` | Executing a DataLoader batch operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_dispatch` | Dispatching grouped DataLoader batch operations. | ![Development](https://img.shields.io/badge/-development-blue) | +| `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | +| `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | +| `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | +| `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### GraphQL document validation span + + + + + + +**Status:** ![Development](https://img.shields.io/badge/-development-blue) + +This span represents the time spent validating a GraphQL document. + +**Span name** SHOULD be `GraphQL Document Validation`. + +This span covers the validation phase of GraphQL request processing, +where the document AST is validated against the GraphQL schema. + +**Span status** SHOULD be set to `Error` when the processing phase fails. + +**Span kind** SHOULD be `INTERNAL`. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `validate`. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [3] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [4] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | + +**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. + +**[2] `error.type`:** If the processing phase fails, `error.type` SHOULD be set to the +`graphql.error.code` from the error's extensions if available, or to +a low-cardinality error identifier such as the exception type. + +If the processing phase completes successfully, instrumentations +SHOULD NOT set `error.type`. + +**[3] `graphql.document.id`:** This is a hash or identifier of the document provided by the user to identify trusted documents. + +**[4] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. +When both `graphql.document.hash` and `graphql.document.id` are available, they SHOULD be preferred over transmitting the raw GraphQL document text for telemetry purposes. This reduces payload size and avoids exposing potentially sensitive operation details. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + +--- + +`graphql.processing.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for processing types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_batch` | Executing a DataLoader batch operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_dispatch` | Dispatching grouped DataLoader batch operations. | ![Development](https://img.shields.io/badge/-development-blue) | +| `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | +| `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | +| `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | +| `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### GraphQL variable coercion span + + + + + + +**Status:** ![Development](https://img.shields.io/badge/-development-blue) + +This span represents the time spent coercing variables for a GraphQL request. + +**Span name** SHOULD be `GraphQL Variable Coercion`. + +This span covers the variable coercion phase of GraphQL request processing, +where input variables are coerced and validated according to their types. + +**Span status** SHOULD be set to `Error` when the processing phase fails. + +**Span kind** SHOULD be `INTERNAL`. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `variable_coercion`. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [3] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [4] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the operation being executed. [5] | `FindBookById`; `GetUserProfile` | + +**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. + +**[2] `error.type`:** If the processing phase fails, `error.type` SHOULD be set to the +`graphql.error.code` from the error's extensions if available, or to +a low-cardinality error identifier such as the exception type. + +If the processing phase completes successfully, instrumentations +SHOULD NOT set `error.type`. + +**[3] `graphql.document.id`:** This is a hash or identifier of the document provided by the user to identify trusted documents. + +**[4] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. +When both `graphql.document.hash` and `graphql.document.id` are available, they SHOULD be preferred over transmitting the raw GraphQL document text for telemetry purposes. This reduces payload size and avoids exposing potentially sensitive operation details. + +**[5] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + +--- + +`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + +--- + +`graphql.processing.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for processing types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_batch` | Executing a DataLoader batch operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_dispatch` | Dispatching grouped DataLoader batch operations. | ![Development](https://img.shields.io/badge/-development-blue) | +| `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | +| `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | +| `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | +| `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### GraphQL operation planning span + + + + + + +**Status:** ![Development](https://img.shields.io/badge/-development-blue) + +This span represents the time spent planning the execution of a GraphQL operation. + +**Span name** SHOULD be `GraphQL Operation Planning`. + +This span covers the operation planning phase of GraphQL request processing, +where the server analyzes the validated query and creates an execution plan. +This phase is common in federated GraphQL systems where queries need to be +planned across multiple sources, and in advanced GraphQL servers that +perform query optimization and planning. + +Planning typically involves determining execution order, identifying required +data sources, optimizing data fetching strategies, and preparing the execution +context. + +**Span status** SHOULD be set to `Error` when the processing phase fails. + +**Span kind** SHOULD be `INTERNAL`. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `plan`. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the planning phase ended with an error. | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [3] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the operation being executed. [4] | `FindBookById`; `GetUserProfile` | + +**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. + +**[2] `error.type`:** If the planning phase fails, `error.type` SHOULD be set to the +`graphql.error.code` from the error's extensions if available, or to +a low-cardinality error identifier such as the exception type. + +If the planning phase completes successfully, instrumentations +SHOULD NOT set `error.type`. + +**[3] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. +When both `graphql.document.hash` and `graphql.document.id` are available, they SHOULD be preferred over transmitting the raw GraphQL document text for telemetry purposes. This reduces payload size and avoids exposing potentially sensitive operation details. + +**[4] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + +--- + +`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + +--- + +`graphql.processing.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for processing types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_batch` | Executing a DataLoader batch operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_dispatch` | Dispatching grouped DataLoader batch operations. | ![Development](https://img.shields.io/badge/-development-blue) | +| `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | +| `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | +| `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | +| `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### GraphQL operation execution span + + + + + + +**Status:** ![Development](https://img.shields.io/badge/-development-blue) + +This span represents the execution phase of a GraphQL operation. + +**Span name** SHOULD be `GraphQL Operation Execution`. + +This span covers the whole execution part of a GraphQL request, +including field resolution and result formatting. + +**Span status** SHOULD be set to `Error` when the processing phase fails. + +**Span kind** SHOULD be `INTERNAL`. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `execute`. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [3] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [4] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the operation being executed. [5] | `FindBookById`; `GetUserProfile` | + +**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. + +**[2] `error.type`:** If the processing phase fails, `error.type` SHOULD be set to the +`graphql.error.code` from the error's extensions if available, or to +a low-cardinality error identifier such as the exception type. + +If the processing phase completes successfully, instrumentations +SHOULD NOT set `error.type`. + +**[3] `graphql.document.id`:** This is a hash or identifier of the document provided by the user to identify trusted documents. + +**[4] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. +When both `graphql.document.hash` and `graphql.document.id` are available, they SHOULD be preferred over transmitting the raw GraphQL document text for telemetry purposes. This reduces payload size and avoids exposing potentially sensitive operation details. + +**[5] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + +--- + +`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | -**[1] `graphql.document`:** The value may be sanitized to exclude sensitive information. +--- + +`graphql.processing.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for processing types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_batch` | Executing a DataLoader batch operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_dispatch` | Dispatching grouped DataLoader batch operations. | ![Development](https://img.shields.io/badge/-development-blue) | +| `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | +| `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | +| `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | +| `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### GraphQL step execution span + + + + + + +**Status:** ![Development](https://img.shields.io/badge/-development-blue) + +This span represents the execution of an individual step within a GraphQL operation execution plan. + +**Span name** SHOULD be `GraphQL Step Execution`. + +This span covers the execution of a single step in the GraphQL execution plan, +where each step typically represents the resolution of one or more fields +that can be executed together. + +Each step execution may involve field resolution, data fetching from external +services, data transformation, and result aggregation. Multiple step execution +spans may run concurrently or sequentially depending on the execution strategy +and field dependencies. + +The `graphql.step.execution` span SHOULD be a descendant of the +`graphql.operation.execution` span. + +Some GraphQL implementations (e.g., plan-based engines like Grafast, +or distributed systems like federation and stitching) replace the classic +resolver-based execution with a plan-based approach where the operation +is broken into steps. In this model, the `graphql.operation.execution` +span still represents the overall execution phase, while `graphql.step.execution` +spans represent individual units of work within that execution - such as +data fetching, transformations, or requests to source systems. + +The `graphql.operation.planning` span, when present, MAY also be +a descendant of `graphql.operation.execution`, depending on whether +the implementation treats planning as part of the execution phase or as a +separate preceding phase. + +**Span status** SHOULD be set to `Error` when the processing phase fails. + +**Span kind** SHOULD be `INTERNAL`. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`graphql.operation.step.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The id of the step in the execution plan. [1] | `0`; `1`; `2` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `step_execute`. [2] | `parse`; `validate`; `execute`; `resolve` | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the step execution ended with an error. | string | Describes a class of error the operation ended with. [3] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [4] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the operation being executed. [5] | `FindBookById`; `GetUserProfile` | +| [`graphql.operation.step.kind`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The kind of step in the execution plan. [6] | `node`; `operation`; `fetch`; `batch` | +| [`graphql.operation.step.plan.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The id of the execution plan this step belongs to. [7] | `plan-1`; `abc-123` | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| [`graphql.source.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the source system. [8] | `accounts`; `products`; `reviews` | +| [`graphql.source.operation.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | A hash of the GraphQL operation to be executed on the source. [9] | `sha256:abc123`; `md5:def456` | +| [`graphql.source.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the GraphQL operation to be executed on the source. [10] | `GetUser`; `FetchProducts`; `ResolveReviews` | +| [`graphql.source.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The type of GraphQL operation to be executed on the source. [11] | `query`; `mutation`; `subscription` | + +**[1] `graphql.operation.step.id`:** The step identifier is implementation-specific and may be numeric, UUID, or any other format used by the GraphQL execution engine. + +**[2] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. + +**[3] `error.type`:** If the step execution fails, `error.type` SHOULD be set to the +`graphql.error.code` from the error's extensions if available, or to +a low-cardinality error identifier such as the exception type. + +If the step execution completes successfully, instrumentations +SHOULD NOT set `error.type`. + +**[4] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. +When both `graphql.document.hash` and `graphql.document.id` are available, they SHOULD be preferred over transmitting the raw GraphQL document text for telemetry purposes. This reduces payload size and avoids exposing potentially sensitive operation details. + +**[5] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. + +**[6] `graphql.operation.step.kind`:** The step kind describes the type of work performed in this execution step. Values are implementation-specific but common examples include node resolution, fetch operations, and batch processing. + +**[7] `graphql.operation.step.plan.id`:** The plan identifier links this step to a specific execution plan, enabling correlation of all steps within the same plan. This is particularly useful in federated GraphQL systems where plans may be cached and reused. + +**[8] `graphql.source.name`:** The human-readable name of the downstream source that a distributed GraphQL gateway dispatches to. For example, this could be a subgraph name in a federated system or a stitched schema name. + +**[9] `graphql.source.operation.hash`:** A hash of the operation document that the gateway sends to the source system. Useful for identifying operations without transmitting the full document. +The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."), consistent with `graphql.document.hash`. + +**[10] `graphql.source.operation.name`:** The operation name of the query or mutation that the gateway sends to the source system. + +**[11] `graphql.source.operation.type`:** The type of operation that the gateway sends to the source system. This enum matches `graphql.operation.type` for consistency. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + +--- + +`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + +--- + +`graphql.processing.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for processing types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_batch` | Executing a DataLoader batch operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_dispatch` | Dispatching grouped DataLoader batch operations. | ![Development](https://img.shields.io/badge/-development-blue) | +| `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | +| `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | +| `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | +| `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | + +--- + +`graphql.source.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### GraphQL field execution span + + + + + + +**Status:** ![Development](https://img.shields.io/badge/-development-blue) + +This span represents the execution of a GraphQL field. + +**Span name** SHOULD be `{graphql.selection.field.coordinate}`. + +This span covers the execution of an individual field, including both +synchronous and asynchronous resolvers. The span ends when the resolver +result is available. + +> **Warning** +> Creating spans for every resolver execution can result in traces with +> hundreds or thousands of spans, severely impacting performance and +> trace readability. Instrumentations MUST NOT create resolver execution +> spans by default for all resolvers. + +Instrumentations SHOULD provide configuration options to control which +resolvers generate spans. Recommended strategies include: + +- **Manual selection**: Allow developers to explicitly mark specific + resolvers for tracing (e.g., via annotations, decorators, or configuration) +- **Asynchronous resolvers only**: Only trace resolvers that return + promises or other asynchronous constructs +- **Depth-based filtering**: Only trace resolvers at the top N levels + of the query (e.g., top 2 levels) +- **Performance-based filtering**: Only trace resolvers that exceed + a certain execution time threshold + +The selection criteria SHOULD be documented clearly for users to +understand which resolvers will generate spans. + +**Span status** SHOULD be set to `Error` when the resolver throws an +exception or the field resolution results in a GraphQL error. + +**Span kind** SHOULD be `INTERNAL`. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `resolve`. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`graphql.selection.field.coordinate`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The coordinate of the field that is being resolved. [2] | `Person.address`; `Query.findBookById` | +| [`graphql.selection.field.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The name of the field that is being resolved. [3] | `address`; `name`; `id` | +| [`graphql.selection.field.parent_type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type that declares the field that is being resolved. [4] | `Person`; `Query`; `Mutation` | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the field resolution ended with an error. | string | Describes a class of error the operation ended with. [5] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.selection.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the selection that is being resolved. Either the field name or an alias. [6] | `newAddress`; `bookTitle` | +| [`graphql.selection.path`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The path of the selection that is being resolved. [7] | `person[0].address` | + +**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. + +**[2] `graphql.selection.field.coordinate`:** The coordinate follows the format "{ParentType}.{fieldName}" and uniquely identifies the field within the GraphQL schema. + +**[3] `graphql.selection.field.name`:** This is always the actual field name as defined in the schema, not an alias. + +**[4] `graphql.selection.field.parent_type`:** This is the GraphQL type name that contains the field definition. + +**[5] `error.type`:** If the resolver throws an exception or results in a GraphQL field error, +`error.type` SHOULD be set to the exception type (its fully-qualified +class name, if applicable) or the `graphql.error.code` if available. + +**[6] `graphql.selection.name`:** If the field has an alias, this SHOULD be the alias name. Otherwise, it SHOULD be the field name. + +**[7] `graphql.selection.path`:** The path represents the location of the field being resolved within the result structure. Therefore, if a field is aliased, the path will use the alias name instead of the actual field name. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + +--- + +`graphql.processing.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for processing types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_batch` | Executing a DataLoader batch operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_dispatch` | Dispatching grouped DataLoader batch operations. | ![Development](https://img.shields.io/badge/-development-blue) | +| `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | +| `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | +| `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | +| `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### GraphQL DataLoader dispatch span + + + + + + +**Status:** ![Development](https://img.shields.io/badge/-development-blue) + +This optional span groups all DataLoader batch operations for a given request. + +**Span name** SHOULD be `GraphQL DataLoader Dispatch`. + +This span is OPTIONAL. When present, it acts as a grouping span for all +DataLoader batch spans within a single GraphQL request. It represents +the overall dispatch of DataLoader batch operations. + +The span SHOULD be a child of the `graphql.server` span. + +Instrumentations that do not produce this span SHOULD attach DataLoader +batch spans directly to the `graphql.server` span or to the +resolver execution span that triggered the batch dispatch. + +**Span kind** SHOULD be `INTERNAL`. + +**Span status** SHOULD follow the [Recording Errors](/docs/general/recording-errors.md) document. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `dataloader_dispatch`. [1] | `parse`; `validate`; `execute`; `resolve` | + +**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. + +--- + +`graphql.processing.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for processing types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_batch` | Executing a DataLoader batch operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_dispatch` | Dispatching grouped DataLoader batch operations. | ![Development](https://img.shields.io/badge/-development-blue) | +| `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | +| `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | +| `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | +| `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### GraphQL DataLoader batch span + + + + + + +**Status:** ![Development](https://img.shields.io/badge/-development-blue) + +This span represents the execution of a DataLoader batch operation. + +**Span name** SHOULD be `GraphQL DataLoader Batch {graphql.dataloader.name}` when +the DataLoader has a name, otherwise `GraphQL DataLoader Batch`. + +This span covers the batched execution of multiple individual DataLoader +requests. It represents the time from when the batch is scheduled to +when all results are available. This span is created when a DataLoader +batches multiple individual load requests into a single batch operation +to optimize data access patterns. + +The parent of this span depends on what the instrumentation supports: + +- If the `graphql.dataloader.dispatch` span is present, + this span SHOULD be a child of it. +- Otherwise, this span SHOULD be a child of the `graphql.server` + span, or of the resolver execution span that triggered the batch dispatch. + +The span SHOULD have links to all `graphql.field.execution` spans +whose resolvers contributed load requests to this batch, so that +the causal relationship between resolvers and the batch is preserved. + +Each link SHOULD include the following attributes: + +- `graphql.selection.field.coordinate`: The coordinate of the field + that triggered the load request (e.g., `User.avatar`). + +When the number of contributing resolvers exceeds a practical limit, +instrumentations MAY cap the number of links to a configurable +maximum. The `graphql.dataloader.batch.size` attribute still +reflects the true batch size regardless of link count. + +**Span kind** SHOULD be `INTERNAL`. + +**Span status** SHOULD follow the [Recording Errors](/docs/general/recording-errors.md) document. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`graphql.dataloader.batch.size`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | int | The number of individual requests in the DataLoader batch. [1] | `5`; `12`; `25` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `dataloader_batch`. [2] | `parse`; `validate`; `execute`; `resolve` | +| [`graphql.dataloader.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the DataLoader instance. [3] | `UserLoader`; `ProductByIdLoader`; `CommentsByPostIdLoader` | +| [`graphql.dataloader.batch.keys`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string[] | A subset of the keys requested in the DataLoader batch. [4] | `["user:1", "user:2", "user:3"]`; `["post:42", "post:99"]` | +| [`graphql.dataloader.cache.hit_count`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | int | The number of requests in the batch that were served from the DataLoader cache. [5] | `0`; `3`; `10` | +| [`graphql.dataloader.cache.miss_count`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | int | The number of requests in the batch that required fetching. [6] | `2`; `5`; `12` | + +**[1] `graphql.dataloader.batch.size`:** This represents the total number of individual load requests that were batched together in a single batch operation. This includes both cache hits and cache misses. + +**[2] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. + +**[3] `graphql.dataloader.name`:** This represents the name or identifier of the DataLoader instance. When the DataLoader implementation supports naming, this SHOULD be set. This helps in identifying specific DataLoader instances in observability. + +**[4] `graphql.dataloader.batch.keys`:** This attribute is opt-in and SHOULD NOT be enabled by default, as keys may contain sensitive or high-cardinality data. When enabled, implementations MAY truncate the list to a configurable maximum number of keys. The string representation of each key depends on the DataLoader implementation. + +**[5] `graphql.dataloader.cache.hit_count`:** This represents the number of individual load requests that were resolved from the DataLoader's cache without requiring a fetch. + +**[6] `graphql.dataloader.cache.miss_count`:** This represents the number of individual load requests that were not found in the DataLoader's cache and required a fetch operation. + +--- + +`graphql.processing.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for processing types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_batch` | Executing a DataLoader batch operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_dispatch` | Dispatching grouped DataLoader batch operations. | ![Development](https://img.shields.io/badge/-development-blue) | +| `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | +| `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | +| `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | +| `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +### GraphQL subscription event span + + + + + + +**Status:** ![Development](https://img.shields.io/badge/-development-blue) + +This span represents the processing of a single subscription event. + +**Span name** SHOULD be `GraphQL Subscription Event`. + +This span covers the processing of an individual event delivered to a +GraphQL subscription. Each time the subscription source emits an event, +a new span SHOULD be created to track the execution of the subscription +field selection set against that event. + +The span SHOULD be a child of the `graphql.server` span that initiated +the subscription, if that span is still available. When the originating +server span has ended (as is common with long-lived subscriptions), +instrumentations SHOULD create a new root span or link to the original +subscription span. + +Context propagation for subscriptions: + +- The initial subscription request carries context from the client +- Each subscription event SHOULD propagate context from the originating + subscription where possible +- Instrumentations MAY create links to the original subscription span + +**Span status** SHOULD be set to `Error` when the subscription event +processing fails. + +**Span kind** SHOULD be `INTERNAL`. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `subscription`. | `query`; `mutation`; `subscription` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `execute`. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` [2] | string | Describes a class of error the operation ended with. [3] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available and not empty. | string | The name of the operation being executed. [4] | `FindBookById`; `GetUserProfile` | +| [`graphql.subscription.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | A unique identifier for the subscription instance. [5] | `sub-abc123`; `ws-conn-42-sub-1` | + +**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. + +**[2] `error.type`:** If the subscription event processing ended with an error. + +**[3] `error.type`:** If the subscription event processing fails, `error.type` SHOULD be set to +the `graphql.error.code` from the error's extensions if available, or to +a low-cardinality error identifier such as the exception type. + +If the subscription event completes successfully, instrumentations +SHOULD NOT set `error.type`. + +**[4] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. + +**[5] `graphql.subscription.id`:** This identifier tracks a specific subscription instance throughout its lifecycle. It SHOULD be unique within the scope of the server and can be used to correlate subscription creation, events, and termination. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + +--- + +`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + +--- + +`graphql.processing.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for processing types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_batch` | Executing a DataLoader batch operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_dispatch` | Dispatching grouped DataLoader batch operations. | ![Development](https://img.shields.io/badge/-development-blue) | +| `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | +| `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | +| `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | +| `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +## GraphQL client + +### GraphQL client span + + + + + + +**Status:** ![Development](https://img.shields.io/badge/-development-blue) + +This span represents an outgoing GraphQL request to a remote server. + +**Span name** SHOULD be of the format `{graphql.operation.type}` provided +`graphql.operation.type` is available. If `graphql.operation.type` is not available, +the span SHOULD be named `GraphQL Operation`. + +When `graphql.operation.name` is available, instrumentations MAY provide +a configuration option to enable a more descriptive span name following +the `{graphql.operation.type} {graphql.operation.name}` format. + +**Span kind** MUST be `CLIENT`. + +The GraphQL client span SHOULD be the parent of any HTTP client span +created for the underlying transport. This allows the GraphQL-level +operation to be correlated with its transport-level details. + +When the GraphQL client library handles transport internally (i.e., no +separate HTTP client instrumentation exists), the GraphQL client span +may be the only span representing the outbound call. + +**Span status** SHOULD be set to `Error` when the response indicates +a failure (e.g., transport error, complete GraphQL failure, or GraphQL +errors in the response). + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the GraphQL operation ended with an error. | string | Describes a class of error the operation ended with. [1] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [2] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available and not empty. | string | The name of the operation being executed. [3] | `FindBookById`; `GetUserProfile` | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| [`server.port`](/docs/registry/attributes/server.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` [4] | int | Server port number. [5] | `80`; `8080`; `443` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [6] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`server.address`](/docs/registry/attributes/server.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Recommended` | string | The domain name or IP address of the GraphQL server being called. [7] | `example.com`; `10.1.2.80`; `/tmp/my.sock` | + +**[1] `error.type`:** If the GraphQL client request fails (e.g., network error, server error, +or GraphQL errors in the response), `error.type` SHOULD be set to a +low-cardinality error identifier. + +When `graphql.error.code` is available from the response errors, +it SHOULD be used as the `error.type` value. + +If the request completes successfully with no errors, instrumentations +SHOULD NOT set `error.type`. + +**[2] `graphql.document.id`:** This is a hash or identifier of the document provided by the user to identify trusted documents. + +**[3] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. + +**[4] `server.port`:** If not the default port for the scheme (e.g., not 443 for HTTPS). + +**[5] `server.port`:** When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD represent the server port behind any intermediaries, for example proxies, if it's available. + +**[6] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. +When both `graphql.document.hash` and `graphql.document.id` are available, they SHOULD be preferred over transmitting the raw GraphQL document text for telemetry purposes. This reduces payload size and avoids exposing potentially sensitive operation details. + +**[7] `server.address`:** This SHOULD be the logical server address, not a proxy address. For federated GraphQL, this is the address of the subgraph service. + +The following attributes can be important for making sampling decisions +and SHOULD be provided **at span creation time** (if provided at all): + +* [`server.address`](/docs/registry/attributes/server.md) +* [`server.port`](/docs/registry/attributes/server.md) + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | --- @@ -48,9 +1173,10 @@ the span SHOULD be named `GraphQL Operation`. | Value | Description | Stability | | --- | --- | --- | -| `mutation` | GraphQL mutation | ![Development](https://img.shields.io/badge/-development-blue) | -| `query` | GraphQL query | ![Development](https://img.shields.io/badge/-development-blue) | -| `subscription` | GraphQL subscription | ![Development](https://img.shields.io/badge/-development-blue) | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | diff --git a/docs/registry/attributes/graphql.md b/docs/registry/attributes/graphql.md index fb84bc6b0b..a819320acd 100644 --- a/docs/registry/attributes/graphql.md +++ b/docs/registry/attributes/graphql.md @@ -3,19 +3,36 @@ # GraphQL +- [GraphQL Attributes](#graphql-attributes) +- [GraphQL DataLoader Attributes](#graphql-dataloader-attributes) +- [GraphQL Error Attributes](#graphql-error-attributes) +- [GraphQL Operation Attributes](#graphql-operation-attributes) +- [GraphQL Selection Attributes](#graphql-selection-attributes) +- [GraphQL Source Attributes](#graphql-source-attributes) +- [GraphQL Subscription Attributes](#graphql-subscription-attributes) + ## GraphQL Attributes -This document defines attributes for GraphQL. +This document defines attributes for GraphQL operations and resolvers. **Attributes:** | Key | Stability | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | -| `graphql.document` | ![Development](https://img.shields.io/badge/-development-blue) | string | The GraphQL document being executed. [1] | `query findBookById { bookById(id: ?) { name } }` | -| `graphql.operation.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the operation being executed. | `findBookById` | +| `graphql.document.hash` | ![Development](https://img.shields.io/badge/-development-blue) | string | The hash of the operation document. [1] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| `graphql.document.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | The document identifier for trusted documents. [2] | `aa3e37c1bf54708e93f12c137afba004` | +| `graphql.operation.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the operation being executed. [3] | `FindBookById`; `GetUserProfile` | | `graphql.operation.type` | ![Development](https://img.shields.io/badge/-development-blue) | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| `graphql.processing.type` | ![Development](https://img.shields.io/badge/-development-blue) | string | The type of processing represented by this span. [4] | `parse`; `validate`; `execute`; `resolve` | + +**[1] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. +When both `graphql.document.hash` and `graphql.document.id` are available, they SHOULD be preferred over transmitting the raw GraphQL document text for telemetry purposes. This reduces payload size and avoids exposing potentially sensitive operation details. + +**[2] `graphql.document.id`:** This is a hash or identifier of the document provided by the user to identify trusted documents. -**[1] `graphql.document`:** The value may be sanitized to exclude sensitive information. +**[3] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. + +**[4] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. --- @@ -23,6 +40,163 @@ This document defines attributes for GraphQL. | Value | Description | Stability | | --- | --- | --- | -| `mutation` | GraphQL mutation | ![Development](https://img.shields.io/badge/-development-blue) | -| `query` | GraphQL query | ![Development](https://img.shields.io/badge/-development-blue) | -| `subscription` | GraphQL subscription | ![Development](https://img.shields.io/badge/-development-blue) | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + +--- + +`graphql.processing.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for processing types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_batch` | Executing a DataLoader batch operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_dispatch` | Dispatching grouped DataLoader batch operations. | ![Development](https://img.shields.io/badge/-development-blue) | +| `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | +| `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | +| `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | +| `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | + +## GraphQL DataLoader Attributes + +This document defines attributes for GraphQL DataLoader operations. + +**Attributes:** + +| Key | Stability | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | +| `graphql.dataloader.batch.keys` | ![Development](https://img.shields.io/badge/-development-blue) | string[] | A subset of the keys requested in the DataLoader batch. [5] | `["user:1", "user:2", "user:3"]`; `["post:42", "post:99"]` | +| `graphql.dataloader.batch.size` | ![Development](https://img.shields.io/badge/-development-blue) | int | The number of individual requests in the DataLoader batch. [6] | `5`; `12`; `25` | +| `graphql.dataloader.cache.hit_count` | ![Development](https://img.shields.io/badge/-development-blue) | int | The number of requests in the batch that were served from the DataLoader cache. [7] | `0`; `3`; `10` | +| `graphql.dataloader.cache.miss_count` | ![Development](https://img.shields.io/badge/-development-blue) | int | The number of requests in the batch that required fetching. [8] | `2`; `5`; `12` | +| `graphql.dataloader.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the DataLoader instance. [9] | `UserLoader`; `ProductByIdLoader`; `CommentsByPostIdLoader` | + +**[5] `graphql.dataloader.batch.keys`:** This attribute is opt-in and SHOULD NOT be enabled by default, as keys may contain sensitive or high-cardinality data. When enabled, implementations MAY truncate the list to a configurable maximum number of keys. The string representation of each key depends on the DataLoader implementation. + +**[6] `graphql.dataloader.batch.size`:** This represents the total number of individual load requests that were batched together in a single batch operation. This includes both cache hits and cache misses. + +**[7] `graphql.dataloader.cache.hit_count`:** This represents the number of individual load requests that were resolved from the DataLoader's cache without requiring a fetch. + +**[8] `graphql.dataloader.cache.miss_count`:** This represents the number of individual load requests that were not found in the DataLoader's cache and required a fetch operation. + +**[9] `graphql.dataloader.name`:** This represents the name or identifier of the DataLoader instance. When the DataLoader implementation supports naming, this SHOULD be set. This helps in identifying specific DataLoader instances in observability. + +## GraphQL Error Attributes + +This document defines the shared attributes used to report GraphQL errors associated with a span or event. + +**Attributes:** + +| Key | Stability | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | +| `graphql.error.code` | ![Development](https://img.shields.io/badge/-development-blue) | string | An optional error code from the extensions field. [10] | `GRAPHQL_VALIDATION_FAILED`; `UNAUTHENTICATED`; `HC00116` | +| `graphql.error.locations` | ![Development](https://img.shields.io/badge/-development-blue) | any | The locations in the GraphQL document associated with the error. [11] | `[{ "line": 3, "column": 7 }, { "line": 5, "column": 4 }]` | +| `graphql.error.message` | ![Development](https://img.shields.io/badge/-development-blue) | string | The error message intended for the developer as a guide to understand and correct the error. [12] | `Cannot query field 'nonExistentField' on type 'User'`; `Variable '$id' of required type 'ID!' was not provided.` | +| `graphql.error.path` | ![Development](https://img.shields.io/badge/-development-blue) | string | The path of the response field which experienced the error. [13] | `user.friends[0].name`; `findBookById` | +| `graphql.error.schema_coordinate` | ![Development](https://img.shields.io/badge/-development-blue) | string | The coordinate of the field in the schema which experienced the error. [14] | `User.friends`; `Query.me` | + +**[10] `graphql.error.code`:** This is an optional field that can be used to categorize errors. The error extension code is a recommended way to categorize errors for easier filtering and monitoring. + +**[11] `graphql.error.locations`:** If an error can be associated to a particular point in the requested GraphQL document, it should contain an array of location objects. Each location is a JSON object with the keys `line` and `column`, both positive integers starting from 1, which describe the beginning of an associated syntax element. +The value MUST be an array of objects, where each object has the following properties: - `line` (integer, required): The line number in the GraphQL document. - `column` (integer, required): The column number in the GraphQL document. + +**[12] `graphql.error.message`:** Every error must contain an entry with the key message with a string description of the error intended for the developer as a guide to understand and correct the error. +> **Warning** > This attribute has unbounded cardinality and MUST NOT be used as a metric > dimension. It is intended for span events and log records only. + +**[13] `graphql.error.path`:** If an error can be associated to a particular field in the GraphQL result, it must contain an entry with the key path that details the path of the response field which experienced the error. +This allows clients to identify whether a null result is intentional or caused by a runtime error. +The path starts from the root of the response. Field names are separated by dots and list indices are represented using bracket notation. If the error happens in an aliased field, the path should use the aliased name, since it represents a path in the response, not in the request. + +**[14] `graphql.error.schema_coordinate`:** If an error can be associated to a particular field in the GraphQL schema, it must contain a schema coordinate in the form of `typeName.fieldName` identifying the field which experienced the error. + +## GraphQL Operation Attributes + +This document defines attributes for GraphQL operation execution steps and planning. + +**Attributes:** + +| Key | Stability | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | +| `graphql.operation.step.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | The id of the step in the execution plan. [15] | `0`; `1`; `2` | +| `graphql.operation.step.kind` | ![Development](https://img.shields.io/badge/-development-blue) | string | The kind of step in the execution plan. [16] | `node`; `operation`; `fetch`; `batch` | +| `graphql.operation.step.plan.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | The id of the execution plan this step belongs to. [17] | `plan-1`; `abc-123` | + +**[15] `graphql.operation.step.id`:** The step identifier is implementation-specific and may be numeric, UUID, or any other format used by the GraphQL execution engine. + +**[16] `graphql.operation.step.kind`:** The step kind describes the type of work performed in this execution step. Values are implementation-specific but common examples include node resolution, fetch operations, and batch processing. + +**[17] `graphql.operation.step.plan.id`:** The plan identifier links this step to a specific execution plan, enabling correlation of all steps within the same plan. This is particularly useful in federated GraphQL systems where plans may be cached and reused. + +## GraphQL Selection Attributes + +Attributes for GraphQL field selections and resolver execution. + +**Attributes:** + +| Key | Stability | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | +| `graphql.selection.field.coordinate` | ![Development](https://img.shields.io/badge/-development-blue) | string | The coordinate of the field that is being resolved. [18] | `Person.address`; `Query.findBookById` | +| `graphql.selection.field.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the field that is being resolved. [19] | `address`; `name`; `id` | +| `graphql.selection.field.parent_type` | ![Development](https://img.shields.io/badge/-development-blue) | string | The type that declares the field that is being resolved. [20] | `Person`; `Query`; `Mutation` | +| `graphql.selection.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the selection that is being resolved. Either the field name or an alias. [21] | `newAddress`; `bookTitle` | +| `graphql.selection.path` | ![Development](https://img.shields.io/badge/-development-blue) | string | The path of the selection that is being resolved. [22] | `person[0].address` | + +**[18] `graphql.selection.field.coordinate`:** The coordinate follows the format "{ParentType}.{fieldName}" and uniquely identifies the field within the GraphQL schema. + +**[19] `graphql.selection.field.name`:** This is always the actual field name as defined in the schema, not an alias. + +**[20] `graphql.selection.field.parent_type`:** This is the GraphQL type name that contains the field definition. + +**[21] `graphql.selection.name`:** If the field has an alias, this SHOULD be the alias name. Otherwise, it SHOULD be the field name. + +**[22] `graphql.selection.path`:** The path represents the location of the field being resolved within the result structure. Therefore, if a field is aliased, the path will use the alias name instead of the actual field name. + +## GraphQL Source Attributes + +This document defines attributes for GraphQL source systems in distributed GraphQL architectures. + +**Attributes:** + +| Key | Stability | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | +| `graphql.source.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the source system. [23] | `accounts`; `products`; `reviews` | +| `graphql.source.operation.hash` | ![Development](https://img.shields.io/badge/-development-blue) | string | A hash of the GraphQL operation to be executed on the source. [24] | `sha256:abc123`; `md5:def456` | +| `graphql.source.operation.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the GraphQL operation to be executed on the source. [25] | `GetUser`; `FetchProducts`; `ResolveReviews` | +| `graphql.source.operation.type` | ![Development](https://img.shields.io/badge/-development-blue) | string | The type of GraphQL operation to be executed on the source. [26] | `query`; `mutation`; `subscription` | + +**[23] `graphql.source.name`:** The human-readable name of the downstream source that a distributed GraphQL gateway dispatches to. For example, this could be a subgraph name in a federated system or a stitched schema name. + +**[24] `graphql.source.operation.hash`:** A hash of the operation document that the gateway sends to the source system. Useful for identifying operations without transmitting the full document. +The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."), consistent with `graphql.document.hash`. + +**[25] `graphql.source.operation.name`:** The operation name of the query or mutation that the gateway sends to the source system. + +**[26] `graphql.source.operation.type`:** The type of operation that the gateway sends to the source system. This enum matches `graphql.operation.type` for consistency. + +--- + +`graphql.source.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | + +## GraphQL Subscription Attributes + +Attributes for GraphQL subscription operations. + +**Attributes:** + +| Key | Stability | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | +| `graphql.subscription.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | A unique identifier for the subscription instance. [27] | `sub-abc123`; `ws-conn-42-sub-1` | + +**[27] `graphql.subscription.id`:** This identifier tracks a specific subscription instance throughout its lifecycle. It SHOULD be unique within the scope of the server and can be used to correlate subscription creation, events, and termination. diff --git a/model/graphql/events.yaml b/model/graphql/events.yaml new file mode 100644 index 0000000000..ad5f071e87 --- /dev/null +++ b/model/graphql/events.yaml @@ -0,0 +1,96 @@ +groups: + - id: event.graphql.error + name: graphql.error + stability: development + type: event + brief: > + This event describes a GraphQL error that occurred during operation execution. + note: > + GraphQL errors can occur during various phases of request processing including + parsing, validation, and execution. Errors should be recorded on the root + span for simplicity and scalability. Multiple errors can be recorded as + separate events on the same span. + + Instrumentations SHOULD set the severity based on the impact of the error. + When the response contains both `data` and `errors` (partial success), + the severity SHOULD be WARN (severity number 13). When the response + contains only `errors` and no `data` (complete failure), the severity + SHOULD be ERROR (severity number 17). When severity cannot be determined, + instrumentations SHOULD default to ERROR. + + **GraphQL errors and exceptions:** + When a GraphQL error is caused by an underlying exception, instrumentations + SHOULD record both the GraphQL error details and the exception information + on the same `graphql.error` event rather than emitting separate events. + The exception attributes (`exception.type`, `exception.message`, + `exception.stacktrace`) MAY be included alongside the `graphql.error.*` + attributes on the same event. This avoids duplicate events for the same + underlying issue and keeps the error context together. + + When a GraphQL error is NOT caused by an exception (e.g., validation errors, + authorization errors returned by the GraphQL layer), only the `graphql.error.*` + attributes are needed. + + Instrumentations SHOULD cap the number of error events per span to a configurable + maximum (default: 10) to prevent excessive telemetry. When capped, the total error + count SHOULD still be recorded via `graphql.error.count` on the parent span. + + **Relationship to span status and `error.type`:** + GraphQL error events provide detailed per-error information from the response `errors` + array. They are complementary to span-level error signals: + + - `error.type` on the parent span provides a low-cardinality error classification + for the overall operation outcome. It SHOULD be set based on the `graphql.error.code` + of the first error or a general error category. + - Individual `graphql.error` events provide the structured details for each error + in the response. + + Instrumentations SHOULD set both `error.type` on the span and emit error events + for each error in the response. The error events provide the detail; `error.type` + and span status provide the summary. + attributes: + - ref: graphql.error.message + requirement_level: required + - ref: graphql.error.locations + requirement_level: + conditionally_required: Should be included when the error can be associated with a specific location in the GraphQL document. + - ref: graphql.error.path + requirement_level: + conditionally_required: Must be included when the error can be associated with a particular field in the GraphQL result. + - ref: graphql.error.code + requirement_level: opt_in + - ref: exception.type + requirement_level: + conditionally_required: If the error was caused by an exception. + note: > + The fully qualified class name or type of the exception that caused + this GraphQL error, when applicable. + - ref: exception.message + requirement_level: + conditionally_required: If the error was caused by an exception. + note: > + The exception message. When both `graphql.error.message` and + `exception.message` are available, both SHOULD be set as they + may differ (the GraphQL error message is user-facing, while the + exception message is the raw internal error). + - ref: exception.stacktrace + requirement_level: opt_in + note: > + The exception stacktrace, when available and the error was caused + by an exception. This attribute is opt-in due to its size and + potential to contain sensitive information. + - ref: graphql.error.schema_coordinate + requirement_level: + conditionally_required: Should be included when the error can be associated with a particular field in the GraphQL schema. + - ref: graphql.operation.type + requirement_level: recommended + note: > + Including the operation type on error events enables correlation and filtering + of errors by operation type without requiring access to the parent span. + - ref: graphql.operation.name + requirement_level: + conditionally_required: If available and not empty. + note: > + Including the operation name on error events enables correlation of errors + to specific operations, especially useful when error events are processed + independently of spans (e.g., in log-based pipelines). diff --git a/model/graphql/metrics.yaml b/model/graphql/metrics.yaml new file mode 100644 index 0000000000..04dff7cd45 --- /dev/null +++ b/model/graphql/metrics.yaml @@ -0,0 +1,345 @@ +groups: + - id: metric_attributes.graphql.server.base + type: attribute_group + brief: "Base attributes shared across all GraphQL server metrics." + attributes: + - ref: graphql.operation.type + requirement_level: + conditionally_required: If available. + - ref: graphql.operation.name + requirement_level: opt_in + note: | + The `graphql.operation.name` is provided by the client and can have + unbounded cardinality. It MUST only be enabled when the operation + namespace has bounded cardinality, such as when using persisted + operations or trusted documents. + + When `graphql.operation.name` is not enabled, `graphql.document.hash` + or `graphql.document.id` MAY be used as lower-cardinality alternatives. + + - id: metric_attributes.graphql.server + type: attribute_group + brief: "Common attributes for GraphQL server request-level metrics." + extends: metric_attributes.graphql.server.base + attributes: + - ref: graphql.document.id + requirement_level: opt_in + note: | + The `graphql.document.id` MUST only be enabled when the set of + document identifiers has bounded cardinality, such as when using + trusted documents or persisted operations. + - ref: error.type + requirement_level: + conditionally_required: If the GraphQL request ended with an error. + note: | + For GraphQL, `error.type` captures the category of error that caused + the request to fail. Since GraphQL can return partial data with errors + in a 200 OK response, this attribute SHOULD be set when the response + contains errors that the instrumentation considers a failure. + + The `error.type` value SHOULD be predictable and SHOULD have low + cardinality. Instrumentations SHOULD document the list of errors + they report. + + - id: metric_attributes.graphql.server.phase + type: attribute_group + brief: "Common attributes for GraphQL server phase duration metrics." + extends: metric_attributes.graphql.server.base + attributes: + - ref: error.type + requirement_level: + conditionally_required: If the processing phase ended with an error. + note: | + For phase-level metrics, `error.type` captures the category of error + that caused the phase to fail. This allows monitoring error rates + per phase independently of the overall request outcome. + + - id: metric.graphql.server.request.duration + type: metric + metric_name: graphql.server.request.duration + annotations: + code_generation: + metric_value_type: double + brief: "Duration of GraphQL server requests." + instrument: histogram + unit: "s" + stability: development + note: | + This metric measures the end-to-end duration of processing a GraphQL + request on the server. It starts when the server begins processing + the GraphQL operation and ends when the response is complete. + + When this metric is reported alongside a GraphQL server span, the + metric value SHOULD be the same as the GraphQL server span duration. + + This metric is complementary to `http.server.request.duration` which + measures the transport-level duration. The GraphQL metric captures + application-level processing time. + + Histogram bucket boundaries SHOULD be chosen to capture expected + request durations. + extends: metric_attributes.graphql.server + + - id: metric.graphql.server.active_requests + type: metric + metric_name: graphql.server.active_requests + annotations: + code_generation: + metric_value_type: int + brief: "Number of active GraphQL server requests." + instrument: updowncounter + unit: "{request}" + stability: development + note: | + This metric tracks the number of GraphQL requests currently being + processed by the server. It is incremented when processing begins + and decremented when processing is complete. + + Since `graphql.operation.type` is not available before parsing, + instrumentations SHOULD delay the increment until after parsing completes + and the operation type is known. This means requests that fail during + parsing will not be reflected in this metric, but it ensures consistent + attribute sets on increment and decrement. + attributes: + - ref: graphql.operation.type + requirement_level: + conditionally_required: If available. + + - id: metric.graphql.server.request.parse.duration + type: metric + metric_name: graphql.server.request.parse.duration + annotations: + code_generation: + metric_value_type: double + brief: "Duration of GraphQL document parsing." + instrument: histogram + unit: "s" + stability: development + note: | + This metric measures the time spent parsing the GraphQL document + string into an AST. When reported alongside a parse span, the + metric value SHOULD match the span duration. + + Histogram bucket boundaries SHOULD be chosen to capture typical + parsing/validation times. + extends: metric_attributes.graphql.server.phase + + - id: metric.graphql.server.request.validate.duration + type: metric + metric_name: graphql.server.request.validate.duration + annotations: + code_generation: + metric_value_type: double + brief: "Duration of GraphQL document validation." + instrument: histogram + unit: "s" + stability: development + note: | + This metric measures the time spent validating the GraphQL document + AST against the schema. When reported alongside a validation span, + the metric value SHOULD match the span duration. + + Histogram bucket boundaries SHOULD be chosen to capture typical + parsing/validation times. + extends: metric_attributes.graphql.server.phase + + - id: metric.graphql.server.request.execute.duration + type: metric + metric_name: graphql.server.request.execute.duration + annotations: + code_generation: + metric_value_type: double + brief: "Duration of GraphQL operation execution." + instrument: histogram + unit: "s" + stability: development + note: | + This metric measures the time spent executing the GraphQL operation, + including field resolution and result formatting. When reported + alongside an execution span, the metric value SHOULD match the span + duration. + + Histogram bucket boundaries SHOULD be chosen to capture expected + execution durations. + extends: metric_attributes.graphql.server.phase + + - id: metric.graphql.server.request.plan.duration + type: metric + metric_name: graphql.server.request.plan.duration + annotations: + code_generation: + metric_value_type: double + brief: "Duration of GraphQL operation planning." + instrument: histogram + unit: "s" + stability: development + note: | + This metric measures the time spent planning the execution of a + GraphQL operation. This is relevant for federated GraphQL systems + and advanced servers that perform query optimization. + + This metric is OPTIONAL - it only applies to servers that have + a distinct planning phase. + + Histogram bucket boundaries SHOULD be chosen to capture typical + planning times. + extends: metric_attributes.graphql.server.phase + + - id: metric.graphql.server.response.error_count + type: metric + metric_name: graphql.server.response.error_count + annotations: + code_generation: + metric_value_type: int + brief: "Number of errors in a GraphQL response." + instrument: histogram + unit: "{error}" + stability: development + note: | + This metric records the number of errors included in the GraphQL + response `errors` array. A value of 0 indicates a successful + response with no errors. + + This is a histogram (not a counter) because it records the error + count per response, enabling analysis of error distribution across + requests. + + Histogram bucket boundaries for error counts. + attributes: + - ref: graphql.operation.type + requirement_level: + conditionally_required: If available. + - ref: graphql.operation.name + requirement_level: opt_in + + - id: metric.graphql.server.subscription.active + type: metric + metric_name: graphql.server.subscription.active + annotations: + code_generation: + metric_value_type: int + brief: "Number of active GraphQL subscriptions." + instrument: updowncounter + unit: "{subscription}" + stability: development + note: | + This metric tracks the number of GraphQL subscriptions currently active + on the server. It is incremented when a subscription is created and + decremented when a subscription is terminated (either by client + disconnect or server-side cancellation). + + This is distinct from `graphql.server.active_requests`, which tracks + short-lived request processing (parse → execute → respond). For + subscriptions, `active_requests` only covers the initial setup request, + while this metric tracks the long-lived subscription connection that + may remain open for hours or days delivering events. + attributes: + - ref: graphql.document.id + requirement_level: opt_in + note: | + The `graphql.document.id` MUST only be enabled when the set of + document identifiers has bounded cardinality, such as when using + trusted documents or persisted operations. + - ref: graphql.document.hash + requirement_level: opt_in + note: | + The `graphql.document.hash` MUST only be enabled when the set of + document hashes has bounded cardinality, such as when using + persisted operations or trusted documents. + - ref: graphql.operation.name + requirement_level: opt_in + note: | + The `graphql.operation.name` is provided by the client and can have + unbounded cardinality. It MUST only be enabled when the operation + namespace has bounded cardinality, such as when using persisted + operations or trusted documents. + + - id: metric.graphql.server.subscription.event.duration + type: metric + metric_name: graphql.server.subscription.event.duration + annotations: + code_generation: + metric_value_type: double + brief: "Duration of processing a single subscription event." + instrument: histogram + unit: "s" + stability: development + note: | + This metric measures the time spent processing an individual subscription + event, from when the event is received from the source to when the + response is sent to the subscriber. + + Histogram bucket boundaries SHOULD be chosen to capture expected + subscription event processing times. + attributes: + - ref: graphql.document.id + requirement_level: opt_in + note: | + The `graphql.document.id` MUST only be enabled when the set of + document identifiers has bounded cardinality, such as when using + trusted documents or persisted operations. + - ref: graphql.document.hash + requirement_level: opt_in + note: | + The `graphql.document.hash` MUST only be enabled when the set of + document hashes has bounded cardinality, such as when using + persisted operations or trusted documents. + - ref: graphql.operation.name + requirement_level: opt_in + note: | + The `graphql.operation.name` is provided by the client and can have + unbounded cardinality. It MUST only be enabled when the operation + namespace has bounded cardinality, such as when using persisted + operations or trusted documents. + - ref: error.type + requirement_level: + conditionally_required: If the subscription event processing ended with an error. + + - id: metric_attributes.graphql.client + type: attribute_group + brief: "Common attributes for GraphQL client metrics." + attributes: + - ref: graphql.operation.type + requirement_level: + conditionally_required: If available. + - ref: graphql.operation.name + requirement_level: opt_in + note: | + The `graphql.operation.name` can have unbounded cardinality. + It MUST only be enabled when the operation namespace has bounded + cardinality, such as when using persisted operations or trusted documents. + - ref: server.address + requirement_level: recommended + - ref: server.port + requirement_level: + conditionally_required: If not the default port for the scheme. + - ref: error.type + requirement_level: + conditionally_required: If the GraphQL request ended with an error. + - ref: graphql.document.id + requirement_level: opt_in + note: | + The `graphql.document.id` MUST only be enabled when the set of + document identifiers has bounded cardinality, such as when using + trusted documents or persisted operations. + + - id: metric.graphql.client.request.duration + type: metric + metric_name: graphql.client.request.duration + annotations: + code_generation: + metric_value_type: double + brief: "Duration of GraphQL client requests." + instrument: histogram + unit: "s" + stability: development + note: | + This metric measures the duration of outgoing GraphQL client requests, + from when the request is initiated to when the response is fully received. + + This metric is complementary to `http.client.request.duration` which + measures the transport-level duration. + + Histogram bucket boundaries SHOULD be chosen to capture expected + request durations. + extends: metric_attributes.graphql.client diff --git a/model/graphql/registry.yaml b/model/graphql/registry.yaml index c3c565fad1..23a59a2d67 100644 --- a/model/graphql/registry.yaml +++ b/model/graphql/registry.yaml @@ -1,14 +1,19 @@ groups: - id: registry.graphql type: attribute_group + stability: development display_name: GraphQL Attributes - brief: 'This document defines attributes for GraphQL.' + brief: "This document defines attributes for GraphQL operations and resolvers." attributes: - id: graphql.operation.name brief: "The name of the operation being executed." type: string stability: development - examples: 'findBookById' + examples: ["FindBookById", "GetUserProfile"] + note: > + This represents the operation name as specified in the GraphQL + operation document. When the operation name is not provided, this + attribute SHOULD be omitted. - id: graphql.operation.type brief: "The type of the operation being executed." stability: development @@ -16,20 +21,428 @@ groups: members: - id: query value: "query" - brief: "GraphQL query" + brief: "GraphQL query operation" stability: development - id: mutation value: "mutation" - brief: "GraphQL mutation" + brief: "GraphQL mutation operation" stability: development - id: subscription value: "subscription" - brief: "GraphQL subscription" + brief: "GraphQL subscription operation" stability: development - examples: ['query', 'mutation', 'subscription'] - - id: graphql.document - brief: "The GraphQL document being executed." + - id: _OTHER + value: "_OTHER" + brief: + "A fallback for operation types not covered by specific values + in this enum." + stability: development + examples: ["query", "mutation", "subscription"] + - id: graphql.processing.type + brief: "The type of processing represented by this span." + stability: development + type: + members: + - id: parse + value: "parse" + brief: "Parsing the GraphQL document into an AST." + stability: development + - id: validate + value: "validate" + brief: "Validating the GraphQL document against the schema." + stability: development + - id: variable_coercion + value: "variable_coercion" + brief: "Coercing and validating input variables." + stability: development + - id: plan + value: "plan" + brief: "Planning the execution of a GraphQL operation." + stability: development + - id: execute + value: "execute" + brief: "Executing a GraphQL operation." + stability: development + - id: step_execute + value: "step_execute" + brief: "Executing an individual step within an execution plan." + stability: development + - id: resolve + value: "resolve" + brief: "Resolving an individual field." + stability: development + - id: dataloader_dispatch + value: "dataloader_dispatch" + brief: "Dispatching grouped DataLoader batch operations." + stability: development + - id: dataloader_batch + value: "dataloader_batch" + brief: "Executing a DataLoader batch operation." + stability: development + - id: _OTHER + value: "_OTHER" + brief: + "A fallback for processing types not covered by specific values + in this enum." + stability: development + examples: ["parse", "validate", "execute", "resolve"] + note: > + This attribute allows telemetry consumers to programmatically identify + what kind of processing a span represents without relying on span + names. + - id: graphql.document.hash + brief: "The hash of the operation document." + type: string + stability: development + examples: + [ + "sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c", + ] + note: > + The hash algorithm used SHOULD be specified as part of the value + (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the + default hash algorithm unless there is a specific reason to use a + different one. This can be used for monitoring operation distribution + and caching strategies. + + When both `graphql.document.hash` and `graphql.document.id` are + available, they SHOULD be preferred over transmitting the raw GraphQL + document text for telemetry purposes. This reduces payload size and + avoids exposing potentially sensitive operation details. + - id: graphql.document.id + brief: > + The document identifier for trusted documents. + type: string + stability: development + examples: ["aa3e37c1bf54708e93f12c137afba004"] + note: > + This is a hash or identifier of the document provided by the user to + identify trusted documents. + + - id: registry.graphql.selection + type: attribute_group + stability: development + display_name: GraphQL Selection Attributes + brief: > + Attributes for GraphQL field selections and resolver execution. + attributes: + - id: graphql.selection.name + brief: + "The name of the selection that is being resolved. Either the field + name or an alias." + type: string + stability: development + examples: ["newAddress", "bookTitle"] + note: > + If the field has an alias, this SHOULD be the alias name. Otherwise, + it SHOULD be the field name. + - id: graphql.selection.path + brief: "The path of the selection that is being resolved." + type: string + stability: development + examples: ["person[0].address"] + note: > + The path represents the location of the field being resolved within + the result structure. Therefore, if a field is aliased, the path will + use the alias name instead of the actual field name. + - id: graphql.selection.field.name + brief: "The name of the field that is being resolved." + type: string + stability: development + examples: ["address", "name", "id"] + note: > + This is always the actual field name as defined in the schema, not an + alias. + - id: graphql.selection.field.parent_type + brief: "The type that declares the field that is being resolved." + type: string + stability: development + examples: ["Person", "Query", "Mutation"] + note: > + This is the GraphQL type name that contains the field definition. + - id: graphql.selection.field.coordinate + brief: "The coordinate of the field that is being resolved." + type: string + stability: development + examples: ["Person.address", "Query.findBookById"] + note: > + The coordinate follows the format "{ParentType}.{fieldName}" and + uniquely identifies the field within the GraphQL schema. + + - id: registry.graphql.error + type: attribute_group + stability: development + display_name: GraphQL Error Attributes + brief: > + This document defines the shared attributes used to report GraphQL errors + associated with a span or event. + note: > + The `graphql.error.*` attributes capture structured details from the + GraphQL response `errors` array. These are complementary to the standard + `error.type` attribute: + + - `error.type` provides a low-cardinality error classification used for + span status + and metric dimensions (e.g., `GRAPHQL_VALIDATION_FAILED`). + - `graphql.error.*` attributes provide the full structured error details + from the + GraphQL response (message, path, locations, code, etc.). + + When `graphql.error.code` is available, it SHOULD be used as the + `error.type` value. See span definitions for detailed `error.type` + guidance per span kind. + attributes: + - id: graphql.error.message + type: string + stability: development + brief: > + The error message intended for the developer as a guide to understand + and correct the error. + examples: + [ + "Cannot query field 'nonExistentField' on type 'User'", + "Variable '$id' of required type 'ID!' was not provided.", + ] + note: > + Every error must contain an entry with the key message with a string + description of the error intended for the developer as a guide to + understand and correct the error. + + > **Warning** > This attribute has unbounded cardinality and MUST NOT + be used as a metric > dimension. It is intended for span events and + log records only. + + - id: graphql.error.locations + type: any + stability: development + brief: > + The locations in the GraphQL document associated with the error. + examples: + - '[{ "line": 3, "column": 7 }, { "line": 5, "column": 4 }]' + note: > + If an error can be associated to a particular point in the requested + GraphQL document, it should contain an array of location objects. Each + location is a JSON object with the keys `line` and `column`, both + positive integers starting from 1, which describe the beginning of an + associated syntax element. + + The value MUST be an array of objects, where each object has the following properties: + - `line` (integer, required): The line number in the GraphQL document. + - `column` (integer, required): The column number in the GraphQL document. + + - id: graphql.error.path + type: string + stability: development + brief: > + The path of the response field which experienced the error. + examples: ["user.friends[0].name", "findBookById"] + note: > + If an error can be associated to a particular field in the GraphQL + result, it must contain an entry with the key path that details the + path of the response field which experienced the error. + + This allows clients to identify whether a null result is intentional + or caused by a runtime error. + + The path starts from the root of the response. Field names are + separated by dots and list indices are represented using bracket + notation. If the error happens in an aliased field, the path should + use the aliased name, since it represents a path in the response, not + in the request. + + - id: graphql.error.code + type: string + stability: development + brief: > + An optional error code from the extensions field. + examples: ["GRAPHQL_VALIDATION_FAILED", "UNAUTHENTICATED", "HC00116"] + note: > + This is an optional field that can be used to categorize errors. The + error extension code is a recommended way to categorize errors for + easier filtering and monitoring. + + - id: graphql.error.schema_coordinate + type: string + stability: development + brief: > + The coordinate of the field in the schema which experienced the error. + examples: ["User.friends", "Query.me"] + note: > + If an error can be associated to a particular field in the GraphQL + schema, it must contain a schema coordinate in the form of + `typeName.fieldName` identifying the field which experienced the + error. + + - id: registry.graphql.operation + type: attribute_group + stability: development + display_name: GraphQL Operation Attributes + brief: > + This document defines attributes for GraphQL operation execution steps and + planning. + attributes: + - id: graphql.operation.step.id + brief: "The id of the step in the execution plan." + type: string + stability: development + examples: ["0", "1", "2"] + note: > + The step identifier is implementation-specific and may be numeric, + UUID, or any other format used by the GraphQL execution engine. + + - id: graphql.operation.step.kind + brief: "The kind of step in the execution plan." + type: string + stability: development + examples: ["node", "operation", "fetch", "batch"] + note: > + The step kind describes the type of work performed in this execution + step. Values are implementation-specific but common examples include + node resolution, fetch operations, and batch processing. + - id: graphql.operation.step.plan.id + brief: "The id of the execution plan this step belongs to." + type: string + stability: development + examples: ["plan-1", "abc-123"] + note: > + The plan identifier links this step to a specific execution plan, + enabling correlation of all steps within the same plan. This is + particularly useful in federated GraphQL systems where plans may be + cached and reused. + + - id: registry.graphql.source + type: attribute_group + stability: development + display_name: GraphQL Source Attributes + brief: > + This document defines attributes for GraphQL source systems in distributed + GraphQL architectures. + attributes: + - id: graphql.source.name + brief: "The name of the source system." + type: string + stability: development + examples: ["accounts", "products", "reviews"] + note: > + The human-readable name of the downstream source that a distributed + GraphQL gateway dispatches to. For example, this could be a subgraph + name in a federated system or a stitched schema name. + - id: graphql.source.operation.name + brief: "The name of the GraphQL operation to be executed on the source." + type: string + stability: development + examples: ["GetUser", "FetchProducts", "ResolveReviews"] + note: > + The operation name of the query or mutation that the gateway sends to + the source system. + - id: graphql.source.operation.type + brief: "The type of GraphQL operation to be executed on the source." + stability: development + type: + members: + - id: query + value: "query" + brief: "GraphQL query operation" + stability: development + - id: mutation + value: "mutation" + brief: "GraphQL mutation operation" + stability: development + - id: subscription + value: "subscription" + brief: "GraphQL subscription operation" + stability: development + - id: _OTHER + value: "_OTHER" + brief: + "A fallback for operation types not covered by specific values + in this enum." + stability: development + examples: ["query", "mutation", "subscription"] + note: > + The type of operation that the gateway sends to the source system. + This enum matches `graphql.operation.type` for consistency. + - id: graphql.source.operation.hash + brief: "A hash of the GraphQL operation to be executed on the source." + type: string + stability: development + examples: ["sha256:abc123", "md5:def456"] + note: > + A hash of the operation document that the gateway sends to the source + system. Useful for identifying operations without transmitting the + full document. + + The hash algorithm used SHOULD be specified as part of the value + (e.g., "sha256:..."), consistent with `graphql.document.hash`. + + - id: registry.graphql.dataloader + type: attribute_group + stability: development + display_name: GraphQL DataLoader Attributes + brief: > + This document defines attributes for GraphQL DataLoader operations. + attributes: + - id: graphql.dataloader.name + brief: "The name of the DataLoader instance." + type: string + stability: development + examples: ["UserLoader", "ProductByIdLoader", "CommentsByPostIdLoader"] + note: > + This represents the name or identifier of the DataLoader instance. + When the DataLoader implementation supports naming, this SHOULD be + set. This helps in identifying specific DataLoader instances in + observability. + - id: graphql.dataloader.batch.size + brief: "The number of individual requests in the DataLoader batch." + type: int + stability: development + examples: [5, 12, 25] + note: > + This represents the total number of individual load requests that were + batched together in a single batch operation. This includes both cache + hits and cache misses. + - id: graphql.dataloader.batch.keys + brief: "A subset of the keys requested in the DataLoader batch." + type: string[] + stability: development + examples: [["user:1", "user:2", "user:3"], ["post:42", "post:99"]] + note: > + This attribute is opt-in and SHOULD NOT be enabled by default, as keys + may contain sensitive or high-cardinality data. When enabled, + implementations MAY truncate the list to a configurable maximum number + of keys. The string representation of each key depends on the + DataLoader implementation. + - id: graphql.dataloader.cache.hit_count + brief: "The number of requests in the batch that were served from the + DataLoader cache." + type: int + stability: development + examples: [0, 3, 10] + note: > + This represents the number of individual load requests that were + resolved from the DataLoader's cache without requiring a fetch. + - id: graphql.dataloader.cache.miss_count + brief: "The number of requests in the batch that required fetching." + type: int + stability: development + examples: [2, 5, 12] + note: > + This represents the number of individual load requests that were not + found in the DataLoader's cache and required a fetch operation. + + - id: registry.graphql.subscription + type: attribute_group + stability: development + display_name: GraphQL Subscription Attributes + brief: > + Attributes for GraphQL subscription operations. + attributes: + - id: graphql.subscription.id + brief: "A unique identifier for the subscription instance." type: string stability: development - note: The value may be sanitized to exclude sensitive information. - examples: 'query findBookById { bookById(id: ?) { name } }' + examples: ["sub-abc123", "ws-conn-42-sub-1"] + note: > + This identifier tracks a specific subscription instance throughout its + lifecycle. It SHOULD be unique within the scope of the server and can + be used to correlate subscription creation, events, and termination. diff --git a/model/graphql/spans.yaml b/model/graphql/spans.yaml new file mode 100644 index 0000000000..69e1d9bcde --- /dev/null +++ b/model/graphql/spans.yaml @@ -0,0 +1,659 @@ +# GraphQL Span Hierarchy +# ====================== +# +# The following diagram shows the normative parent-child relationships +# between GraphQL spans. Spans marked [optional] are not required. +# +# graphql.server (SERVER) +# ├── graphql.document.parsing (INTERNAL) +# ├── graphql.document.validation (INTERNAL) +# ├── graphql.document.variable_coercion (INTERNAL) +# ├── graphql.operation.planning (INTERNAL) [optional] +# ├── graphql.operation.execution (INTERNAL) +# │ ├── graphql.step.execution (INTERNAL) [optional, repeating] +# │ └── graphql.field.execution (INTERNAL) [optional, repeating] +# ├── graphql.dataloader.dispatch (INTERNAL) [optional] +# │ └── graphql.dataloader.batch (INTERNAL) [repeating] +# └── graphql.subscription.event (INTERNAL) [subscription only] +# +# graphql.client (CLIENT) +# └── (HTTP client span) +# +groups: + - id: span.graphql.server + type: span + stability: development + span_kind: server + brief: > + This span represents an incoming GraphQL request on a server + implementation. + note: | + **Span name** SHOULD be of the format `{graphql.operation.type}` provided + `graphql.operation.type` is available. If `graphql.operation.type` is not available, + the span SHOULD be named `GraphQL Operation`. + + For operation domains with bounded cardinality (e.g. trusted documents), instrumentations MAY provide + a configuration option to enable a more descriptive span name following + the `{graphql.operation.type} {graphql.operation.name}` format when + `graphql.operation.name` is available and the operation is successfully identified + in the document. + + > **Warning** + > The `graphql.operation.name` value is provided by the client and can have high + > cardinality. Using it in the GraphQL server span name is NOT RECOMMENDED for + > ad-hoc operations without careful consideration of cardinality implications. + > For trusted documents, the cardinality is bounded and using the operation + > name in the span name is more acceptable. + > + > Implementations MUST NOT include the operation name in the span name when + > the operation was not found or could not be identified in the document. + > This prevents potential security issues and ensures span names remain meaningful. + + For subscription operations, the server span represents the initial subscription + request and setup. Individual subscription events are represented by separate + `graphql.subscription.event` spans. Long-lived subscriptions MAY end the server + span after successful setup, with subsequent events creating their own spans. + + **Span status** guidance: + + Span status SHOULD be set to `Error` when the response contains errors. + This includes parse errors, validation errors, variable coercion errors, + request errors, total execution failure (where `data` is `null` and `errors` + is non-empty), and partial success (where both `data` and `errors` are present). + Individual GraphQL errors SHOULD still be recorded as events. + + > **Note** + > Instrumentations that have additional context about specific errors MAY + > use this context to set the span status more precisely. For example, if + > an instrumentation knows that the errors only affect optional, + > non-critical fields, it MAY choose not to set `Error` status. + + **Span status description** guidance: + + Span status descriptions are optional and SHOULD only be set when they provide + additional context not already captured by `error.type` or error events. + Recommended cases for setting a description: + + - When the error is ambiguous without additional context + - To distinguish between complete failure and partial success when both set + `Error` status (e.g., "Partial success: 2 of 5 fields had errors") + - When transport-level context is needed (e.g., "WebSocket connection closed unexpectedly") + + Do NOT repeat `error.type` or `graphql.error.message` in the status description. + + **Incremental delivery (`@defer`/`@stream`):** + + When a GraphQL operation uses `@defer` or `@stream` directives, the response + is delivered incrementally. The server span SHOULD encompass the entire response + delivery, including all deferred and streamed payloads. If the instrumentation + cannot keep the span open for the full incremental delivery, it SHOULD: + + - End the span after the initial payload and document this behavior + - Record deferred/streamed errors as separate events if the span + has already ended + + **Batched operations:** + + When multiple GraphQL operations are batched in a single HTTP request, + instrumentations SHOULD create one server span per operation in the batch. + The HTTP server span serves as the parent of all GraphQL operation spans. + Each operation span SHOULD have its own `graphql.operation.type` and + `graphql.operation.name` attributes. + attributes: + - ref: error.type + requirement_level: + conditionally_required: If the GraphQL operation ended with an error. + examples: + [ + "GRAPHQL_PARSE_FAILED", + "GRAPHQL_VALIDATION_FAILED", + "HC0016", + "java.lang.RuntimeException", + ] + note: | + GraphQL errors can occur during various phases of request processing. + + If the request fails before execution begins (e.g., parsing, validation, or + variable coercion fails), `error.type` SHOULD be set to a low-cardinality + error identifier. When `graphql.error.code` is available from the error's + extensions, it SHOULD be used as the `error.type` value. + + If the request completes execution but the response contains errors, + `error.type` SHOULD be set to the `graphql.error.code` from the first + error's extensions if available, or to a low-cardinality error identifier + describing the class of error (e.g., the exception type). + + If the request completes execution with partial data (both `data` and + `errors` are present), `error.type` SHOULD still be set. + + If the request completes successfully with no errors, instrumentations + SHOULD NOT set `error.type`. + + The `error.type` value SHOULD be predictable and SHOULD have low + cardinality. Instrumentations SHOULD document the list of errors they + report. + - ref: graphql.operation.type + sampling_relevant: true + requirement_level: + conditionally_required: If parsing succeeded + - ref: graphql.operation.name + requirement_level: + conditionally_required: If available and not empty. + - ref: graphql.document.hash + requirement_level: recommended + - ref: graphql.document.id + requirement_level: + conditionally_required: If using trusted documents. + - ref: server.address + requirement_level: recommended + brief: > + The logical server hostname or IP address serving the GraphQL + endpoint. + note: > + This SHOULD be the logical server address, not a proxy or load + balancer address. + - ref: server.port + requirement_level: + conditionally_required: If not the default port for the scheme (e.g., not 443 for HTTPS). + + - id: span.graphql.client + type: span + stability: development + span_kind: client + brief: > + This span represents an outgoing GraphQL request to a remote server. + note: | + **Span name** SHOULD be of the format `{graphql.operation.type}` provided + `graphql.operation.type` is available. If `graphql.operation.type` is not available, + the span SHOULD be named `GraphQL Operation`. + + When `graphql.operation.name` is available, instrumentations MAY provide + a configuration option to enable a more descriptive span name following + the `{graphql.operation.type} {graphql.operation.name}` format. + + **Span kind** MUST be `CLIENT`. + + The GraphQL client span SHOULD be the parent of any HTTP client span + created for the underlying transport. This allows the GraphQL-level + operation to be correlated with its transport-level details. + + When the GraphQL client library handles transport internally (i.e., no + separate HTTP client instrumentation exists), the GraphQL client span + may be the only span representing the outbound call. + + **Span status** SHOULD be set to `Error` when the response indicates + a failure (e.g., transport error, complete GraphQL failure, or GraphQL + errors in the response). + attributes: + - ref: error.type + requirement_level: + conditionally_required: If the GraphQL operation ended with an error. + note: | + If the GraphQL client request fails (e.g., network error, server error, + or GraphQL errors in the response), `error.type` SHOULD be set to a + low-cardinality error identifier. + + When `graphql.error.code` is available from the response errors, + it SHOULD be used as the `error.type` value. + + If the request completes successfully with no errors, instrumentations + SHOULD NOT set `error.type`. + - ref: server.address + sampling_relevant: true + requirement_level: recommended + brief: > + The domain name or IP address of the GraphQL server being called. + note: > + This SHOULD be the logical server address, not a proxy address. For + federated GraphQL, this is the address of the subgraph service. + - ref: server.port + sampling_relevant: true + requirement_level: + conditionally_required: If not the default port for the scheme (e.g., not 443 for HTTPS). + - ref: graphql.operation.type + requirement_level: + conditionally_required: If available. + - ref: graphql.operation.name + requirement_level: + conditionally_required: If available and not empty. + - ref: graphql.document.hash + requirement_level: recommended + - ref: graphql.document.id + requirement_level: + conditionally_required: If using trusted documents. + + - id: span.graphql.document.parsing.internal + type: span + stability: development + span_kind: internal + brief: > + This span represents the time spent parsing a GraphQL document. + note: | + **Span name** SHOULD be `GraphQL Document Parsing`. + + This span covers the parsing phase of GraphQL request processing, + where the document string is parsed into an abstract syntax tree (AST). + + **Span status** SHOULD be set to `Error` when the processing phase fails. + attributes: + - ref: error.type + requirement_level: + conditionally_required: If the processing phase ended with an error. + note: | + If the processing phase fails, `error.type` SHOULD be set to the + `graphql.error.code` from the error's extensions if available, or to + a low-cardinality error identifier such as the exception type. + + If the processing phase completes successfully, instrumentations + SHOULD NOT set `error.type`. + - ref: graphql.processing.type + requirement_level: required + brief: MUST be `parse`. + - ref: graphql.document.hash + requirement_level: recommended + - ref: graphql.document.id + requirement_level: + conditionally_required: If using trusted documents. + + - id: span.graphql.document.validation.internal + type: span + stability: development + span_kind: internal + brief: > + This span represents the time spent validating a GraphQL document. + note: | + **Span name** SHOULD be `GraphQL Document Validation`. + + This span covers the validation phase of GraphQL request processing, + where the document AST is validated against the GraphQL schema. + + **Span status** SHOULD be set to `Error` when the processing phase fails. + attributes: + - ref: error.type + requirement_level: + conditionally_required: If the processing phase ended with an error. + note: | + If the processing phase fails, `error.type` SHOULD be set to the + `graphql.error.code` from the error's extensions if available, or to + a low-cardinality error identifier such as the exception type. + + If the processing phase completes successfully, instrumentations + SHOULD NOT set `error.type`. + - ref: graphql.processing.type + requirement_level: required + brief: MUST be `validate`. + - ref: graphql.document.hash + requirement_level: recommended + - ref: graphql.document.id + requirement_level: + conditionally_required: If using trusted documents. + + - id: span.graphql.document.variable_coercion.internal + type: span + stability: development + span_kind: internal + brief: > + This span represents the time spent coercing variables for a GraphQL + request. + note: | + **Span name** SHOULD be `GraphQL Variable Coercion`. + + This span covers the variable coercion phase of GraphQL request processing, + where input variables are coerced and validated according to their types. + + **Span status** SHOULD be set to `Error` when the processing phase fails. + attributes: + - ref: error.type + requirement_level: + conditionally_required: If the processing phase ended with an error. + note: | + If the processing phase fails, `error.type` SHOULD be set to the + `graphql.error.code` from the error's extensions if available, or to + a low-cardinality error identifier such as the exception type. + + If the processing phase completes successfully, instrumentations + SHOULD NOT set `error.type`. + - ref: graphql.processing.type + requirement_level: required + brief: MUST be `variable_coercion`. + - ref: graphql.operation.type + requirement_level: required + - ref: graphql.operation.name + requirement_level: recommended + - ref: graphql.document.hash + requirement_level: recommended + - ref: graphql.document.id + requirement_level: + conditionally_required: If using trusted documents. + + - id: span.graphql.operation.planning.internal + type: span + stability: development + span_kind: internal + brief: > + This span represents the time spent planning the execution of a GraphQL + operation. + note: | + **Span name** SHOULD be `GraphQL Operation Planning`. + + This span covers the operation planning phase of GraphQL request processing, + where the server analyzes the validated query and creates an execution plan. + This phase is common in federated GraphQL systems where queries need to be + planned across multiple sources, and in advanced GraphQL servers that + perform query optimization and planning. + + Planning typically involves determining execution order, identifying required + data sources, optimizing data fetching strategies, and preparing the execution + context. + + **Span status** SHOULD be set to `Error` when the processing phase fails. + attributes: + - ref: graphql.processing.type + requirement_level: required + brief: MUST be `plan`. + - ref: error.type + requirement_level: + conditionally_required: If the planning phase ended with an error. + note: | + If the planning phase fails, `error.type` SHOULD be set to the + `graphql.error.code` from the error's extensions if available, or to + a low-cardinality error identifier such as the exception type. + + If the planning phase completes successfully, instrumentations + SHOULD NOT set `error.type`. + - ref: graphql.operation.type + requirement_level: required + - ref: graphql.operation.name + requirement_level: recommended + - ref: graphql.document.hash + requirement_level: recommended + + - id: span.graphql.step.execution.internal + type: span + stability: development + span_kind: internal + brief: > + This span represents the execution of an individual step within a GraphQL + operation execution plan. + note: | + **Span name** SHOULD be `GraphQL Step Execution`. + + This span covers the execution of a single step in the GraphQL execution plan, + where each step typically represents the resolution of one or more fields + that can be executed together. + + Each step execution may involve field resolution, data fetching from external + services, data transformation, and result aggregation. Multiple step execution + spans may run concurrently or sequentially depending on the execution strategy + and field dependencies. + + The `graphql.step.execution` span SHOULD be a descendant of the + `graphql.operation.execution` span. + + Some GraphQL implementations (e.g., plan-based engines like Grafast, + or distributed systems like federation and stitching) replace the classic + resolver-based execution with a plan-based approach where the operation + is broken into steps. In this model, the `graphql.operation.execution` + span still represents the overall execution phase, while `graphql.step.execution` + spans represent individual units of work within that execution - such as + data fetching, transformations, or requests to source systems. + + The `graphql.operation.planning` span, when present, MAY also be + a descendant of `graphql.operation.execution`, depending on whether + the implementation treats planning as part of the execution phase or as a + separate preceding phase. + + **Span status** SHOULD be set to `Error` when the processing phase fails. + attributes: + - ref: graphql.processing.type + requirement_level: required + brief: MUST be `step_execute`. + - ref: error.type + requirement_level: + conditionally_required: If the step execution ended with an error. + note: | + If the step execution fails, `error.type` SHOULD be set to the + `graphql.error.code` from the error's extensions if available, or to + a low-cardinality error identifier such as the exception type. + + If the step execution completes successfully, instrumentations + SHOULD NOT set `error.type`. + - ref: graphql.operation.type + requirement_level: recommended + - ref: graphql.operation.name + requirement_level: recommended + - ref: graphql.document.hash + requirement_level: recommended + - ref: graphql.operation.step.id + requirement_level: required + - ref: graphql.operation.step.kind + requirement_level: recommended + - ref: graphql.operation.step.plan.id + requirement_level: recommended + - ref: graphql.source.name + requirement_level: opt_in + - ref: graphql.source.operation.name + requirement_level: opt_in + - ref: graphql.source.operation.type + requirement_level: opt_in + - ref: graphql.source.operation.hash + requirement_level: opt_in + + - id: span.graphql.operation.execution.internal + type: span + stability: development + span_kind: internal + brief: > + This span represents the execution phase of a GraphQL operation. + note: | + **Span name** SHOULD be `GraphQL Operation Execution`. + + This span covers the whole execution part of a GraphQL request, + including field resolution and result formatting. + + **Span status** SHOULD be set to `Error` when the processing phase fails. + attributes: + - ref: error.type + requirement_level: + conditionally_required: If the processing phase ended with an error. + note: | + If the processing phase fails, `error.type` SHOULD be set to the + `graphql.error.code` from the error's extensions if available, or to + a low-cardinality error identifier such as the exception type. + + If the processing phase completes successfully, instrumentations + SHOULD NOT set `error.type`. + - ref: graphql.processing.type + requirement_level: required + brief: MUST be `execute`. + - ref: graphql.operation.type + requirement_level: required + - ref: graphql.operation.name + requirement_level: recommended + - ref: graphql.document.hash + requirement_level: recommended + - ref: graphql.document.id + requirement_level: + conditionally_required: If using trusted documents. + + - id: span.graphql.field.execution.internal + type: span + stability: development + span_kind: internal + brief: > + This span represents the execution of a GraphQL field. + note: | + **Span name** SHOULD be `{graphql.selection.field.coordinate}`. + + This span covers the execution of an individual field, including both + synchronous and asynchronous resolvers. The span ends when the resolver + result is available. + + > **Warning** + > Creating spans for every resolver execution can result in traces with + > hundreds or thousands of spans, severely impacting performance and + > trace readability. Instrumentations MUST NOT create resolver execution + > spans by default for all resolvers. + + Instrumentations SHOULD provide configuration options to control which + resolvers generate spans. Recommended strategies include: + + - **Manual selection**: Allow developers to explicitly mark specific + resolvers for tracing (e.g., via annotations, decorators, or configuration) + - **Asynchronous resolvers only**: Only trace resolvers that return + promises or other asynchronous constructs + - **Depth-based filtering**: Only trace resolvers at the top N levels + of the query (e.g., top 2 levels) + - **Performance-based filtering**: Only trace resolvers that exceed + a certain execution time threshold + + The selection criteria SHOULD be documented clearly for users to + understand which resolvers will generate spans. + + **Span status** SHOULD be set to `Error` when the resolver throws an + exception or the field resolution results in a GraphQL error. + attributes: + - ref: error.type + requirement_level: + conditionally_required: If the field resolution ended with an error. + note: | + If the resolver throws an exception or results in a GraphQL field error, + `error.type` SHOULD be set to the exception type (its fully-qualified + class name, if applicable) or the `graphql.error.code` if available. + - ref: graphql.processing.type + requirement_level: required + brief: MUST be `resolve`. + - ref: graphql.selection.name + requirement_level: recommended + - ref: graphql.selection.path + requirement_level: recommended + - ref: graphql.selection.field.name + requirement_level: required + - ref: graphql.selection.field.parent_type + requirement_level: required + - ref: graphql.selection.field.coordinate + requirement_level: required + + - id: span.graphql.dataloader.dispatch.internal + type: span + stability: development + span_kind: internal + brief: > + This optional span groups all DataLoader batch operations for a given + request. + note: | + **Span name** SHOULD be `GraphQL DataLoader Dispatch`. + + This span is OPTIONAL. When present, it acts as a grouping span for all + DataLoader batch spans within a single GraphQL request. It represents + the overall dispatch of DataLoader batch operations. + + The span SHOULD be a child of the `graphql.server` span. + + Instrumentations that do not produce this span SHOULD attach DataLoader + batch spans directly to the `graphql.server` span or to the + resolver execution span that triggered the batch dispatch. + attributes: + - ref: graphql.processing.type + requirement_level: required + brief: MUST be `dataloader_dispatch`. + + - id: span.graphql.dataloader.batch.internal + type: span + stability: development + span_kind: internal + brief: > + This span represents the execution of a DataLoader batch operation. + note: | + **Span name** SHOULD be `GraphQL DataLoader Batch {graphql.dataloader.name}` when + the DataLoader has a name, otherwise `GraphQL DataLoader Batch`. + + This span covers the batched execution of multiple individual DataLoader + requests. It represents the time from when the batch is scheduled to + when all results are available. This span is created when a DataLoader + batches multiple individual load requests into a single batch operation + to optimize data access patterns. + + The parent of this span depends on what the instrumentation supports: + - If the `graphql.dataloader.dispatch` span is present, + this span SHOULD be a child of it. + - Otherwise, this span SHOULD be a child of the `graphql.server` + span, or of the resolver execution span that triggered the batch dispatch. + + The span SHOULD have links to all `graphql.field.execution` spans + whose resolvers contributed load requests to this batch, so that + the causal relationship between resolvers and the batch is preserved. + + Each link SHOULD include the following attributes: + - `graphql.selection.field.coordinate`: The coordinate of the field + that triggered the load request (e.g., `User.avatar`). + + When the number of contributing resolvers exceeds a practical limit, + instrumentations MAY cap the number of links to a configurable + maximum. The `graphql.dataloader.batch.size` attribute still + reflects the true batch size regardless of link count. + attributes: + - ref: graphql.processing.type + requirement_level: required + brief: MUST be `dataloader_batch`. + - ref: graphql.dataloader.name + requirement_level: recommended + - ref: graphql.dataloader.batch.size + requirement_level: required + - ref: graphql.dataloader.batch.keys + requirement_level: opt_in + - ref: graphql.dataloader.cache.hit_count + requirement_level: opt_in + - ref: graphql.dataloader.cache.miss_count + requirement_level: opt_in + + - id: span.graphql.subscription.event.internal + type: span + stability: development + span_kind: internal + brief: > + This span represents the processing of a single subscription event. + note: | + **Span name** SHOULD be `GraphQL Subscription Event`. + + This span covers the processing of an individual event delivered to a + GraphQL subscription. Each time the subscription source emits an event, + a new span SHOULD be created to track the execution of the subscription + field selection set against that event. + + The span SHOULD be a child of the `graphql.server` span that initiated + the subscription, if that span is still available. When the originating + server span has ended (as is common with long-lived subscriptions), + instrumentations SHOULD create a new root span or link to the original + subscription span. + + Context propagation for subscriptions: + - The initial subscription request carries context from the client + - Each subscription event SHOULD propagate context from the originating + subscription where possible + - Instrumentations MAY create links to the original subscription span + + **Span status** SHOULD be set to `Error` when the subscription event + processing fails. + attributes: + - ref: error.type + requirement_level: + conditionally_required: If the subscription event processing ended with an error. + note: | + If the subscription event processing fails, `error.type` SHOULD be set to + the `graphql.error.code` from the error's extensions if available, or to + a low-cardinality error identifier such as the exception type. + + If the subscription event completes successfully, instrumentations + SHOULD NOT set `error.type`. + - ref: graphql.processing.type + requirement_level: required + brief: MUST be `execute`. + - ref: graphql.operation.type + requirement_level: required + brief: MUST be `subscription`. + - ref: graphql.operation.name + requirement_level: + conditionally_required: If available and not empty. + - ref: graphql.subscription.id + requirement_level: recommended diff --git a/model/graphql/spans.yml b/model/graphql/spans.yml deleted file mode 100644 index d47b4d90ff..0000000000 --- a/model/graphql/spans.yml +++ /dev/null @@ -1,27 +0,0 @@ -groups: - - id: span.graphql.server - type: span - stability: development - span_kind: server - brief: > - This span represents an incoming operation on a GraphQL server implementation. - note: | - **Span name** SHOULD be of the format `{graphql.operation.type}` provided - `graphql.operation.type` is available. If `graphql.operation.type` is not available, - the span SHOULD be named `GraphQL Operation`. - - > [!WARNING] - > The `graphql.operation.name` value is provided by the client and can have high - > cardinality. Using it in the GraphQL server span name (by default) is - > NOT RECOMMENDED. - > - > Instrumentation MAY provide a configuration option to enable a more descriptive - > span name following `{graphql.operation.type} {graphql.operation.name}` format - > when `graphql.operation.name` is available. - attributes: - - ref: graphql.operation.name - requirement_level: recommended - - ref: graphql.operation.type - requirement_level: recommended - - ref: graphql.document - requirement_level: recommended From 2e2c25344225d19de6e19892192a420a3526c95b Mon Sep 17 00:00:00 2001 From: Pascal Senn Date: Thu, 26 Mar 2026 18:49:57 +0100 Subject: [PATCH 02/10] clenaup --- docs/graphql/graphql-events.md | 1 - docs/graphql/graphql-metrics.md | 226 +++++--------------------------- docs/graphql/graphql-spans.md | 76 ++++++----- model/graphql/metrics.yaml | 83 +++--------- model/graphql/registry.yaml | 31 +---- model/graphql/spans.yaml | 9 +- 6 files changed, 101 insertions(+), 325 deletions(-) diff --git a/docs/graphql/graphql-events.md b/docs/graphql/graphql-events.md index 1fc3c1d878..0f725214af 100644 --- a/docs/graphql/graphql-events.md +++ b/docs/graphql/graphql-events.md @@ -33,7 +33,6 @@ Instrumentations SHOULD set the severity based on the impact of the error. When When a GraphQL error is NOT caused by an exception (e.g., validation errors, authorization errors returned by the GraphQL layer), only the `graphql.error.*` attributes are needed. Instrumentations SHOULD cap the number of error events per span to a configurable maximum (default: 10) to prevent excessive telemetry. When capped, the total error count SHOULD still be recorded via `graphql.error.count` on the parent span. **Relationship to span status and `error.type`:** GraphQL error events provide detailed per-error information from the response `errors` array. They are complementary to span-level error signals: - - `error.type` on the parent span provides a low-cardinality error classification for the overall operation outcome. It SHOULD be set based on the `graphql.error.code` of the first error or a general error category. diff --git a/docs/graphql/graphql-metrics.md b/docs/graphql/graphql-metrics.md index 3053ec0b2a..4baca066df 100644 --- a/docs/graphql/graphql-metrics.md +++ b/docs/graphql/graphql-metrics.md @@ -13,10 +13,7 @@ This document defines semantic conventions for GraphQL client and server metrics - [GraphQL server](#graphql-server) - [Metric: `graphql.server.request.duration`](#metric-graphqlserverrequestduration) - [Metric: `graphql.server.active_requests`](#metric-graphqlserveractive_requests) - - [Metric: `graphql.server.request.parse.duration`](#metric-graphqlserverrequestparseduration) - - [Metric: `graphql.server.request.validate.duration`](#metric-graphqlserverrequestvalidateduration) - - [Metric: `graphql.server.request.execute.duration`](#metric-graphqlserverrequestexecuteduration) - - [Metric: `graphql.server.request.plan.duration`](#metric-graphqlserverrequestplanduration) + - [Metric: `graphql.server.processing.duration`](#metric-graphqlserverprocessingduration) - [Metric: `graphql.server.response.error_count`](#metric-graphqlserverresponseerror_count) - [Metric: `graphql.server.subscription.active`](#metric-graphqlserversubscriptionactive) - [Metric: `graphql.server.subscription.event.duration`](#metric-graphqlserversubscriptioneventduration) @@ -147,160 +144,45 @@ attribute sets on increment and decrement. -### Metric: `graphql.server.request.parse.duration` +### Metric: `graphql.server.processing.duration` - + | Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | | -------- | --------------- | ----------- | -------------- | --------- | ------ | -| `graphql.server.request.parse.duration` | Histogram | `s` | Duration of GraphQL document parsing. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | +| `graphql.server.processing.duration` | Histogram | `s` | Duration of a GraphQL server processing phase. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | -**[1]:** This metric measures the time spent parsing the GraphQL document -string into an AST. When reported alongside a parse span, the -metric value SHOULD match the span duration. +**[1]:** This metric measures the duration of individual processing phases +within a GraphQL server request. The `graphql.processing.type` +attribute identifies which phase is being measured (e.g., parse, +validate, execute, plan). -Histogram bucket boundaries SHOULD be chosen to capture typical -parsing/validation times. - -**Attributes:** - -| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | -| --- | --- | --- | --- | --- | --- | -| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [1] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | -| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | -| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [2] | `FindBookById`; `GetUserProfile` | - -**[1] `error.type`:** For phase-level metrics, `error.type` captures the category of error -that caused the phase to fail. This allows monitoring error rates -per phase independently of the overall request outcome. - -**[2] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have -unbounded cardinality. It MUST only be enabled when the operation -namespace has bounded cardinality, such as when using persisted -operations or trusted documents. - -When `graphql.operation.name` is not enabled, `graphql.document.hash` -or `graphql.document.id` MAY be used as lower-cardinality alternatives. - ---- - -`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. - -| Value | Description | Stability | -| --- | --- | --- | -| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | - ---- - -`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. - -| Value | Description | Stability | -| --- | --- | --- | -| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | -| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | -| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | -| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | - - - - - -### Metric: `graphql.server.request.validate.duration` - - - - - - -| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | -| -------- | --------------- | ----------- | -------------- | --------- | ------ | -| `graphql.server.request.validate.duration` | Histogram | `s` | Duration of GraphQL document validation. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | - -**[1]:** This metric measures the time spent validating the GraphQL document -AST against the schema. When reported alongside a validation span, -the metric value SHOULD match the span duration. - -Histogram bucket boundaries SHOULD be chosen to capture typical -parsing/validation times. - -**Attributes:** - -| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | -| --- | --- | --- | --- | --- | --- | -| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [1] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | -| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | -| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [2] | `FindBookById`; `GetUserProfile` | - -**[1] `error.type`:** For phase-level metrics, `error.type` captures the category of error -that caused the phase to fail. This allows monitoring error rates -per phase independently of the overall request outcome. - -**[2] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have -unbounded cardinality. It MUST only be enabled when the operation -namespace has bounded cardinality, such as when using persisted -operations or trusted documents. - -When `graphql.operation.name` is not enabled, `graphql.document.hash` -or `graphql.document.id` MAY be used as lower-cardinality alternatives. - ---- - -`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. - -| Value | Description | Stability | -| --- | --- | --- | -| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | - ---- - -`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. - -| Value | Description | Stability | -| --- | --- | --- | -| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | -| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | -| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | -| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | - - - - - -### Metric: `graphql.server.request.execute.duration` - - - - - - -| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | -| -------- | --------------- | ----------- | -------------- | --------- | ------ | -| `graphql.server.request.execute.duration` | Histogram | `s` | Duration of GraphQL operation execution. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | - -**[1]:** This metric measures the time spent executing the GraphQL operation, -including field resolution and result formatting. When reported -alongside an execution span, the metric value SHOULD match the span -duration. +When reported alongside a corresponding span, the metric value +SHOULD match the span duration. Histogram bucket boundaries SHOULD be chosen to capture expected -execution durations. +durations for the respective processing phases. **Attributes:** | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | -| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [1] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of processing represented by this span. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | | [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | -| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [2] | `FindBookById`; `GetUserProfile` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [3] | `FindBookById`; `GetUserProfile` | + +**[1] `graphql.processing.type`:** Identifies the processing phase being measured. Common values +include `parse`, `validate`, `execute`, and `plan`. -**[1] `error.type`:** For phase-level metrics, `error.type` captures the category of error +**[2] `error.type`:** For phase-level metrics, `error.type` captures the category of error that caused the phase to fail. This allows monitoring error rates per phase independently of the overall request outcome. -**[2] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have +**[3] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have unbounded cardinality. It MUST only be enabled when the operation namespace has bounded cardinality, such as when using persisted operations or trusted documents. @@ -327,69 +209,23 @@ or `graphql.document.id` MAY be used as lower-cardinality alternatives. | `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | | `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | - - - - -### Metric: `graphql.server.request.plan.duration` - - - - - - -| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | -| -------- | --------------- | ----------- | -------------- | --------- | ------ | -| `graphql.server.request.plan.duration` | Histogram | `s` | Duration of GraphQL operation planning. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | - -**[1]:** This metric measures the time spent planning the execution of a -GraphQL operation. This is relevant for federated GraphQL systems -and advanced servers that perform query optimization. - -This metric is OPTIONAL - it only applies to servers that have -a distinct planning phase. - -Histogram bucket boundaries SHOULD be chosen to capture typical -planning times. - -**Attributes:** - -| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | -| --- | --- | --- | --- | --- | --- | -| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [1] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | -| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | -| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [2] | `FindBookById`; `GetUserProfile` | - -**[1] `error.type`:** For phase-level metrics, `error.type` captures the category of error -that caused the phase to fail. This allows monitoring error rates -per phase independently of the overall request outcome. - -**[2] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have -unbounded cardinality. It MUST only be enabled when the operation -namespace has bounded cardinality, such as when using persisted -operations or trusted documents. - -When `graphql.operation.name` is not enabled, `graphql.document.hash` -or `graphql.document.id` MAY be used as lower-cardinality alternatives. - ---- - -`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. - -| Value | Description | Stability | -| --- | --- | --- | -| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | - --- -`graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. +`graphql.processing.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. | Value | Description | Stability | | --- | --- | --- | -| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | -| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | -| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | -| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | +| `_OTHER` | A fallback for processing types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_batch` | Executing a DataLoader batch operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_dispatch` | Dispatching grouped DataLoader batch operations. | ![Development](https://img.shields.io/badge/-development-blue) | +| `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | +| `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | +| `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | +| `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | +| `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | diff --git a/docs/graphql/graphql-spans.md b/docs/graphql/graphql-spans.md index e397512949..ba79b387d2 100644 --- a/docs/graphql/graphql-spans.md +++ b/docs/graphql/graphql-spans.md @@ -112,21 +112,24 @@ The HTTP server span serves as the parent of all GraphQL operation spans. Each operation span SHOULD have its own `graphql.operation.type` and `graphql.operation.name` attributes. -**Span kind** SHOULD be `SERVER`. +**Span kind** SHOULD be `INTERNAL`. **Attributes:** | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | -| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the GraphQL operation ended with an error. | string | Describes a class of error the operation ended with. [1] | `GRAPHQL_PARSE_FAILED`; `GRAPHQL_VALIDATION_FAILED`; `HC0016`; `java.lang.RuntimeException` | -| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [2] | `aa3e37c1bf54708e93f12c137afba004` | -| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available and not empty. | string | The name of the operation being executed. [3] | `FindBookById`; `GetUserProfile` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `request`. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the GraphQL operation ended with an error. | string | Describes a class of error the operation ended with. [2] | `GRAPHQL_PARSE_FAILED`; `GRAPHQL_VALIDATION_FAILED`; `HC0016`; `java.lang.RuntimeException` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [3] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available and not empty. | string | The name of the operation being executed. [4] | `FindBookById`; `GetUserProfile` | | [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If parsing succeeded | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | -| [`server.port`](/docs/registry/attributes/server.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` [4] | int | Server port number. [5] | `80`; `8080`; `443` | -| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [6] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | -| [`server.address`](/docs/registry/attributes/server.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Recommended` | string | The logical server hostname or IP address serving the GraphQL endpoint. [7] | `example.com`; `10.1.2.80`; `/tmp/my.sock` | +| [`server.port`](/docs/registry/attributes/server.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` [5] | int | Server port number. [6] | `80`; `8080`; `443` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [7] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`server.address`](/docs/registry/attributes/server.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Recommended` | string | The logical server hostname or IP address serving the GraphQL endpoint. [8] | `example.com`; `10.1.2.80`; `/tmp/my.sock` | + +**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. -**[1] `error.type`:** GraphQL errors can occur during various phases of request processing. +**[2] `error.type`:** GraphQL errors can occur during various phases of request processing. If the request fails before execution begins (e.g., parsing, validation, or variable coercion fails), `error.type` SHOULD be set to a low-cardinality @@ -148,18 +151,18 @@ The `error.type` value SHOULD be predictable and SHOULD have low cardinality. Instrumentations SHOULD document the list of errors they report. -**[2] `graphql.document.id`:** This is a hash or identifier of the document provided by the user to identify trusted documents. +**[3] `graphql.document.id`:** This is a hash or identifier of the document provided by the user to identify trusted documents. -**[3] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. +**[4] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. -**[4] `server.port`:** If not the default port for the scheme (e.g., not 443 for HTTPS). +**[5] `server.port`:** If not the default port for the scheme (e.g., not 443 for HTTPS). -**[5] `server.port`:** When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD represent the server port behind any intermediaries, for example proxies, if it's available. +**[6] `server.port`:** When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD represent the server port behind any intermediaries, for example proxies, if it's available. -**[6] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. +**[7] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. When both `graphql.document.hash` and `graphql.document.id` are available, they SHOULD be preferred over transmitting the raw GraphQL document text for telemetry purposes. This reduces payload size and avoids exposing potentially sensitive operation details. -**[7] `server.address`:** This SHOULD be the logical server address, not a proxy or load balancer address. +**[8] `server.address`:** This SHOULD be the logical server address, not a proxy or load balancer address. The following attributes can be important for making sampling decisions and SHOULD be provided **at span creation time** (if provided at all): @@ -185,6 +188,24 @@ and SHOULD be provided **at span creation time** (if provided at all): | `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | | `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | +--- + +`graphql.processing.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback for processing types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_batch` | Executing a DataLoader batch operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `dataloader_dispatch` | Dispatching grouped DataLoader batch operations. | ![Development](https://img.shields.io/badge/-development-blue) | +| `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | +| `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | +| `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | +| `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | +| `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | + @@ -252,6 +273,7 @@ When both `graphql.document.hash` and `graphql.document.id` are available, they | `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | | `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | | `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -324,6 +346,7 @@ When both `graphql.document.hash` and `graphql.document.id` are available, they | `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | | `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | | `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -411,6 +434,7 @@ When both `graphql.document.hash` and `graphql.document.id` are available, they | `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | | `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | | `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -502,6 +526,7 @@ When both `graphql.document.hash` and `graphql.document.id` are available, they | `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | | `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | | `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -589,6 +614,7 @@ When both `graphql.document.hash` and `graphql.document.id` are available, they | `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | | `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | | `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -655,7 +681,6 @@ separate preceding phase. | [`graphql.source.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the source system. [8] | `accounts`; `products`; `reviews` | | [`graphql.source.operation.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | A hash of the GraphQL operation to be executed on the source. [9] | `sha256:abc123`; `md5:def456` | | [`graphql.source.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the GraphQL operation to be executed on the source. [10] | `GetUser`; `FetchProducts`; `ResolveReviews` | -| [`graphql.source.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The type of GraphQL operation to be executed on the source. [11] | `query`; `mutation`; `subscription` | **[1] `graphql.operation.step.id`:** The step identifier is implementation-specific and may be numeric, UUID, or any other format used by the GraphQL execution engine. @@ -684,8 +709,6 @@ The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256: **[10] `graphql.source.operation.name`:** The operation name of the query or mutation that the gateway sends to the source system. -**[11] `graphql.source.operation.type`:** The type of operation that the gateway sends to the source system. This enum matches `graphql.operation.type` for consistency. - --- `error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. @@ -717,22 +740,12 @@ The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256: | `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | | `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | | `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | | `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | ---- - -`graphql.source.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. - -| Value | Description | Stability | -| --- | --- | --- | -| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | -| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | -| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | -| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | - @@ -828,6 +841,7 @@ class name, if applicable) or the `graphql.error.code` if available. | `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | | `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | | `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -884,6 +898,7 @@ resolver execution span that triggered the batch dispatch. | `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | | `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | | `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -914,7 +929,6 @@ batches multiple individual load requests into a single batch operation to optimize data access patterns. The parent of this span depends on what the instrumentation supports: - - If the `graphql.dataloader.dispatch` span is present, this span SHOULD be a child of it. - Otherwise, this span SHOULD be a child of the `graphql.server` @@ -925,7 +939,6 @@ whose resolvers contributed load requests to this batch, so that the causal relationship between resolvers and the batch is preserved. Each link SHOULD include the following attributes: - - `graphql.selection.field.coordinate`: The coordinate of the field that triggered the load request (e.g., `User.avatar`). @@ -973,6 +986,7 @@ reflects the true batch size regardless of link count. | `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | | `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | | `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -1007,7 +1021,6 @@ instrumentations SHOULD create a new root span or link to the original subscription span. Context propagation for subscriptions: - - The initial subscription request carries context from the client - Each subscription event SHOULD propagate context from the originating subscription where possible @@ -1074,6 +1087,7 @@ SHOULD NOT set `error.type`. | `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | | `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | | `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | diff --git a/model/graphql/metrics.yaml b/model/graphql/metrics.yaml index 04dff7cd45..9ab298aa9b 100644 --- a/model/graphql/metrics.yaml +++ b/model/graphql/metrics.yaml @@ -105,85 +105,34 @@ groups: requirement_level: conditionally_required: If available. - - id: metric.graphql.server.request.parse.duration + - id: metric.graphql.server.processing.duration type: metric - metric_name: graphql.server.request.parse.duration + metric_name: graphql.server.processing.duration annotations: code_generation: metric_value_type: double - brief: "Duration of GraphQL document parsing." + brief: "Duration of a GraphQL server processing phase." instrument: histogram unit: "s" stability: development note: | - This metric measures the time spent parsing the GraphQL document - string into an AST. When reported alongside a parse span, the - metric value SHOULD match the span duration. + This metric measures the duration of individual processing phases + within a GraphQL server request. The `graphql.processing.type` + attribute identifies which phase is being measured (e.g., parse, + validate, execute, plan). - Histogram bucket boundaries SHOULD be chosen to capture typical - parsing/validation times. - extends: metric_attributes.graphql.server.phase - - - id: metric.graphql.server.request.validate.duration - type: metric - metric_name: graphql.server.request.validate.duration - annotations: - code_generation: - metric_value_type: double - brief: "Duration of GraphQL document validation." - instrument: histogram - unit: "s" - stability: development - note: | - This metric measures the time spent validating the GraphQL document - AST against the schema. When reported alongside a validation span, - the metric value SHOULD match the span duration. - - Histogram bucket boundaries SHOULD be chosen to capture typical - parsing/validation times. - extends: metric_attributes.graphql.server.phase - - - id: metric.graphql.server.request.execute.duration - type: metric - metric_name: graphql.server.request.execute.duration - annotations: - code_generation: - metric_value_type: double - brief: "Duration of GraphQL operation execution." - instrument: histogram - unit: "s" - stability: development - note: | - This metric measures the time spent executing the GraphQL operation, - including field resolution and result formatting. When reported - alongside an execution span, the metric value SHOULD match the span - duration. + When reported alongside a corresponding span, the metric value + SHOULD match the span duration. Histogram bucket boundaries SHOULD be chosen to capture expected - execution durations. - extends: metric_attributes.graphql.server.phase - - - id: metric.graphql.server.request.plan.duration - type: metric - metric_name: graphql.server.request.plan.duration - annotations: - code_generation: - metric_value_type: double - brief: "Duration of GraphQL operation planning." - instrument: histogram - unit: "s" - stability: development - note: | - This metric measures the time spent planning the execution of a - GraphQL operation. This is relevant for federated GraphQL systems - and advanced servers that perform query optimization. - - This metric is OPTIONAL - it only applies to servers that have - a distinct planning phase. - - Histogram bucket boundaries SHOULD be chosen to capture typical - planning times. + durations for the respective processing phases. extends: metric_attributes.graphql.server.phase + attributes: + - ref: graphql.processing.type + requirement_level: required + note: | + Identifies the processing phase being measured. Common values + include `parse`, `validate`, `execute`, and `plan`. - id: metric.graphql.server.response.error_count type: metric diff --git a/model/graphql/registry.yaml b/model/graphql/registry.yaml index 23a59a2d67..c451cf6153 100644 --- a/model/graphql/registry.yaml +++ b/model/graphql/registry.yaml @@ -43,6 +43,10 @@ groups: stability: development type: members: + - id: request + value: "request" + brief: "Processing the entire GraphQL request." + stability: development - id: parse value: "parse" brief: "Parsing the GraphQL document into an AST." @@ -335,33 +339,6 @@ groups: note: > The operation name of the query or mutation that the gateway sends to the source system. - - id: graphql.source.operation.type - brief: "The type of GraphQL operation to be executed on the source." - stability: development - type: - members: - - id: query - value: "query" - brief: "GraphQL query operation" - stability: development - - id: mutation - value: "mutation" - brief: "GraphQL mutation operation" - stability: development - - id: subscription - value: "subscription" - brief: "GraphQL subscription operation" - stability: development - - id: _OTHER - value: "_OTHER" - brief: - "A fallback for operation types not covered by specific values - in this enum." - stability: development - examples: ["query", "mutation", "subscription"] - note: > - The type of operation that the gateway sends to the source system. - This enum matches `graphql.operation.type` for consistency. - id: graphql.source.operation.hash brief: "A hash of the GraphQL operation to be executed on the source." type: string diff --git a/model/graphql/spans.yaml b/model/graphql/spans.yaml index 69e1d9bcde..7c8c85a0e2 100644 --- a/model/graphql/spans.yaml +++ b/model/graphql/spans.yaml @@ -4,7 +4,7 @@ # The following diagram shows the normative parent-child relationships # between GraphQL spans. Spans marked [optional] are not required. # -# graphql.server (SERVER) +# graphql.server (INTERNAL) # ├── graphql.document.parsing (INTERNAL) # ├── graphql.document.validation (INTERNAL) # ├── graphql.document.variable_coercion (INTERNAL) @@ -23,7 +23,7 @@ groups: - id: span.graphql.server type: span stability: development - span_kind: server + span_kind: internal brief: > This span represents an incoming GraphQL request on a server implementation. @@ -100,6 +100,9 @@ groups: Each operation span SHOULD have its own `graphql.operation.type` and `graphql.operation.name` attributes. attributes: + - ref: graphql.processing.type + requirement_level: required + brief: MUST be `request`. - ref: error.type requirement_level: conditionally_required: If the GraphQL operation ended with an error. @@ -434,8 +437,6 @@ groups: requirement_level: opt_in - ref: graphql.source.operation.name requirement_level: opt_in - - ref: graphql.source.operation.type - requirement_level: opt_in - ref: graphql.source.operation.hash requirement_level: opt_in From 74021a070e2caeecccc5ec55ebacaab5d917c479 Mon Sep 17 00:00:00 2001 From: Pascal Senn Date: Sat, 2 May 2026 18:40:02 +0200 Subject: [PATCH 03/10] Refactor GraphQL error and field attributes for improved clarity and consistency - Updated error attributes in events.yaml to clarify the relationship between GraphQL-specific error details and exception information. - Renamed metrics in metrics.yaml for consistency, changing `active_requests` to `request.active`. - Revised registry.yaml to enhance attribute descriptions and examples, including the addition of `graphql.document.locations` and renaming selection attributes to field attributes. - Improved span definitions in spans.yaml to align with updated field naming conventions and clarified requirements for document IDs and hashes. --- docs/graphql/graphql-events.md | 24 ++-- docs/graphql/graphql-metrics.md | 12 +- docs/graphql/graphql-spans.md | 70 +++++----- docs/registry/attributes/graphql.md | 131 ++++++++---------- model/graphql/events.yaml | 36 +++-- model/graphql/metrics.yaml | 8 +- model/graphql/registry.yaml | 202 ++++++++++++---------------- model/graphql/spans.yaml | 29 ++-- 8 files changed, 243 insertions(+), 269 deletions(-) diff --git a/docs/graphql/graphql-events.md b/docs/graphql/graphql-events.md index 0f725214af..1422d727ec 100644 --- a/docs/graphql/graphql-events.md +++ b/docs/graphql/graphql-events.md @@ -29,9 +29,9 @@ This event describes a GraphQL error that occurred during operation execution. GraphQL errors can occur during various phases of request processing including parsing, validation, and execution. Errors should be recorded on the root span for simplicity and scalability. Multiple errors can be recorded as separate events on the same span. Instrumentations SHOULD set the severity based on the impact of the error. When the response contains both `data` and `errors` (partial success), the severity SHOULD be WARN (severity number 13). When the response contains only `errors` and no `data` (complete failure), the severity SHOULD be ERROR (severity number 17). When severity cannot be determined, instrumentations SHOULD default to ERROR. -**GraphQL errors and exceptions:** When a GraphQL error is caused by an underlying exception, instrumentations SHOULD record both the GraphQL error details and the exception information on the same `graphql.error` event rather than emitting separate events. The exception attributes (`exception.type`, `exception.message`, `exception.stacktrace`) MAY be included alongside the `graphql.error.*` attributes on the same event. This avoids duplicate events for the same underlying issue and keeps the error context together. -When a GraphQL error is NOT caused by an exception (e.g., validation errors, authorization errors returned by the GraphQL layer), only the `graphql.error.*` attributes are needed. -Instrumentations SHOULD cap the number of error events per span to a configurable maximum (default: 10) to prevent excessive telemetry. When capped, the total error count SHOULD still be recorded via `graphql.error.count` on the parent span. +**GraphQL errors and exceptions:** When a GraphQL error is caused by an underlying exception, instrumentations SHOULD record both the GraphQL error details and the exception information on the same `graphql.error` event rather than emitting separate events. The exception attributes (`exception.type`, `exception.message`, `exception.stacktrace`) MAY be included alongside the GraphQL-specific error detail attributes on the same event. This avoids duplicate events for the same underlying issue and keeps the error context together. +When a GraphQL error is NOT caused by an exception (e.g., validation errors, authorization errors returned by the GraphQL layer), only the GraphQL-specific error detail attributes are needed. +Instrumentations SHOULD cap the number of error events per span to a configurable maximum (default: 10) to prevent excessive telemetry. If error events are capped, `graphql.error.count` on the parent span SHOULD reflect the total number of errors rather than the number of emitted events. `graphql.error.count` SHOULD be omitted when the count is 0. **Relationship to span status and `error.type`:** GraphQL error events provide detailed per-error information from the response `errors` array. They are complementary to span-level error signals: - `error.type` on the parent span provides a low-cardinality error classification for the overall operation outcome. It SHOULD be set based on the `graphql.error.code` @@ -48,9 +48,9 @@ Instrumentations SHOULD set both `error.type` on the span and emit error events | [`graphql.error.message`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The error message intended for the developer as a guide to understand and correct the error. [1] | `Cannot query field 'nonExistentField' on type 'User'`; `Variable '$id' of required type 'ID!' was not provided.` | | [`exception.message`](/docs/registry/attributes/exception.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the error was caused by an exception. | string | The exception message. [2] | `Division by zero`; `Can't convert 'int' object to str implicitly` | | [`exception.type`](/docs/registry/attributes/exception.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the error was caused by an exception. | 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. [3] | `java.net.ConnectException`; `OSError` | -| [`graphql.error.locations`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [4] | any | The locations in the GraphQL document associated with the error. [5] | `[{ "line": 3, "column": 7 }, { "line": 5, "column": 4 }]` | -| [`graphql.error.path`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [6] | string | The path of the response field which experienced the error. [7] | `user.friends[0].name`; `findBookById` | -| [`graphql.error.schema_coordinate`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [8] | string | The coordinate of the field in the schema which experienced the error. [9] | `User.friends`; `Query.me` | +| [`graphql.document.locations`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [4] | any | The locations in the GraphQL document associated with an error. [5] | `[{ "line": 3, "column": 7 }, { "line": 5, "column": 4 }]` | +| [`graphql.field.path`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [6] | string | The path of the field that is being resolved. [7] | `person[0].address` | +| [`graphql.field.schema_coordinate`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [8] | string | The schema coordinate of the field that is being resolved. [9] | `Person.address`; `Query.findBookById` | | [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available and not empty. | string | The name of the operation being executed. [10] | `FindBookById`; `GetUserProfile` | | [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The type of the operation being executed. [11] | `query`; `mutation`; `subscription` | | [`exception.stacktrace`](/docs/registry/attributes/exception.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | 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. [12] | `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)` | @@ -63,20 +63,20 @@ Instrumentations SHOULD set both `error.type` on the span and emit error events **[3] `exception.type`:** The fully qualified class name or type of the exception that caused this GraphQL error, when applicable. -**[4] `graphql.error.locations`:** Should be included when the error can be associated with a specific location in the GraphQL document. +**[4] `graphql.document.locations`:** Should be included when the error can be associated with a specific location in the GraphQL document. -**[5] `graphql.error.locations`:** If an error can be associated to a particular point in the requested GraphQL document, it should contain an array of location objects. Each location is a JSON object with the keys `line` and `column`, both positive integers starting from 1, which describe the beginning of an associated syntax element. +**[5] `graphql.document.locations`:** If an error can be associated to a particular point in the requested GraphQL document, it should contain an array of location objects. Each location is a JSON object with the keys `line` and `column`, both positive integers starting from 1, which describe the beginning of an associated syntax element. The value MUST be an array of objects, where each object has the following properties: - `line` (integer, required): The line number in the GraphQL document. - `column` (integer, required): The column number in the GraphQL document. -**[6] `graphql.error.path`:** Must be included when the error can be associated with a particular field in the GraphQL result. +**[6] `graphql.field.path`:** Must be included when the error can be associated with a particular field in the GraphQL result. -**[7] `graphql.error.path`:** If an error can be associated to a particular field in the GraphQL result, it must contain an entry with the key path that details the path of the response field which experienced the error. +**[7] `graphql.field.path`:** The path of the response field which experienced the error. If an error can be associated to a particular field in the GraphQL result, it must contain an entry with the key path that details the path of the response field which experienced the error. This allows clients to identify whether a null result is intentional or caused by a runtime error. The path starts from the root of the response. Field names are separated by dots and list indices are represented using bracket notation. If the error happens in an aliased field, the path should use the aliased name, since it represents a path in the response, not in the request. -**[8] `graphql.error.schema_coordinate`:** Should be included when the error can be associated with a particular field in the GraphQL schema. +**[8] `graphql.field.schema_coordinate`:** Should be included when the error can be associated with a particular field in the GraphQL schema. -**[9] `graphql.error.schema_coordinate`:** If an error can be associated to a particular field in the GraphQL schema, it must contain a schema coordinate in the form of `typeName.fieldName` identifying the field which experienced the error. +**[9] `graphql.field.schema_coordinate`:** The schema coordinate follows the format "{ParentType}.{fieldName}" and uniquely identifies the field within the GraphQL schema. **[10] `graphql.operation.name`:** Including the operation name on error events enables correlation of errors to specific operations, especially useful when error events are processed independently of spans (e.g., in log-based pipelines). diff --git a/docs/graphql/graphql-metrics.md b/docs/graphql/graphql-metrics.md index 4baca066df..ebfc120188 100644 --- a/docs/graphql/graphql-metrics.md +++ b/docs/graphql/graphql-metrics.md @@ -12,7 +12,7 @@ This document defines semantic conventions for GraphQL client and server metrics - [GraphQL server](#graphql-server) - [Metric: `graphql.server.request.duration`](#metric-graphqlserverrequestduration) - - [Metric: `graphql.server.active_requests`](#metric-graphqlserveractive_requests) + - [Metric: `graphql.server.request.active`](#metric-graphqlserverrequestactive) - [Metric: `graphql.server.processing.duration`](#metric-graphqlserverprocessingduration) - [Metric: `graphql.server.response.error_count`](#metric-graphqlserverresponseerror_count) - [Metric: `graphql.server.subscription.active`](#metric-graphqlserversubscriptionactive) @@ -102,16 +102,16 @@ or `graphql.document.id` MAY be used as lower-cardinality alternatives. -### Metric: `graphql.server.active_requests` +### Metric: `graphql.server.request.active` - + | Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | | -------- | --------------- | ----------- | -------------- | --------- | ------ | -| `graphql.server.active_requests` | UpDownCounter | `{request}` | Number of active GraphQL server requests. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | +| `graphql.server.request.active` | UpDownCounter | `{request}` | Number of active GraphQL server requests. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | **[1]:** This metric tracks the number of GraphQL requests currently being processed by the server. It is incremented when processing begins @@ -292,9 +292,9 @@ on the server. It is incremented when a subscription is created and decremented when a subscription is terminated (either by client disconnect or server-side cancellation). -This is distinct from `graphql.server.active_requests`, which tracks +This is distinct from `graphql.server.request.active`, which tracks short-lived request processing (parse → execute → respond). For -subscriptions, `active_requests` only covers the initial setup request, +subscriptions, `request.active` only covers the initial setup request, while this metric tracks the long-lived subscription connection that may remain open for hours or days delivering events. diff --git a/docs/graphql/graphql-spans.md b/docs/graphql/graphql-spans.md index ba79b387d2..37e49808a0 100644 --- a/docs/graphql/graphql-spans.md +++ b/docs/graphql/graphql-spans.md @@ -478,8 +478,9 @@ context. | [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | | [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `plan`. [1] | `parse`; `validate`; `execute`; `resolve` | | [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the planning phase ended with an error. | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | -| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [3] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | -| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the operation being executed. [4] | `FindBookById`; `GetUserProfile` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [3] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [4] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the operation being executed. [5] | `FindBookById`; `GetUserProfile` | **[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. @@ -490,10 +491,12 @@ a low-cardinality error identifier such as the exception type. If the planning phase completes successfully, instrumentations SHOULD NOT set `error.type`. -**[3] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. +**[3] `graphql.document.id`:** This is a hash or identifier of the document provided by the user to identify trusted documents. + +**[4] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. When both `graphql.document.hash` and `graphql.document.id` are available, they SHOULD be preferred over transmitting the raw GraphQL document text for telemetry purposes. This reduces payload size and avoids exposing potentially sensitive operation details. -**[4] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. +**[5] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. --- @@ -673,14 +676,15 @@ separate preceding phase. | [`graphql.operation.step.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The id of the step in the execution plan. [1] | `0`; `1`; `2` | | [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `step_execute`. [2] | `parse`; `validate`; `execute`; `resolve` | | [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the step execution ended with an error. | string | Describes a class of error the operation ended with. [3] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | -| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [4] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | -| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the operation being executed. [5] | `FindBookById`; `GetUserProfile` | -| [`graphql.operation.step.kind`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The kind of step in the execution plan. [6] | `node`; `operation`; `fetch`; `batch` | -| [`graphql.operation.step.plan.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The id of the execution plan this step belongs to. [7] | `plan-1`; `abc-123` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [4] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [5] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the operation being executed. [6] | `FindBookById`; `GetUserProfile` | +| [`graphql.operation.step.kind`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The kind of step in the execution plan. [7] | `node`; `operation`; `fetch`; `batch` | +| [`graphql.operation.step.plan.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The id of the execution plan this step belongs to. [8] | `plan-1`; `abc-123` | | [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | -| [`graphql.source.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the source system. [8] | `accounts`; `products`; `reviews` | -| [`graphql.source.operation.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | A hash of the GraphQL operation to be executed on the source. [9] | `sha256:abc123`; `md5:def456` | -| [`graphql.source.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the GraphQL operation to be executed on the source. [10] | `GetUser`; `FetchProducts`; `ResolveReviews` | +| [`graphql.source_schema.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the source schema. [9] | `accounts`; `products`; `reviews` | +| [`graphql.source_schema.operation.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | A hash of the GraphQL operation to be executed on the source schema. [10] | `sha256:abc123`; `md5:def456` | +| [`graphql.source_schema.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the GraphQL operation to be executed on the source schema. [11] | `GetUser`; `FetchProducts`; `ResolveReviews` | **[1] `graphql.operation.step.id`:** The step identifier is implementation-specific and may be numeric, UUID, or any other format used by the GraphQL execution engine. @@ -693,21 +697,23 @@ a low-cardinality error identifier such as the exception type. If the step execution completes successfully, instrumentations SHOULD NOT set `error.type`. -**[4] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. +**[4] `graphql.document.id`:** This is a hash or identifier of the document provided by the user to identify trusted documents. + +**[5] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. When both `graphql.document.hash` and `graphql.document.id` are available, they SHOULD be preferred over transmitting the raw GraphQL document text for telemetry purposes. This reduces payload size and avoids exposing potentially sensitive operation details. -**[5] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. +**[6] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. -**[6] `graphql.operation.step.kind`:** The step kind describes the type of work performed in this execution step. Values are implementation-specific but common examples include node resolution, fetch operations, and batch processing. +**[7] `graphql.operation.step.kind`:** The step kind describes the type of work performed in this execution step. Values are implementation-specific but common examples include node resolution, fetch operations, and batch processing. -**[7] `graphql.operation.step.plan.id`:** The plan identifier links this step to a specific execution plan, enabling correlation of all steps within the same plan. This is particularly useful in federated GraphQL systems where plans may be cached and reused. +**[8] `graphql.operation.step.plan.id`:** The plan identifier links this step to a specific execution plan, enabling correlation of all steps within the same plan. This is particularly useful in federated GraphQL systems where plans may be cached and reused. -**[8] `graphql.source.name`:** The human-readable name of the downstream source that a distributed GraphQL gateway dispatches to. For example, this could be a subgraph name in a federated system or a stitched schema name. +**[9] `graphql.source_schema.name`:** The human-readable name of the source schema (subgraph) that a distributed GraphQL gateway dispatches to. For example, this could be a subgraph name in a federated system or a stitched schema name. The term "source schema" follows the [GraphQL Composite Schemas Specification](https://github.com/graphql/composite-schemas-spec). -**[9] `graphql.source.operation.hash`:** A hash of the operation document that the gateway sends to the source system. Useful for identifying operations without transmitting the full document. +**[10] `graphql.source_schema.operation.hash`:** A hash of the operation document that the gateway sends to the source schema. Useful for identifying operations without transmitting the full document. The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."), consistent with `graphql.document.hash`. -**[10] `graphql.source.operation.name`:** The operation name of the query or mutation that the gateway sends to the source system. +**[11] `graphql.source_schema.operation.name`:** The operation name of the query or mutation that the gateway sends to the source schema. --- @@ -761,7 +767,7 @@ The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256: This span represents the execution of a GraphQL field. -**Span name** SHOULD be `{graphql.selection.field.coordinate}`. +**Span name** SHOULD be `{graphql.field.schema_coordinate}`. This span covers the execution of an individual field, including both synchronous and asynchronous resolvers. The span ends when the resolver @@ -797,29 +803,29 @@ exception or the field resolution results in a GraphQL error. | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | -| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `resolve`. [1] | `parse`; `validate`; `execute`; `resolve` | -| [`graphql.selection.field.coordinate`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The coordinate of the field that is being resolved. [2] | `Person.address`; `Query.findBookById` | -| [`graphql.selection.field.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The name of the field that is being resolved. [3] | `address`; `name`; `id` | -| [`graphql.selection.field.parent_type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type that declares the field that is being resolved. [4] | `Person`; `Query`; `Mutation` | +| [`graphql.field.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The name of the field that is being resolved. [1] | `address`; `name`; `id` | +| [`graphql.field.parent_type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type that declares the field that is being resolved. [2] | `Person`; `Query`; `Mutation` | +| [`graphql.field.schema_coordinate`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The schema coordinate of the field that is being resolved. [3] | `Person.address`; `Query.findBookById` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `resolve`. [4] | `parse`; `validate`; `execute`; `resolve` | | [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the field resolution ended with an error. | string | Describes a class of error the operation ended with. [5] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | -| [`graphql.selection.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the selection that is being resolved. Either the field name or an alias. [6] | `newAddress`; `bookTitle` | -| [`graphql.selection.path`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The path of the selection that is being resolved. [7] | `person[0].address` | +| [`graphql.field.alias`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The alias of the field that is being resolved. [6] | `newAddress`; `bookTitle` | +| [`graphql.field.path`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The path of the field that is being resolved. [7] | `person[0].address` | -**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. +**[1] `graphql.field.name`:** This is always the actual field name as defined in the schema, not an alias. -**[2] `graphql.selection.field.coordinate`:** The coordinate follows the format "{ParentType}.{fieldName}" and uniquely identifies the field within the GraphQL schema. +**[2] `graphql.field.parent_type`:** This is the GraphQL type name that contains the field definition. -**[3] `graphql.selection.field.name`:** This is always the actual field name as defined in the schema, not an alias. +**[3] `graphql.field.schema_coordinate`:** The schema coordinate follows the format "{ParentType}.{fieldName}" and uniquely identifies the field within the GraphQL schema. -**[4] `graphql.selection.field.parent_type`:** This is the GraphQL type name that contains the field definition. +**[4] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. **[5] `error.type`:** If the resolver throws an exception or results in a GraphQL field error, `error.type` SHOULD be set to the exception type (its fully-qualified class name, if applicable) or the `graphql.error.code` if available. -**[6] `graphql.selection.name`:** If the field has an alias, this SHOULD be the alias name. Otherwise, it SHOULD be the field name. +**[6] `graphql.field.alias`:** If the field has an alias, this SHOULD be the alias name. Otherwise, it SHOULD be the field name. -**[7] `graphql.selection.path`:** The path represents the location of the field being resolved within the result structure. Therefore, if a field is aliased, the path will use the alias name instead of the actual field name. +**[7] `graphql.field.path`:** The path represents the location of the field being resolved within the result structure. Therefore, if a field is aliased, the path will use the alias name instead of the actual field name. --- @@ -939,7 +945,7 @@ whose resolvers contributed load requests to this batch, so that the causal relationship between resolvers and the batch is preserved. Each link SHOULD include the following attributes: -- `graphql.selection.field.coordinate`: The coordinate of the field +- `graphql.field.schema_coordinate`: The schema coordinate of the field that triggered the load request (e.g., `User.avatar`). When the number of contributing resolvers exceeds a practical limit, diff --git a/docs/registry/attributes/graphql.md b/docs/registry/attributes/graphql.md index a819320acd..fa97c2d7ab 100644 --- a/docs/registry/attributes/graphql.md +++ b/docs/registry/attributes/graphql.md @@ -6,9 +6,9 @@ - [GraphQL Attributes](#graphql-attributes) - [GraphQL DataLoader Attributes](#graphql-dataloader-attributes) - [GraphQL Error Attributes](#graphql-error-attributes) +- [GraphQL Field Attributes](#graphql-field-attributes) - [GraphQL Operation Attributes](#graphql-operation-attributes) -- [GraphQL Selection Attributes](#graphql-selection-attributes) -- [GraphQL Source Attributes](#graphql-source-attributes) +- [GraphQL Source Schema Attributes](#graphql-source-schema-attributes) - [GraphQL Subscription Attributes](#graphql-subscription-attributes) ## GraphQL Attributes @@ -21,18 +21,22 @@ This document defines attributes for GraphQL operations and resolvers. | --- | --- | --- | --- | --- | | `graphql.document.hash` | ![Development](https://img.shields.io/badge/-development-blue) | string | The hash of the operation document. [1] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | | `graphql.document.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | The document identifier for trusted documents. [2] | `aa3e37c1bf54708e93f12c137afba004` | -| `graphql.operation.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the operation being executed. [3] | `FindBookById`; `GetUserProfile` | +| `graphql.document.locations` | ![Development](https://img.shields.io/badge/-development-blue) | any | The locations in the GraphQL document associated with an error. [3] | `[{ "line": 3, "column": 7 }, { "line": 5, "column": 4 }]` | +| `graphql.operation.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the operation being executed. [4] | `FindBookById`; `GetUserProfile` | | `graphql.operation.type` | ![Development](https://img.shields.io/badge/-development-blue) | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | -| `graphql.processing.type` | ![Development](https://img.shields.io/badge/-development-blue) | string | The type of processing represented by this span. [4] | `parse`; `validate`; `execute`; `resolve` | +| `graphql.processing.type` | ![Development](https://img.shields.io/badge/-development-blue) | string | The type of processing represented by this span. [5] | `parse`; `validate`; `execute`; `resolve` | **[1] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. When both `graphql.document.hash` and `graphql.document.id` are available, they SHOULD be preferred over transmitting the raw GraphQL document text for telemetry purposes. This reduces payload size and avoids exposing potentially sensitive operation details. **[2] `graphql.document.id`:** This is a hash or identifier of the document provided by the user to identify trusted documents. -**[3] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. +**[3] `graphql.document.locations`:** If an error can be associated to a particular point in the requested GraphQL document, it should contain an array of location objects. Each location is a JSON object with the keys `line` and `column`, both positive integers starting from 1, which describe the beginning of an associated syntax element. +The value MUST be an array of objects, where each object has the following properties: - `line` (integer, required): The line number in the GraphQL document. - `column` (integer, required): The column number in the GraphQL document. + +**[4] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. -**[4] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. +**[5] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. --- @@ -57,6 +61,7 @@ When both `graphql.document.hash` and `graphql.document.id` are available, they | `execute` | Executing a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | | `parse` | Parsing the GraphQL document into an AST. | ![Development](https://img.shields.io/badge/-development-blue) | | `plan` | Planning the execution of a GraphQL operation. | ![Development](https://img.shields.io/badge/-development-blue) | +| `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -70,21 +75,21 @@ This document defines attributes for GraphQL DataLoader operations. | Key | Stability | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | -| `graphql.dataloader.batch.keys` | ![Development](https://img.shields.io/badge/-development-blue) | string[] | A subset of the keys requested in the DataLoader batch. [5] | `["user:1", "user:2", "user:3"]`; `["post:42", "post:99"]` | -| `graphql.dataloader.batch.size` | ![Development](https://img.shields.io/badge/-development-blue) | int | The number of individual requests in the DataLoader batch. [6] | `5`; `12`; `25` | -| `graphql.dataloader.cache.hit_count` | ![Development](https://img.shields.io/badge/-development-blue) | int | The number of requests in the batch that were served from the DataLoader cache. [7] | `0`; `3`; `10` | -| `graphql.dataloader.cache.miss_count` | ![Development](https://img.shields.io/badge/-development-blue) | int | The number of requests in the batch that required fetching. [8] | `2`; `5`; `12` | -| `graphql.dataloader.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the DataLoader instance. [9] | `UserLoader`; `ProductByIdLoader`; `CommentsByPostIdLoader` | +| `graphql.dataloader.batch.keys` | ![Development](https://img.shields.io/badge/-development-blue) | string[] | A subset of the keys requested in the DataLoader batch. [6] | `["user:1", "user:2", "user:3"]`; `["post:42", "post:99"]` | +| `graphql.dataloader.batch.size` | ![Development](https://img.shields.io/badge/-development-blue) | int | The number of individual requests in the DataLoader batch. [7] | `5`; `12`; `25` | +| `graphql.dataloader.cache.hit_count` | ![Development](https://img.shields.io/badge/-development-blue) | int | The number of requests in the batch that were served from the DataLoader cache. [8] | `0`; `3`; `10` | +| `graphql.dataloader.cache.miss_count` | ![Development](https://img.shields.io/badge/-development-blue) | int | The number of requests in the batch that required fetching. [9] | `2`; `5`; `12` | +| `graphql.dataloader.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the DataLoader instance. [10] | `UserLoader`; `ProductByIdLoader`; `CommentsByPostIdLoader` | -**[5] `graphql.dataloader.batch.keys`:** This attribute is opt-in and SHOULD NOT be enabled by default, as keys may contain sensitive or high-cardinality data. When enabled, implementations MAY truncate the list to a configurable maximum number of keys. The string representation of each key depends on the DataLoader implementation. +**[6] `graphql.dataloader.batch.keys`:** This attribute is opt-in and SHOULD NOT be enabled by default, as keys may contain sensitive or high-cardinality data. When enabled, implementations MAY truncate the list to a configurable maximum number of keys. The string representation of each key depends on the DataLoader implementation. -**[6] `graphql.dataloader.batch.size`:** This represents the total number of individual load requests that were batched together in a single batch operation. This includes both cache hits and cache misses. +**[7] `graphql.dataloader.batch.size`:** This represents the total number of individual load requests that were batched together in a single batch operation. This includes both cache hits and cache misses. -**[7] `graphql.dataloader.cache.hit_count`:** This represents the number of individual load requests that were resolved from the DataLoader's cache without requiring a fetch. +**[8] `graphql.dataloader.cache.hit_count`:** This represents the number of individual load requests that were resolved from the DataLoader's cache without requiring a fetch. -**[8] `graphql.dataloader.cache.miss_count`:** This represents the number of individual load requests that were not found in the DataLoader's cache and required a fetch operation. +**[9] `graphql.dataloader.cache.miss_count`:** This represents the number of individual load requests that were not found in the DataLoader's cache and required a fetch operation. -**[9] `graphql.dataloader.name`:** This represents the name or identifier of the DataLoader instance. When the DataLoader implementation supports naming, this SHOULD be set. This helps in identifying specific DataLoader instances in observability. +**[10] `graphql.dataloader.name`:** This represents the name or identifier of the DataLoader instance. When the DataLoader implementation supports naming, this SHOULD be set. This helps in identifying specific DataLoader instances in observability. ## GraphQL Error Attributes @@ -94,100 +99,74 @@ This document defines the shared attributes used to report GraphQL errors associ | Key | Stability | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | -| `graphql.error.code` | ![Development](https://img.shields.io/badge/-development-blue) | string | An optional error code from the extensions field. [10] | `GRAPHQL_VALIDATION_FAILED`; `UNAUTHENTICATED`; `HC00116` | -| `graphql.error.locations` | ![Development](https://img.shields.io/badge/-development-blue) | any | The locations in the GraphQL document associated with the error. [11] | `[{ "line": 3, "column": 7 }, { "line": 5, "column": 4 }]` | +| `graphql.error.code` | ![Development](https://img.shields.io/badge/-development-blue) | string | An optional error code from the extensions field. [11] | `GRAPHQL_VALIDATION_FAILED`; `UNAUTHENTICATED`; `HC00116` | | `graphql.error.message` | ![Development](https://img.shields.io/badge/-development-blue) | string | The error message intended for the developer as a guide to understand and correct the error. [12] | `Cannot query field 'nonExistentField' on type 'User'`; `Variable '$id' of required type 'ID!' was not provided.` | -| `graphql.error.path` | ![Development](https://img.shields.io/badge/-development-blue) | string | The path of the response field which experienced the error. [13] | `user.friends[0].name`; `findBookById` | -| `graphql.error.schema_coordinate` | ![Development](https://img.shields.io/badge/-development-blue) | string | The coordinate of the field in the schema which experienced the error. [14] | `User.friends`; `Query.me` | -**[10] `graphql.error.code`:** This is an optional field that can be used to categorize errors. The error extension code is a recommended way to categorize errors for easier filtering and monitoring. - -**[11] `graphql.error.locations`:** If an error can be associated to a particular point in the requested GraphQL document, it should contain an array of location objects. Each location is a JSON object with the keys `line` and `column`, both positive integers starting from 1, which describe the beginning of an associated syntax element. -The value MUST be an array of objects, where each object has the following properties: - `line` (integer, required): The line number in the GraphQL document. - `column` (integer, required): The column number in the GraphQL document. +**[11] `graphql.error.code`:** This is an optional field that can be used to categorize errors. The error extension code is a recommended way to categorize errors for easier filtering and monitoring. **[12] `graphql.error.message`:** Every error must contain an entry with the key message with a string description of the error intended for the developer as a guide to understand and correct the error. > **Warning** > This attribute has unbounded cardinality and MUST NOT be used as a metric > dimension. It is intended for span events and log records only. -**[13] `graphql.error.path`:** If an error can be associated to a particular field in the GraphQL result, it must contain an entry with the key path that details the path of the response field which experienced the error. -This allows clients to identify whether a null result is intentional or caused by a runtime error. -The path starts from the root of the response. Field names are separated by dots and list indices are represented using bracket notation. If the error happens in an aliased field, the path should use the aliased name, since it represents a path in the response, not in the request. +## GraphQL Field Attributes -**[14] `graphql.error.schema_coordinate`:** If an error can be associated to a particular field in the GraphQL schema, it must contain a schema coordinate in the form of `typeName.fieldName` identifying the field which experienced the error. - -## GraphQL Operation Attributes - -This document defines attributes for GraphQL operation execution steps and planning. +Attributes for GraphQL fields **Attributes:** | Key | Stability | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | -| `graphql.operation.step.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | The id of the step in the execution plan. [15] | `0`; `1`; `2` | -| `graphql.operation.step.kind` | ![Development](https://img.shields.io/badge/-development-blue) | string | The kind of step in the execution plan. [16] | `node`; `operation`; `fetch`; `batch` | -| `graphql.operation.step.plan.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | The id of the execution plan this step belongs to. [17] | `plan-1`; `abc-123` | +| `graphql.field.alias` | ![Development](https://img.shields.io/badge/-development-blue) | string | The alias of the field that is being resolved. [13] | `newAddress`; `bookTitle` | +| `graphql.field.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the field that is being resolved. [14] | `address`; `name`; `id` | +| `graphql.field.parent_type` | ![Development](https://img.shields.io/badge/-development-blue) | string | The type that declares the field that is being resolved. [15] | `Person`; `Query`; `Mutation` | +| `graphql.field.path` | ![Development](https://img.shields.io/badge/-development-blue) | string | The path of the field that is being resolved. [16] | `person[0].address` | +| `graphql.field.schema_coordinate` | ![Development](https://img.shields.io/badge/-development-blue) | string | The schema coordinate of the field that is being resolved. [17] | `Person.address`; `Query.findBookById` | + +**[13] `graphql.field.alias`:** If the field has an alias, this SHOULD be the alias name. Otherwise, it SHOULD be the field name. + +**[14] `graphql.field.name`:** This is always the actual field name as defined in the schema, not an alias. -**[15] `graphql.operation.step.id`:** The step identifier is implementation-specific and may be numeric, UUID, or any other format used by the GraphQL execution engine. +**[15] `graphql.field.parent_type`:** This is the GraphQL type name that contains the field definition. -**[16] `graphql.operation.step.kind`:** The step kind describes the type of work performed in this execution step. Values are implementation-specific but common examples include node resolution, fetch operations, and batch processing. +**[16] `graphql.field.path`:** The path represents the location of the field being resolved within the result structure. Therefore, if a field is aliased, the path will use the alias name instead of the actual field name. -**[17] `graphql.operation.step.plan.id`:** The plan identifier links this step to a specific execution plan, enabling correlation of all steps within the same plan. This is particularly useful in federated GraphQL systems where plans may be cached and reused. +**[17] `graphql.field.schema_coordinate`:** The schema coordinate follows the format "{ParentType}.{fieldName}" and uniquely identifies the field within the GraphQL schema. -## GraphQL Selection Attributes +## GraphQL Operation Attributes -Attributes for GraphQL field selections and resolver execution. +This document defines attributes for GraphQL operation execution steps and planning. **Attributes:** | Key | Stability | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | -| `graphql.selection.field.coordinate` | ![Development](https://img.shields.io/badge/-development-blue) | string | The coordinate of the field that is being resolved. [18] | `Person.address`; `Query.findBookById` | -| `graphql.selection.field.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the field that is being resolved. [19] | `address`; `name`; `id` | -| `graphql.selection.field.parent_type` | ![Development](https://img.shields.io/badge/-development-blue) | string | The type that declares the field that is being resolved. [20] | `Person`; `Query`; `Mutation` | -| `graphql.selection.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the selection that is being resolved. Either the field name or an alias. [21] | `newAddress`; `bookTitle` | -| `graphql.selection.path` | ![Development](https://img.shields.io/badge/-development-blue) | string | The path of the selection that is being resolved. [22] | `person[0].address` | - -**[18] `graphql.selection.field.coordinate`:** The coordinate follows the format "{ParentType}.{fieldName}" and uniquely identifies the field within the GraphQL schema. +| `graphql.operation.step.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | The id of the step in the execution plan. [18] | `0`; `1`; `2` | +| `graphql.operation.step.kind` | ![Development](https://img.shields.io/badge/-development-blue) | string | The kind of step in the execution plan. [19] | `node`; `operation`; `fetch`; `batch` | +| `graphql.operation.step.plan.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | The id of the execution plan this step belongs to. [20] | `plan-1`; `abc-123` | -**[19] `graphql.selection.field.name`:** This is always the actual field name as defined in the schema, not an alias. +**[18] `graphql.operation.step.id`:** The step identifier is implementation-specific and may be numeric, UUID, or any other format used by the GraphQL execution engine. -**[20] `graphql.selection.field.parent_type`:** This is the GraphQL type name that contains the field definition. +**[19] `graphql.operation.step.kind`:** The step kind describes the type of work performed in this execution step. Values are implementation-specific but common examples include node resolution, fetch operations, and batch processing. -**[21] `graphql.selection.name`:** If the field has an alias, this SHOULD be the alias name. Otherwise, it SHOULD be the field name. +**[20] `graphql.operation.step.plan.id`:** The plan identifier links this step to a specific execution plan, enabling correlation of all steps within the same plan. This is particularly useful in federated GraphQL systems where plans may be cached and reused. -**[22] `graphql.selection.path`:** The path represents the location of the field being resolved within the result structure. Therefore, if a field is aliased, the path will use the alias name instead of the actual field name. +## GraphQL Source Schema Attributes -## GraphQL Source Attributes - -This document defines attributes for GraphQL source systems in distributed GraphQL architectures. +This document defines attributes for GraphQL source schemas (subgraphs) in distributed GraphQL architectures. **Attributes:** | Key | Stability | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | -| `graphql.source.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the source system. [23] | `accounts`; `products`; `reviews` | -| `graphql.source.operation.hash` | ![Development](https://img.shields.io/badge/-development-blue) | string | A hash of the GraphQL operation to be executed on the source. [24] | `sha256:abc123`; `md5:def456` | -| `graphql.source.operation.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the GraphQL operation to be executed on the source. [25] | `GetUser`; `FetchProducts`; `ResolveReviews` | -| `graphql.source.operation.type` | ![Development](https://img.shields.io/badge/-development-blue) | string | The type of GraphQL operation to be executed on the source. [26] | `query`; `mutation`; `subscription` | +| `graphql.source_schema.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the source schema. [21] | `accounts`; `products`; `reviews` | +| `graphql.source_schema.operation.hash` | ![Development](https://img.shields.io/badge/-development-blue) | string | A hash of the GraphQL operation to be executed on the source schema. [22] | `sha256:abc123`; `md5:def456` | +| `graphql.source_schema.operation.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the GraphQL operation to be executed on the source schema. [23] | `GetUser`; `FetchProducts`; `ResolveReviews` | -**[23] `graphql.source.name`:** The human-readable name of the downstream source that a distributed GraphQL gateway dispatches to. For example, this could be a subgraph name in a federated system or a stitched schema name. +**[21] `graphql.source_schema.name`:** The human-readable name of the source schema (subgraph) that a distributed GraphQL gateway dispatches to. For example, this could be a subgraph name in a federated system or a stitched schema name. The term "source schema" follows the [GraphQL Composite Schemas Specification](https://github.com/graphql/composite-schemas-spec). -**[24] `graphql.source.operation.hash`:** A hash of the operation document that the gateway sends to the source system. Useful for identifying operations without transmitting the full document. +**[22] `graphql.source_schema.operation.hash`:** A hash of the operation document that the gateway sends to the source schema. Useful for identifying operations without transmitting the full document. The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."), consistent with `graphql.document.hash`. -**[25] `graphql.source.operation.name`:** The operation name of the query or mutation that the gateway sends to the source system. - -**[26] `graphql.source.operation.type`:** The type of operation that the gateway sends to the source system. This enum matches `graphql.operation.type` for consistency. - ---- - -`graphql.source.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. - -| Value | Description | Stability | -| --- | --- | --- | -| `_OTHER` | A fallback for operation types not covered by specific values in this enum. | ![Development](https://img.shields.io/badge/-development-blue) | -| `mutation` | GraphQL mutation operation | ![Development](https://img.shields.io/badge/-development-blue) | -| `query` | GraphQL query operation | ![Development](https://img.shields.io/badge/-development-blue) | -| `subscription` | GraphQL subscription operation | ![Development](https://img.shields.io/badge/-development-blue) | +**[23] `graphql.source_schema.operation.name`:** The operation name of the query or mutation that the gateway sends to the source schema. ## GraphQL Subscription Attributes @@ -197,6 +176,6 @@ Attributes for GraphQL subscription operations. | Key | Stability | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | -| `graphql.subscription.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | A unique identifier for the subscription instance. [27] | `sub-abc123`; `ws-conn-42-sub-1` | +| `graphql.subscription.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | A unique identifier for the subscription instance. [24] | `sub-abc123`; `ws-conn-42-sub-1` | -**[27] `graphql.subscription.id`:** This identifier tracks a specific subscription instance throughout its lifecycle. It SHOULD be unique within the scope of the server and can be used to correlate subscription creation, events, and termination. +**[24] `graphql.subscription.id`:** This identifier tracks a specific subscription instance throughout its lifecycle. It SHOULD be unique within the scope of the server and can be used to correlate subscription creation, events, and termination. diff --git a/model/graphql/events.yaml b/model/graphql/events.yaml index ad5f071e87..2b5d9c8c99 100644 --- a/model/graphql/events.yaml +++ b/model/graphql/events.yaml @@ -23,17 +23,19 @@ groups: SHOULD record both the GraphQL error details and the exception information on the same `graphql.error` event rather than emitting separate events. The exception attributes (`exception.type`, `exception.message`, - `exception.stacktrace`) MAY be included alongside the `graphql.error.*` - attributes on the same event. This avoids duplicate events for the same - underlying issue and keeps the error context together. + `exception.stacktrace`) MAY be included alongside the GraphQL-specific + error detail attributes on the same event. This avoids duplicate events + for the same underlying issue and keeps the error context together. When a GraphQL error is NOT caused by an exception (e.g., validation errors, - authorization errors returned by the GraphQL layer), only the `graphql.error.*` - attributes are needed. + authorization errors returned by the GraphQL layer), only the + GraphQL-specific error detail attributes are needed. Instrumentations SHOULD cap the number of error events per span to a configurable - maximum (default: 10) to prevent excessive telemetry. When capped, the total error - count SHOULD still be recorded via `graphql.error.count` on the parent span. + maximum (default: 10) to prevent excessive telemetry. If error events are capped, + `graphql.error.count` on the parent span SHOULD reflect the total number of + errors rather than the number of emitted events. `graphql.error.count` SHOULD be + omitted when the count is 0. **Relationship to span status and `error.type`:** GraphQL error events provide detailed per-error information from the response `errors` @@ -51,12 +53,26 @@ groups: attributes: - ref: graphql.error.message requirement_level: required - - ref: graphql.error.locations + - ref: graphql.document.locations requirement_level: conditionally_required: Should be included when the error can be associated with a specific location in the GraphQL document. - - ref: graphql.error.path + - ref: graphql.field.path requirement_level: conditionally_required: Must be included when the error can be associated with a particular field in the GraphQL result. + note: > + The path of the response field which experienced the error. If an + error can be associated to a particular field in the GraphQL result, + it must contain an entry with the key path that details the path of + the response field which experienced the error. + + This allows clients to identify whether a null result is intentional + or caused by a runtime error. + + The path starts from the root of the response. Field names are + separated by dots and list indices are represented using bracket + notation. If the error happens in an aliased field, the path should + use the aliased name, since it represents a path in the response, not + in the request. - ref: graphql.error.code requirement_level: opt_in - ref: exception.type @@ -79,7 +95,7 @@ groups: The exception stacktrace, when available and the error was caused by an exception. This attribute is opt-in due to its size and potential to contain sensitive information. - - ref: graphql.error.schema_coordinate + - ref: graphql.field.schema_coordinate requirement_level: conditionally_required: Should be included when the error can be associated with a particular field in the GraphQL schema. - ref: graphql.operation.type diff --git a/model/graphql/metrics.yaml b/model/graphql/metrics.yaml index 9ab298aa9b..5c58d3777c 100644 --- a/model/graphql/metrics.yaml +++ b/model/graphql/metrics.yaml @@ -80,9 +80,9 @@ groups: request durations. extends: metric_attributes.graphql.server - - id: metric.graphql.server.active_requests + - id: metric.graphql.server.request.active type: metric - metric_name: graphql.server.active_requests + metric_name: graphql.server.request.active annotations: code_generation: metric_value_type: int @@ -177,9 +177,9 @@ groups: decremented when a subscription is terminated (either by client disconnect or server-side cancellation). - This is distinct from `graphql.server.active_requests`, which tracks + This is distinct from `graphql.server.request.active`, which tracks short-lived request processing (parse → execute → respond). For - subscriptions, `active_requests` only covers the initial setup request, + subscriptions, `request.active` only covers the initial setup request, while this metric tracks the long-lived subscription connection that may remain open for hours or days delivering events. attributes: diff --git a/model/graphql/registry.yaml b/model/graphql/registry.yaml index c451cf6153..9728d84227 100644 --- a/model/graphql/registry.yaml +++ b/model/graphql/registry.yaml @@ -9,7 +9,7 @@ groups: brief: "The name of the operation being executed." type: string stability: development - examples: ["FindBookById", "GetUserProfile"] + examples: [ "FindBookById", "GetUserProfile" ] note: > This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this @@ -33,11 +33,10 @@ groups: stability: development - id: _OTHER value: "_OTHER" - brief: - "A fallback for operation types not covered by specific values - in this enum." + brief: "A fallback for operation types not covered by specific values in this + enum." stability: development - examples: ["query", "mutation", "subscription"] + examples: [ "query", "mutation", "subscription" ] - id: graphql.processing.type brief: "The type of processing represented by this span." stability: development @@ -85,11 +84,10 @@ groups: stability: development - id: _OTHER value: "_OTHER" - brief: - "A fallback for processing types not covered by specific values - in this enum." + brief: "A fallback for processing types not covered by specific values in this + enum." stability: development - examples: ["parse", "validate", "execute", "resolve"] + examples: [ "parse", "validate", "execute", "resolve" ] note: > This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span @@ -100,7 +98,8 @@ groups: stability: development examples: [ - "sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c", + "sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f87\ + 3d8664c", ] note: > The hash algorithm used SHOULD be specified as part of the value @@ -118,60 +117,77 @@ groups: The document identifier for trusted documents. type: string stability: development - examples: ["aa3e37c1bf54708e93f12c137afba004"] + examples: [ "aa3e37c1bf54708e93f12c137afba004" ] note: > This is a hash or identifier of the document provided by the user to identify trusted documents. + - id: graphql.document.locations + type: any + stability: development + brief: > + The locations in the GraphQL document associated with an error. + examples: + - "[{ \"line\": 3, \"column\": 7 }, { \"line\": 5, \"column\": 4 }]" + note: > + If an error can be associated to a particular point in the requested + GraphQL document, it should contain an array of location objects. Each + location is a JSON object with the keys `line` and `column`, both + positive integers starting from 1, which describe the beginning of an + associated syntax element. + + The value MUST be an array of objects, where each object has the + following properties: - `line` (integer, required): The line number in + the GraphQL document. - `column` (integer, required): The column + number in the GraphQL document. - - id: registry.graphql.selection + - id: registry.graphql.field type: attribute_group stability: development - display_name: GraphQL Selection Attributes + display_name: GraphQL Field Attributes brief: > - Attributes for GraphQL field selections and resolver execution. + Attributes for GraphQL fields attributes: - - id: graphql.selection.name - brief: - "The name of the selection that is being resolved. Either the field - name or an alias." + - id: graphql.field.alias + brief: "The alias of the field that is being resolved." type: string stability: development - examples: ["newAddress", "bookTitle"] + examples: [ "newAddress", "bookTitle" ] note: > If the field has an alias, this SHOULD be the alias name. Otherwise, it SHOULD be the field name. - - id: graphql.selection.path - brief: "The path of the selection that is being resolved." + - id: graphql.field.path + brief: "The path of the field that is being resolved." type: string stability: development - examples: ["person[0].address"] + examples: [ "person[0].address" ] note: > The path represents the location of the field being resolved within the result structure. Therefore, if a field is aliased, the path will use the alias name instead of the actual field name. - - id: graphql.selection.field.name + - id: graphql.field.name brief: "The name of the field that is being resolved." type: string stability: development - examples: ["address", "name", "id"] + examples: [ "address", "name", "id" ] note: > This is always the actual field name as defined in the schema, not an alias. - - id: graphql.selection.field.parent_type + - id: graphql.field.parent_type brief: "The type that declares the field that is being resolved." type: string stability: development - examples: ["Person", "Query", "Mutation"] + examples: [ "Person", "Query", "Mutation" ] note: > This is the GraphQL type name that contains the field definition. - - id: graphql.selection.field.coordinate - brief: "The coordinate of the field that is being resolved." + - id: graphql.field.schema_coordinate + brief: "The schema coordinate of the field that is being resolved." type: string stability: development - examples: ["Person.address", "Query.findBookById"] + examples: [ "Person.address", "Query.findBookById" ] note: > - The coordinate follows the format "{ParentType}.{fieldName}" and - uniquely identifies the field within the GraphQL schema. + The schema coordinate follows the format + "{ParentType}.{fieldName}" and uniquely identifies the field within + the GraphQL schema. - id: registry.graphql.error type: attribute_group @@ -181,16 +197,16 @@ groups: This document defines the shared attributes used to report GraphQL errors associated with a span or event. note: > - The `graphql.error.*` attributes capture structured details from the - GraphQL response `errors` array. These are complementary to the standard - `error.type` attribute: + The `graphql.error.*` attributes capture structured error details from + the GraphQL response `errors` array. These are complementary to the + standard `error.type` attribute: - `error.type` provides a low-cardinality error classification used for span status and metric dimensions (e.g., `GRAPHQL_VALIDATION_FAILED`). - - `graphql.error.*` attributes provide the full structured error details - from the - GraphQL response (message, path, locations, code, etc.). + - GraphQL-specific attributes provide the full structured error details + from the GraphQL response (message, document locations, field path, schema + coordinate, code, etc.). When `graphql.error.code` is available, it SHOULD be used as the `error.type` value. See span definitions for detailed `error.type` @@ -216,67 +232,17 @@ groups: be used as a metric > dimension. It is intended for span events and log records only. - - id: graphql.error.locations - type: any - stability: development - brief: > - The locations in the GraphQL document associated with the error. - examples: - - '[{ "line": 3, "column": 7 }, { "line": 5, "column": 4 }]' - note: > - If an error can be associated to a particular point in the requested - GraphQL document, it should contain an array of location objects. Each - location is a JSON object with the keys `line` and `column`, both - positive integers starting from 1, which describe the beginning of an - associated syntax element. - - The value MUST be an array of objects, where each object has the following properties: - - `line` (integer, required): The line number in the GraphQL document. - - `column` (integer, required): The column number in the GraphQL document. - - - id: graphql.error.path - type: string - stability: development - brief: > - The path of the response field which experienced the error. - examples: ["user.friends[0].name", "findBookById"] - note: > - If an error can be associated to a particular field in the GraphQL - result, it must contain an entry with the key path that details the - path of the response field which experienced the error. - - This allows clients to identify whether a null result is intentional - or caused by a runtime error. - - The path starts from the root of the response. Field names are - separated by dots and list indices are represented using bracket - notation. If the error happens in an aliased field, the path should - use the aliased name, since it represents a path in the response, not - in the request. - - id: graphql.error.code type: string stability: development brief: > An optional error code from the extensions field. - examples: ["GRAPHQL_VALIDATION_FAILED", "UNAUTHENTICATED", "HC00116"] + examples: [ "GRAPHQL_VALIDATION_FAILED", "UNAUTHENTICATED", "HC00116" ] note: > This is an optional field that can be used to categorize errors. The error extension code is a recommended way to categorize errors for easier filtering and monitoring. - - id: graphql.error.schema_coordinate - type: string - stability: development - brief: > - The coordinate of the field in the schema which experienced the error. - examples: ["User.friends", "Query.me"] - note: > - If an error can be associated to a particular field in the GraphQL - schema, it must contain a schema coordinate in the form of - `typeName.fieldName` identifying the field which experienced the - error. - - id: registry.graphql.operation type: attribute_group stability: development @@ -289,7 +255,7 @@ groups: brief: "The id of the step in the execution plan." type: string stability: development - examples: ["0", "1", "2"] + examples: [ "0", "1", "2" ] note: > The step identifier is implementation-specific and may be numeric, UUID, or any other format used by the GraphQL execution engine. @@ -298,7 +264,7 @@ groups: brief: "The kind of step in the execution plan." type: string stability: development - examples: ["node", "operation", "fetch", "batch"] + examples: [ "node", "operation", "fetch", "batch" ] note: > The step kind describes the type of work performed in this execution step. Values are implementation-specific but common examples include @@ -307,46 +273,48 @@ groups: brief: "The id of the execution plan this step belongs to." type: string stability: development - examples: ["plan-1", "abc-123"] + examples: [ "plan-1", "abc-123" ] note: > The plan identifier links this step to a specific execution plan, enabling correlation of all steps within the same plan. This is particularly useful in federated GraphQL systems where plans may be cached and reused. - - id: registry.graphql.source + - id: registry.graphql.source_schema type: attribute_group stability: development - display_name: GraphQL Source Attributes + display_name: GraphQL Source Schema Attributes brief: > - This document defines attributes for GraphQL source systems in distributed - GraphQL architectures. + This document defines attributes for GraphQL source schemas (subgraphs) in + distributed GraphQL architectures. attributes: - - id: graphql.source.name - brief: "The name of the source system." + - id: graphql.source_schema.name + brief: "The name of the source schema." type: string stability: development - examples: ["accounts", "products", "reviews"] + examples: [ "accounts", "products", "reviews" ] note: > - The human-readable name of the downstream source that a distributed - GraphQL gateway dispatches to. For example, this could be a subgraph - name in a federated system or a stitched schema name. - - id: graphql.source.operation.name - brief: "The name of the GraphQL operation to be executed on the source." + The human-readable name of the source schema (subgraph) that a + distributed GraphQL gateway dispatches to. For example, this could be + a subgraph name in a federated system or a stitched schema name. The + term "source schema" follows the [GraphQL Composite Schemas + Specification](https://github.com/graphql/composite-schemas-spec). + - id: graphql.source_schema.operation.name + brief: "The name of the GraphQL operation to be executed on the source schema." type: string stability: development - examples: ["GetUser", "FetchProducts", "ResolveReviews"] + examples: [ "GetUser", "FetchProducts", "ResolveReviews" ] note: > The operation name of the query or mutation that the gateway sends to - the source system. - - id: graphql.source.operation.hash - brief: "A hash of the GraphQL operation to be executed on the source." + the source schema. + - id: graphql.source_schema.operation.hash + brief: "A hash of the GraphQL operation to be executed on the source schema." type: string stability: development - examples: ["sha256:abc123", "md5:def456"] + examples: [ "sha256:abc123", "md5:def456" ] note: > A hash of the operation document that the gateway sends to the source - system. Useful for identifying operations without transmitting the + schema. Useful for identifying operations without transmitting the full document. The hash algorithm used SHOULD be specified as part of the value @@ -363,7 +331,7 @@ groups: brief: "The name of the DataLoader instance." type: string stability: development - examples: ["UserLoader", "ProductByIdLoader", "CommentsByPostIdLoader"] + examples: [ "UserLoader", "ProductByIdLoader", "CommentsByPostIdLoader" ] note: > This represents the name or identifier of the DataLoader instance. When the DataLoader implementation supports naming, this SHOULD be @@ -373,7 +341,7 @@ groups: brief: "The number of individual requests in the DataLoader batch." type: int stability: development - examples: [5, 12, 25] + examples: [ 5, 12, 25 ] note: > This represents the total number of individual load requests that were batched together in a single batch operation. This includes both cache @@ -382,7 +350,7 @@ groups: brief: "A subset of the keys requested in the DataLoader batch." type: string[] stability: development - examples: [["user:1", "user:2", "user:3"], ["post:42", "post:99"]] + examples: [ [ "user:1", "user:2", "user:3" ], [ "post:42", "post:99" ] ] note: > This attribute is opt-in and SHOULD NOT be enabled by default, as keys may contain sensitive or high-cardinality data. When enabled, @@ -390,11 +358,11 @@ groups: of keys. The string representation of each key depends on the DataLoader implementation. - id: graphql.dataloader.cache.hit_count - brief: "The number of requests in the batch that were served from the - DataLoader cache." + brief: "The number of requests in the batch that were served from the DataLoader + cache." type: int stability: development - examples: [0, 3, 10] + examples: [ 0, 3, 10 ] note: > This represents the number of individual load requests that were resolved from the DataLoader's cache without requiring a fetch. @@ -402,7 +370,7 @@ groups: brief: "The number of requests in the batch that required fetching." type: int stability: development - examples: [2, 5, 12] + examples: [ 2, 5, 12 ] note: > This represents the number of individual load requests that were not found in the DataLoader's cache and required a fetch operation. @@ -418,7 +386,7 @@ groups: brief: "A unique identifier for the subscription instance." type: string stability: development - examples: ["sub-abc123", "ws-conn-42-sub-1"] + examples: [ "sub-abc123", "ws-conn-42-sub-1" ] note: > This identifier tracks a specific subscription instance throughout its lifecycle. It SHOULD be unique within the scope of the server and can diff --git a/model/graphql/spans.yaml b/model/graphql/spans.yaml index 7c8c85a0e2..4e38a9371e 100644 --- a/model/graphql/spans.yaml +++ b/model/graphql/spans.yaml @@ -150,8 +150,7 @@ groups: - ref: server.address requirement_level: recommended brief: > - The logical server hostname or IP address serving the GraphQL - endpoint. + The logical server hostname or IP address serving the GraphQL endpoint. note: > This SHOULD be the logical server address, not a proxy or load balancer address. @@ -370,6 +369,9 @@ groups: requirement_level: recommended - ref: graphql.document.hash requirement_level: recommended + - ref: graphql.document.id + requirement_level: + conditionally_required: If using trusted documents. - id: span.graphql.step.execution.internal type: span @@ -427,17 +429,20 @@ groups: requirement_level: recommended - ref: graphql.document.hash requirement_level: recommended + - ref: graphql.document.id + requirement_level: + conditionally_required: If using trusted documents. - ref: graphql.operation.step.id requirement_level: required - ref: graphql.operation.step.kind requirement_level: recommended - ref: graphql.operation.step.plan.id requirement_level: recommended - - ref: graphql.source.name + - ref: graphql.source_schema.name requirement_level: opt_in - - ref: graphql.source.operation.name + - ref: graphql.source_schema.operation.name requirement_level: opt_in - - ref: graphql.source.operation.hash + - ref: graphql.source_schema.operation.hash requirement_level: opt_in - id: span.graphql.operation.execution.internal @@ -484,7 +489,7 @@ groups: brief: > This span represents the execution of a GraphQL field. note: | - **Span name** SHOULD be `{graphql.selection.field.coordinate}`. + **Span name** SHOULD be `{graphql.field.schema_coordinate}`. This span covers the execution of an individual field, including both synchronous and asynchronous resolvers. The span ends when the resolver @@ -524,15 +529,15 @@ groups: - ref: graphql.processing.type requirement_level: required brief: MUST be `resolve`. - - ref: graphql.selection.name + - ref: graphql.field.alias requirement_level: recommended - - ref: graphql.selection.path + - ref: graphql.field.path requirement_level: recommended - - ref: graphql.selection.field.name + - ref: graphql.field.name requirement_level: required - - ref: graphql.selection.field.parent_type + - ref: graphql.field.parent_type requirement_level: required - - ref: graphql.selection.field.coordinate + - ref: graphql.field.schema_coordinate requirement_level: required - id: span.graphql.dataloader.dispatch.internal @@ -586,7 +591,7 @@ groups: the causal relationship between resolvers and the batch is preserved. Each link SHOULD include the following attributes: - - `graphql.selection.field.coordinate`: The coordinate of the field + - `graphql.field.schema_coordinate`: The schema coordinate of the field that triggered the load request (e.g., `User.avatar`). When the number of contributing resolvers exceeds a practical limit, From dd8a96069aaa4b2b122a5fd5daa06ca035e2b170 Mon Sep 17 00:00:00 2001 From: Pascal Senn Date: Sat, 2 May 2026 18:45:11 +0200 Subject: [PATCH 04/10] Enhance GraphQL error event documentation with detailed error recording guidelines --- docs/graphql/graphql-events.md | 12 +++++++++++- model/graphql/events.yaml | 16 +++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/docs/graphql/graphql-events.md b/docs/graphql/graphql-events.md index 1422d727ec..2a4538e34a 100644 --- a/docs/graphql/graphql-events.md +++ b/docs/graphql/graphql-events.md @@ -27,7 +27,17 @@ The event name MUST be `graphql.error`. This event describes a GraphQL error that occurred during operation execution. -GraphQL errors can occur during various phases of request processing including parsing, validation, and execution. Errors should be recorded on the root span for simplicity and scalability. Multiple errors can be recorded as separate events on the same span. +GraphQL errors can occur during various phases of request processing including parsing, validation, and execution. Errors SHOULD be recorded on the span that represents the work during which the error occurred: +- For queries and mutations, errors SHOULD be recorded on the root + `graphql.server` span. +- For subscriptions, errors that occur during the initial subscription + request and setup SHOULD be recorded on the root `graphql.server` span. + Errors that occur while processing an individual subscription event + SHOULD be recorded on the corresponding `graphql.subscription.event` + span, not on the root, so that each error is correlated with the event + it belongs to. + +Multiple errors can be recorded as separate events on the same span. Instrumentations SHOULD set the severity based on the impact of the error. When the response contains both `data` and `errors` (partial success), the severity SHOULD be WARN (severity number 13). When the response contains only `errors` and no `data` (complete failure), the severity SHOULD be ERROR (severity number 17). When severity cannot be determined, instrumentations SHOULD default to ERROR. **GraphQL errors and exceptions:** When a GraphQL error is caused by an underlying exception, instrumentations SHOULD record both the GraphQL error details and the exception information on the same `graphql.error` event rather than emitting separate events. The exception attributes (`exception.type`, `exception.message`, `exception.stacktrace`) MAY be included alongside the GraphQL-specific error detail attributes on the same event. This avoids duplicate events for the same underlying issue and keeps the error context together. When a GraphQL error is NOT caused by an exception (e.g., validation errors, authorization errors returned by the GraphQL layer), only the GraphQL-specific error detail attributes are needed. diff --git a/model/graphql/events.yaml b/model/graphql/events.yaml index 2b5d9c8c99..d39a8f048b 100644 --- a/model/graphql/events.yaml +++ b/model/graphql/events.yaml @@ -7,9 +7,19 @@ groups: This event describes a GraphQL error that occurred during operation execution. note: > GraphQL errors can occur during various phases of request processing including - parsing, validation, and execution. Errors should be recorded on the root - span for simplicity and scalability. Multiple errors can be recorded as - separate events on the same span. + parsing, validation, and execution. Errors SHOULD be recorded on the span + that represents the work during which the error occurred: + + - For queries and mutations, errors SHOULD be recorded on the root + `graphql.server` span. + - For subscriptions, errors that occur during the initial subscription + request and setup SHOULD be recorded on the root `graphql.server` span. + Errors that occur while processing an individual subscription event + SHOULD be recorded on the corresponding `graphql.subscription.event` + span, not on the root, so that each error is correlated with the event + it belongs to. + + Multiple errors can be recorded as separate events on the same span. Instrumentations SHOULD set the severity based on the impact of the error. When the response contains both `data` and `errors` (partial success), From 4c38af3f72addeb80a772a7ff57331808def9b31 Mon Sep 17 00:00:00 2001 From: PascalSenn Date: Thu, 7 May 2026 18:09:40 +0200 Subject: [PATCH 05/10] Update model/graphql/spans.yaml Co-authored-by: James Thompson --- model/graphql/spans.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/model/graphql/spans.yaml b/model/graphql/spans.yaml index 4e38a9371e..da834be3c5 100644 --- a/model/graphql/spans.yaml +++ b/model/graphql/spans.yaml @@ -101,8 +101,9 @@ groups: `graphql.operation.name` attributes. attributes: - ref: graphql.processing.type - requirement_level: required - brief: MUST be `request`. + requirement_level: + required: MUST be `request`. + examples: ["request"] - ref: error.type requirement_level: conditionally_required: If the GraphQL operation ended with an error. From 3ba901cd9917ba019e50f6e97b59bd8c11be57ba Mon Sep 17 00:00:00 2001 From: Pascal Senn Date: Thu, 7 May 2026 18:11:19 +0200 Subject: [PATCH 06/10] Enhance GraphQL documentation and metrics --- docs/graphql/graphql-events.md | 20 +++--- docs/graphql/graphql-metrics.md | 37 ++++++++--- docs/graphql/graphql-spans.md | 96 +++++++++++++++++------------ docs/registry/attributes/graphql.md | 33 +++++----- model/graphql/metrics.yaml | 38 ++++++++++-- model/graphql/registry.yaml | 14 +++-- model/graphql/spans.yaml | 38 ++++++++---- 7 files changed, 179 insertions(+), 97 deletions(-) diff --git a/docs/graphql/graphql-events.md b/docs/graphql/graphql-events.md index 2a4538e34a..7e2d5f1821 100644 --- a/docs/graphql/graphql-events.md +++ b/docs/graphql/graphql-events.md @@ -60,11 +60,11 @@ Instrumentations SHOULD set both `error.type` on the span and emit error events | [`exception.type`](/docs/registry/attributes/exception.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the error was caused by an exception. | 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. [3] | `java.net.ConnectException`; `OSError` | | [`graphql.document.locations`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [4] | any | The locations in the GraphQL document associated with an error. [5] | `[{ "line": 3, "column": 7 }, { "line": 5, "column": 4 }]` | | [`graphql.field.path`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [6] | string | The path of the field that is being resolved. [7] | `person[0].address` | -| [`graphql.field.schema_coordinate`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [8] | string | The schema coordinate of the field that is being resolved. [9] | `Person.address`; `Query.findBookById` | -| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available and not empty. | string | The name of the operation being executed. [10] | `FindBookById`; `GetUserProfile` | -| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The type of the operation being executed. [11] | `query`; `mutation`; `subscription` | -| [`exception.stacktrace`](/docs/registry/attributes/exception.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | 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. [12] | `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)` | -| [`graphql.error.code`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | An optional error code from the extensions field. [13] | `GRAPHQL_VALIDATION_FAILED`; `UNAUTHENTICATED`; `HC00116` | +| [`graphql.field.schema_coordinate`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [8] | string | The schema coordinate of the field that is being resolved, in the form `{ParentType}.{fieldName}`. | `Person.address`; `Query.findBookById` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available and not empty. | string | The name of the operation being executed. [9] | `FindBookById`; `GetUserProfile` | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The type of the operation being executed. [10] | `query`; `mutation`; `subscription` | +| [`exception.stacktrace`](/docs/registry/attributes/exception.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | 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. [11] | `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)` | +| [`graphql.error.code`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | An optional error code from the extensions field. [12] | `GRAPHQL_VALIDATION_FAILED`; `UNAUTHENTICATED`; `HC00116` | **[1] `graphql.error.message`:** Every error must contain an entry with the key message with a string description of the error intended for the developer as a guide to understand and correct the error. > **Warning** > This attribute has unbounded cardinality and MUST NOT be used as a metric > dimension. It is intended for span events and log records only. @@ -86,15 +86,13 @@ The path starts from the root of the response. Field names are separated by dots **[8] `graphql.field.schema_coordinate`:** Should be included when the error can be associated with a particular field in the GraphQL schema. -**[9] `graphql.field.schema_coordinate`:** The schema coordinate follows the format "{ParentType}.{fieldName}" and uniquely identifies the field within the GraphQL schema. +**[9] `graphql.operation.name`:** Including the operation name on error events enables correlation of errors to specific operations, especially useful when error events are processed independently of spans (e.g., in log-based pipelines). -**[10] `graphql.operation.name`:** Including the operation name on error events enables correlation of errors to specific operations, especially useful when error events are processed independently of spans (e.g., in log-based pipelines). +**[10] `graphql.operation.type`:** Including the operation type on error events enables correlation and filtering of errors by operation type without requiring access to the parent span. -**[11] `graphql.operation.type`:** Including the operation type on error events enables correlation and filtering of errors by operation type without requiring access to the parent span. +**[11] `exception.stacktrace`:** The exception stacktrace, when available and the error was caused by an exception. This attribute is opt-in due to its size and potential to contain sensitive information. -**[12] `exception.stacktrace`:** The exception stacktrace, when available and the error was caused by an exception. This attribute is opt-in due to its size and potential to contain sensitive information. - -**[13] `graphql.error.code`:** This is an optional field that can be used to categorize errors. The error extension code is a recommended way to categorize errors for easier filtering and monitoring. +**[12] `graphql.error.code`:** This is an optional field that can be used to categorize errors. The error extension code is a recommended way to categorize errors for easier filtering and monitoring. --- diff --git a/docs/graphql/graphql-metrics.md b/docs/graphql/graphql-metrics.md index ebfc120188..3b434281ae 100644 --- a/docs/graphql/graphql-metrics.md +++ b/docs/graphql/graphql-metrics.md @@ -128,6 +128,22 @@ attribute sets on increment and decrement. | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | | [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The hash of the operation document. [1] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The document identifier for trusted documents. [2] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [3] | `FindBookById`; `GetUserProfile` | + +**[1] `graphql.document.hash`:** The `graphql.document.hash` MUST only be enabled when the set of +document hashes has bounded cardinality, such as when using +persisted operations or trusted documents. + +**[2] `graphql.document.id`:** The `graphql.document.id` MUST only be enabled when the set of +document identifiers has bounded cardinality, such as when using +trusted documents or persisted operations. + +**[3] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have +unbounded cardinality. It MUST only be enabled when the operation +namespace has bounded cardinality, such as when using persisted +operations or trusted documents. --- @@ -155,10 +171,15 @@ attribute sets on increment and decrement. | -------- | --------------- | ----------- | -------------- | --------- | ------ | | `graphql.server.processing.duration` | Histogram | `s` | Duration of a GraphQL server processing phase. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | -**[1]:** This metric measures the duration of individual processing phases -within a GraphQL server request. The `graphql.processing.type` -attribute identifies which phase is being measured (e.g., parse, -validate, execute, plan). +**[1]:** This metric measures the duration of individual request-level processing +phases within a GraphQL server request. The `graphql.processing.type` +attribute identifies which phase is being measured (for example, `parse`, +`validate`, `variable_coercion`, `plan`, or `execute`). + +This metric SHOULD NOT be used for `request`, `resolve`, `step_execute`, +`dataloader_dispatch`, `dataloader_batch`, or `subscription_event` +processing, which represent either broader request duration or more +specific work with dedicated spans and metrics. When reported alongside a corresponding span, the metric value SHOULD match the span duration. @@ -170,13 +191,14 @@ durations for the respective processing phases. | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | -| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of processing represented by this span. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of processing represented by this span. [1] | `parse`; `validate`; `variable_coercion`; `plan`; `execute` | | [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | | [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | | [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [3] | `FindBookById`; `GetUserProfile` | -**[1] `graphql.processing.type`:** Identifies the processing phase being measured. Common values -include `parse`, `validate`, `execute`, and `plan`. +**[1] `graphql.processing.type`:** Identifies the request-level processing phase being measured. Values +SHOULD be one of `parse`, `validate`, `variable_coercion`, `plan`, or +`execute`. **[2] `error.type`:** For phase-level metrics, `error.type` captures the category of error that caused the phase to fail. This allows monitoring error rates @@ -224,6 +246,7 @@ or `graphql.document.id` MAY be used as lower-cardinality alternatives. | `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription_event` | Processing an individual GraphQL subscription event. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | | `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | diff --git a/docs/graphql/graphql-spans.md b/docs/graphql/graphql-spans.md index 37e49808a0..136c9b2a0e 100644 --- a/docs/graphql/graphql-spans.md +++ b/docs/graphql/graphql-spans.md @@ -118,7 +118,7 @@ Each operation span SHOULD have its own `graphql.operation.type` and | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | -| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `request`. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of processing represented by this span. [1] | `request` | | [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the GraphQL operation ended with an error. | string | Describes a class of error the operation ended with. [2] | `GRAPHQL_PARSE_FAILED`; `GRAPHQL_VALIDATION_FAILED`; `HC0016`; `java.lang.RuntimeException` | | [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [3] | `aa3e37c1bf54708e93f12c137afba004` | | [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available and not empty. | string | The name of the operation being executed. [4] | `FindBookById`; `GetUserProfile` | @@ -127,7 +127,7 @@ Each operation span SHOULD have its own `graphql.operation.type` and | [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [7] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | | [`server.address`](/docs/registry/attributes/server.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Recommended` | string | The logical server hostname or IP address serving the GraphQL endpoint. [8] | `example.com`; `10.1.2.80`; `/tmp/my.sock` | -**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. +**[1] `graphql.processing.type`:** MUST be `request`. **[2] `error.type`:** GraphQL errors can occur during various phases of request processing. @@ -203,6 +203,7 @@ and SHOULD be provided **at span creation time** (if provided at all): | `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription_event` | Processing an individual GraphQL subscription event. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | | `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -234,12 +235,12 @@ where the document string is parsed into an abstract syntax tree (AST). | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | -| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `parse`. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of processing represented by this span. [1] | `parse` | | [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | | [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [3] | `aa3e37c1bf54708e93f12c137afba004` | | [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [4] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | -**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. +**[1] `graphql.processing.type`:** MUST be `parse`. **[2] `error.type`:** If the processing phase fails, `error.type` SHOULD be set to the `graphql.error.code` from the error's extensions if available, or to @@ -276,6 +277,7 @@ When both `graphql.document.hash` and `graphql.document.id` are available, they | `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription_event` | Processing an individual GraphQL subscription event. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | | `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -307,12 +309,12 @@ where the document AST is validated against the GraphQL schema. | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | -| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `validate`. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of processing represented by this span. [1] | `validate` | | [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | | [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [3] | `aa3e37c1bf54708e93f12c137afba004` | | [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [4] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | -**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. +**[1] `graphql.processing.type`:** MUST be `validate`. **[2] `error.type`:** If the processing phase fails, `error.type` SHOULD be set to the `graphql.error.code` from the error's extensions if available, or to @@ -349,6 +351,7 @@ When both `graphql.document.hash` and `graphql.document.id` are available, they | `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription_event` | Processing an individual GraphQL subscription event. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | | `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -381,13 +384,13 @@ where input variables are coerced and validated according to their types. | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | | [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | -| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `variable_coercion`. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of processing represented by this span. [1] | `variable_coercion` | | [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | | [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [3] | `aa3e37c1bf54708e93f12c137afba004` | | [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [4] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | | [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the operation being executed. [5] | `FindBookById`; `GetUserProfile` | -**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. +**[1] `graphql.processing.type`:** MUST be `variable_coercion`. **[2] `error.type`:** If the processing phase fails, `error.type` SHOULD be set to the `graphql.error.code` from the error's extensions if available, or to @@ -437,6 +440,7 @@ When both `graphql.document.hash` and `graphql.document.id` are available, they | `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription_event` | Processing an individual GraphQL subscription event. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | | `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -476,13 +480,13 @@ context. | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | | [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | -| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `plan`. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of processing represented by this span. [1] | `plan` | | [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the planning phase ended with an error. | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | | [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [3] | `aa3e37c1bf54708e93f12c137afba004` | | [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [4] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | | [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the operation being executed. [5] | `FindBookById`; `GetUserProfile` | -**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. +**[1] `graphql.processing.type`:** MUST be `plan`. **[2] `error.type`:** If the planning phase fails, `error.type` SHOULD be set to the `graphql.error.code` from the error's extensions if available, or to @@ -532,6 +536,7 @@ When both `graphql.document.hash` and `graphql.document.id` are available, they | `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription_event` | Processing an individual GraphQL subscription event. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | | `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -564,13 +569,13 @@ including field resolution and result formatting. | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | | [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | -| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `execute`. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of processing represented by this span. [1] | `execute` | | [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | | [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [3] | `aa3e37c1bf54708e93f12c137afba004` | | [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [4] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | | [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the operation being executed. [5] | `FindBookById`; `GetUserProfile` | -**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. +**[1] `graphql.processing.type`:** MUST be `execute`. **[2] `error.type`:** If the processing phase fails, `error.type` SHOULD be set to the `graphql.error.code` from the error's extensions if available, or to @@ -620,6 +625,7 @@ When both `graphql.document.hash` and `graphql.document.id` are available, they | `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription_event` | Processing an individual GraphQL subscription event. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | | `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -674,7 +680,7 @@ separate preceding phase. | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | | [`graphql.operation.step.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The id of the step in the execution plan. [1] | `0`; `1`; `2` | -| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `step_execute`. [2] | `parse`; `validate`; `execute`; `resolve` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of processing represented by this span. [2] | `step_execute` | | [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the step execution ended with an error. | string | Describes a class of error the operation ended with. [3] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | | [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [4] | `aa3e37c1bf54708e93f12c137afba004` | | [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [5] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | @@ -688,7 +694,7 @@ separate preceding phase. **[1] `graphql.operation.step.id`:** The step identifier is implementation-specific and may be numeric, UUID, or any other format used by the GraphQL execution engine. -**[2] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. +**[2] `graphql.processing.type`:** MUST be `step_execute`. **[3] `error.type`:** If the step execution fails, `error.type` SHOULD be set to the `graphql.error.code` from the error's extensions if available, or to @@ -749,6 +755,7 @@ The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256: | `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription_event` | Processing an individual GraphQL subscription event. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | | `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -805,27 +812,25 @@ exception or the field resolution results in a GraphQL error. | --- | --- | --- | --- | --- | --- | | [`graphql.field.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The name of the field that is being resolved. [1] | `address`; `name`; `id` | | [`graphql.field.parent_type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type that declares the field that is being resolved. [2] | `Person`; `Query`; `Mutation` | -| [`graphql.field.schema_coordinate`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The schema coordinate of the field that is being resolved. [3] | `Person.address`; `Query.findBookById` | -| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `resolve`. [4] | `parse`; `validate`; `execute`; `resolve` | -| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the field resolution ended with an error. | string | Describes a class of error the operation ended with. [5] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | -| [`graphql.field.alias`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The alias of the field that is being resolved. [6] | `newAddress`; `bookTitle` | -| [`graphql.field.path`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The path of the field that is being resolved. [7] | `person[0].address` | +| [`graphql.field.schema_coordinate`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The schema coordinate of the field that is being resolved, in the form `{ParentType}.{fieldName}`. | `Person.address`; `Query.findBookById` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of processing represented by this span. [3] | `resolve` | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the field resolution ended with an error. | string | Describes a class of error the operation ended with. [4] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.field.alias`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The alias of the field that is being resolved. [5] | `newAddress`; `bookTitle` | +| [`graphql.field.path`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The path of the field that is being resolved. [6] | `person[0].address` | **[1] `graphql.field.name`:** This is always the actual field name as defined in the schema, not an alias. **[2] `graphql.field.parent_type`:** This is the GraphQL type name that contains the field definition. -**[3] `graphql.field.schema_coordinate`:** The schema coordinate follows the format "{ParentType}.{fieldName}" and uniquely identifies the field within the GraphQL schema. +**[3] `graphql.processing.type`:** MUST be `resolve`. -**[4] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. - -**[5] `error.type`:** If the resolver throws an exception or results in a GraphQL field error, +**[4] `error.type`:** If the resolver throws an exception or results in a GraphQL field error, `error.type` SHOULD be set to the exception type (its fully-qualified class name, if applicable) or the `graphql.error.code` if available. -**[6] `graphql.field.alias`:** If the field has an alias, this SHOULD be the alias name. Otherwise, it SHOULD be the field name. +**[5] `graphql.field.alias`:** If the field has an alias, this SHOULD be the alias name. Otherwise, it SHOULD be the field name. -**[7] `graphql.field.path`:** The path represents the location of the field being resolved within the result structure. Therefore, if a field is aliased, the path will use the alias name instead of the actual field name. +**[6] `graphql.field.path`:** The path represents the location of the field being resolved within the result structure. Therefore, if a field is aliased, the path will use the alias name instead of the actual field name. --- @@ -850,6 +855,7 @@ class name, if applicable) or the `graphql.error.code` if available. | `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription_event` | Processing an individual GraphQL subscription event. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | | `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -888,9 +894,9 @@ resolver execution span that triggered the batch dispatch. | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | -| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `dataloader_dispatch`. [1] | `parse`; `validate`; `execute`; `resolve` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of processing represented by this span. [1] | `dataloader_dispatch` | -**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. +**[1] `graphql.processing.type`:** MUST be `dataloader_dispatch`. --- @@ -907,6 +913,7 @@ resolver execution span that triggered the batch dispatch. | `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription_event` | Processing an individual GraphQL subscription event. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | | `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -962,7 +969,7 @@ reflects the true batch size regardless of link count. | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | | [`graphql.dataloader.batch.size`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | int | The number of individual requests in the DataLoader batch. [1] | `5`; `12`; `25` | -| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `dataloader_batch`. [2] | `parse`; `validate`; `execute`; `resolve` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of processing represented by this span. [2] | `dataloader_batch` | | [`graphql.dataloader.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The name of the DataLoader instance. [3] | `UserLoader`; `ProductByIdLoader`; `CommentsByPostIdLoader` | | [`graphql.dataloader.batch.keys`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string[] | A subset of the keys requested in the DataLoader batch. [4] | `["user:1", "user:2", "user:3"]`; `["post:42", "post:99"]` | | [`graphql.dataloader.cache.hit_count`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | int | The number of requests in the batch that were served from the DataLoader cache. [5] | `0`; `3`; `10` | @@ -970,7 +977,7 @@ reflects the true batch size regardless of link count. **[1] `graphql.dataloader.batch.size`:** This represents the total number of individual load requests that were batched together in a single batch operation. This includes both cache hits and cache misses. -**[2] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. +**[2] `graphql.processing.type`:** MUST be `dataloader_batch`. **[3] `graphql.dataloader.name`:** This represents the name or identifier of the DataLoader instance. When the DataLoader implementation supports naming, this SHOULD be set. This helps in identifying specific DataLoader instances in observability. @@ -995,6 +1002,7 @@ reflects the true batch size regardless of link count. | `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription_event` | Processing an individual GraphQL subscription event. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | | `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -1041,26 +1049,35 @@ processing fails. | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | -| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `subscription`. | `query`; `mutation`; `subscription` | -| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | MUST be `execute`. [1] | `parse`; `validate`; `execute`; `resolve` | -| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` [2] | string | Describes a class of error the operation ended with. [3] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | -| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available and not empty. | string | The name of the operation being executed. [4] | `FindBookById`; `GetUserProfile` | -| [`graphql.subscription.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | A unique identifier for the subscription instance. [5] | `sub-abc123`; `ws-conn-42-sub-1` | +| [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of the operation being executed. [1] | `subscription` | +| [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of processing represented by this span. [2] | `subscription_event` | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` [3] | string | Describes a class of error the operation ended with. [4] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If using trusted documents. | string | The document identifier for trusted documents. [5] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available and not empty. | string | The name of the operation being executed. [6] | `FindBookById`; `GetUserProfile` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | The hash of the operation document. [7] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`graphql.subscription.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` | string | A unique identifier for the subscription instance. [8] | `sub-abc123`; `ws-conn-42-sub-1` | -**[1] `graphql.processing.type`:** This attribute allows telemetry consumers to programmatically identify what kind of processing a span represents without relying on span names. +**[1] `graphql.operation.type`:** MUST be `subscription`. -**[2] `error.type`:** If the subscription event processing ended with an error. +**[2] `graphql.processing.type`:** MUST be `subscription_event`. -**[3] `error.type`:** If the subscription event processing fails, `error.type` SHOULD be set to +**[3] `error.type`:** If the subscription event processing ended with an error. + +**[4] `error.type`:** If the subscription event processing fails, `error.type` SHOULD be set to the `graphql.error.code` from the error's extensions if available, or to a low-cardinality error identifier such as the exception type. If the subscription event completes successfully, instrumentations SHOULD NOT set `error.type`. -**[4] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. +**[5] `graphql.document.id`:** This is a hash or identifier of the document provided by the user to identify trusted documents. + +**[6] `graphql.operation.name`:** This represents the operation name as specified in the GraphQL operation document. When the operation name is not provided, this attribute SHOULD be omitted. + +**[7] `graphql.document.hash`:** The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). Instrumentations SHOULD use SHA-256 as the default hash algorithm unless there is a specific reason to use a different one. This can be used for monitoring operation distribution and caching strategies. +When both `graphql.document.hash` and `graphql.document.id` are available, they SHOULD be preferred over transmitting the raw GraphQL document text for telemetry purposes. This reduces payload size and avoids exposing potentially sensitive operation details. -**[5] `graphql.subscription.id`:** This identifier tracks a specific subscription instance throughout its lifecycle. It SHOULD be unique within the scope of the server and can be used to correlate subscription creation, events, and termination. +**[8] `graphql.subscription.id`:** This identifier tracks a specific subscription instance throughout its lifecycle. It SHOULD be unique within the scope of the server and can be used to correlate subscription creation, events, and termination. --- @@ -1096,6 +1113,7 @@ SHOULD NOT set `error.type`. | `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription_event` | Processing an individual GraphQL subscription event. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | | `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | diff --git a/docs/registry/attributes/graphql.md b/docs/registry/attributes/graphql.md index fa97c2d7ab..b70292455f 100644 --- a/docs/registry/attributes/graphql.md +++ b/docs/registry/attributes/graphql.md @@ -64,6 +64,7 @@ The value MUST be an array of objects, where each object has the following prope | `request` | Processing the entire GraphQL request. | ![Development](https://img.shields.io/badge/-development-blue) | | `resolve` | Resolving an individual field. | ![Development](https://img.shields.io/badge/-development-blue) | | `step_execute` | Executing an individual step within an execution plan. | ![Development](https://img.shields.io/badge/-development-blue) | +| `subscription_event` | Processing an individual GraphQL subscription event. | ![Development](https://img.shields.io/badge/-development-blue) | | `validate` | Validating the GraphQL document against the schema. | ![Development](https://img.shields.io/badge/-development-blue) | | `variable_coercion` | Coercing and validating input variables. | ![Development](https://img.shields.io/badge/-development-blue) | @@ -119,7 +120,7 @@ Attributes for GraphQL fields | `graphql.field.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the field that is being resolved. [14] | `address`; `name`; `id` | | `graphql.field.parent_type` | ![Development](https://img.shields.io/badge/-development-blue) | string | The type that declares the field that is being resolved. [15] | `Person`; `Query`; `Mutation` | | `graphql.field.path` | ![Development](https://img.shields.io/badge/-development-blue) | string | The path of the field that is being resolved. [16] | `person[0].address` | -| `graphql.field.schema_coordinate` | ![Development](https://img.shields.io/badge/-development-blue) | string | The schema coordinate of the field that is being resolved. [17] | `Person.address`; `Query.findBookById` | +| `graphql.field.schema_coordinate` | ![Development](https://img.shields.io/badge/-development-blue) | string | The schema coordinate of the field that is being resolved, in the form `{ParentType}.{fieldName}`. | `Person.address`; `Query.findBookById` | **[13] `graphql.field.alias`:** If the field has an alias, this SHOULD be the alias name. Otherwise, it SHOULD be the field name. @@ -129,8 +130,6 @@ Attributes for GraphQL fields **[16] `graphql.field.path`:** The path represents the location of the field being resolved within the result structure. Therefore, if a field is aliased, the path will use the alias name instead of the actual field name. -**[17] `graphql.field.schema_coordinate`:** The schema coordinate follows the format "{ParentType}.{fieldName}" and uniquely identifies the field within the GraphQL schema. - ## GraphQL Operation Attributes This document defines attributes for GraphQL operation execution steps and planning. @@ -139,15 +138,15 @@ This document defines attributes for GraphQL operation execution steps and plann | Key | Stability | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | -| `graphql.operation.step.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | The id of the step in the execution plan. [18] | `0`; `1`; `2` | -| `graphql.operation.step.kind` | ![Development](https://img.shields.io/badge/-development-blue) | string | The kind of step in the execution plan. [19] | `node`; `operation`; `fetch`; `batch` | -| `graphql.operation.step.plan.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | The id of the execution plan this step belongs to. [20] | `plan-1`; `abc-123` | +| `graphql.operation.step.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | The id of the step in the execution plan. [17] | `0`; `1`; `2` | +| `graphql.operation.step.kind` | ![Development](https://img.shields.io/badge/-development-blue) | string | The kind of step in the execution plan. [18] | `node`; `operation`; `fetch`; `batch` | +| `graphql.operation.step.plan.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | The id of the execution plan this step belongs to. [19] | `plan-1`; `abc-123` | -**[18] `graphql.operation.step.id`:** The step identifier is implementation-specific and may be numeric, UUID, or any other format used by the GraphQL execution engine. +**[17] `graphql.operation.step.id`:** The step identifier is implementation-specific and may be numeric, UUID, or any other format used by the GraphQL execution engine. -**[19] `graphql.operation.step.kind`:** The step kind describes the type of work performed in this execution step. Values are implementation-specific but common examples include node resolution, fetch operations, and batch processing. +**[18] `graphql.operation.step.kind`:** The step kind describes the type of work performed in this execution step. Values are implementation-specific but common examples include node resolution, fetch operations, and batch processing. -**[20] `graphql.operation.step.plan.id`:** The plan identifier links this step to a specific execution plan, enabling correlation of all steps within the same plan. This is particularly useful in federated GraphQL systems where plans may be cached and reused. +**[19] `graphql.operation.step.plan.id`:** The plan identifier links this step to a specific execution plan, enabling correlation of all steps within the same plan. This is particularly useful in federated GraphQL systems where plans may be cached and reused. ## GraphQL Source Schema Attributes @@ -157,16 +156,16 @@ This document defines attributes for GraphQL source schemas (subgraphs) in distr | Key | Stability | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | -| `graphql.source_schema.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the source schema. [21] | `accounts`; `products`; `reviews` | -| `graphql.source_schema.operation.hash` | ![Development](https://img.shields.io/badge/-development-blue) | string | A hash of the GraphQL operation to be executed on the source schema. [22] | `sha256:abc123`; `md5:def456` | -| `graphql.source_schema.operation.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the GraphQL operation to be executed on the source schema. [23] | `GetUser`; `FetchProducts`; `ResolveReviews` | +| `graphql.source_schema.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the source schema. [20] | `accounts`; `products`; `reviews` | +| `graphql.source_schema.operation.hash` | ![Development](https://img.shields.io/badge/-development-blue) | string | A hash of the GraphQL operation to be executed on the source schema. [21] | `sha256:abc123`; `md5:def456` | +| `graphql.source_schema.operation.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | The name of the GraphQL operation to be executed on the source schema. [22] | `GetUser`; `FetchProducts`; `ResolveReviews` | -**[21] `graphql.source_schema.name`:** The human-readable name of the source schema (subgraph) that a distributed GraphQL gateway dispatches to. For example, this could be a subgraph name in a federated system or a stitched schema name. The term "source schema" follows the [GraphQL Composite Schemas Specification](https://github.com/graphql/composite-schemas-spec). +**[20] `graphql.source_schema.name`:** The human-readable name of the source schema (subgraph) that a distributed GraphQL gateway dispatches to. For example, this could be a subgraph name in a federated system or a stitched schema name. The term "source schema" follows the [GraphQL Composite Schemas Specification](https://github.com/graphql/composite-schemas-spec). -**[22] `graphql.source_schema.operation.hash`:** A hash of the operation document that the gateway sends to the source schema. Useful for identifying operations without transmitting the full document. +**[21] `graphql.source_schema.operation.hash`:** A hash of the operation document that the gateway sends to the source schema. Useful for identifying operations without transmitting the full document. The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."), consistent with `graphql.document.hash`. -**[23] `graphql.source_schema.operation.name`:** The operation name of the query or mutation that the gateway sends to the source schema. +**[22] `graphql.source_schema.operation.name`:** The operation name of the query or mutation that the gateway sends to the source schema. ## GraphQL Subscription Attributes @@ -176,6 +175,6 @@ Attributes for GraphQL subscription operations. | Key | Stability | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | -| `graphql.subscription.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | A unique identifier for the subscription instance. [24] | `sub-abc123`; `ws-conn-42-sub-1` | +| `graphql.subscription.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | A unique identifier for the subscription instance. [23] | `sub-abc123`; `ws-conn-42-sub-1` | -**[24] `graphql.subscription.id`:** This identifier tracks a specific subscription instance throughout its lifecycle. It SHOULD be unique within the scope of the server and can be used to correlate subscription creation, events, and termination. +**[23] `graphql.subscription.id`:** This identifier tracks a specific subscription instance throughout its lifecycle. It SHOULD be unique within the scope of the server and can be used to correlate subscription creation, events, and termination. diff --git a/model/graphql/metrics.yaml b/model/graphql/metrics.yaml index 5c58d3777c..7477367a1d 100644 --- a/model/graphql/metrics.yaml +++ b/model/graphql/metrics.yaml @@ -104,6 +104,25 @@ groups: - ref: graphql.operation.type requirement_level: conditionally_required: If available. + - ref: graphql.document.id + requirement_level: opt_in + note: | + The `graphql.document.id` MUST only be enabled when the set of + document identifiers has bounded cardinality, such as when using + trusted documents or persisted operations. + - ref: graphql.document.hash + requirement_level: opt_in + note: | + The `graphql.document.hash` MUST only be enabled when the set of + document hashes has bounded cardinality, such as when using + persisted operations or trusted documents. + - ref: graphql.operation.name + requirement_level: opt_in + note: | + The `graphql.operation.name` is provided by the client and can have + unbounded cardinality. It MUST only be enabled when the operation + namespace has bounded cardinality, such as when using persisted + operations or trusted documents. - id: metric.graphql.server.processing.duration type: metric @@ -116,10 +135,15 @@ groups: unit: "s" stability: development note: | - This metric measures the duration of individual processing phases - within a GraphQL server request. The `graphql.processing.type` - attribute identifies which phase is being measured (e.g., parse, - validate, execute, plan). + This metric measures the duration of individual request-level processing + phases within a GraphQL server request. The `graphql.processing.type` + attribute identifies which phase is being measured (for example, `parse`, + `validate`, `variable_coercion`, `plan`, or `execute`). + + This metric SHOULD NOT be used for `request`, `resolve`, `step_execute`, + `dataloader_dispatch`, `dataloader_batch`, or `subscription_event` + processing, which represent either broader request duration or more + specific work with dedicated spans and metrics. When reported alongside a corresponding span, the metric value SHOULD match the span duration. @@ -130,9 +154,11 @@ groups: attributes: - ref: graphql.processing.type requirement_level: required + examples: [ "parse", "validate", "variable_coercion", "plan", "execute" ] note: | - Identifies the processing phase being measured. Common values - include `parse`, `validate`, `execute`, and `plan`. + Identifies the request-level processing phase being measured. Values + SHOULD be one of `parse`, `validate`, `variable_coercion`, `plan`, or + `execute`. - id: metric.graphql.server.response.error_count type: metric diff --git a/model/graphql/registry.yaml b/model/graphql/registry.yaml index 9728d84227..171674dfd8 100644 --- a/model/graphql/registry.yaml +++ b/model/graphql/registry.yaml @@ -66,6 +66,10 @@ groups: value: "execute" brief: "Executing a GraphQL operation." stability: development + - id: subscription_event + value: "subscription_event" + brief: "Processing an individual GraphQL subscription event." + stability: development - id: step_execute value: "step_execute" brief: "Executing an individual step within an execution plan." @@ -122,7 +126,7 @@ groups: This is a hash or identifier of the document provided by the user to identify trusted documents. - id: graphql.document.locations - type: any + type: any[] stability: development brief: > The locations in the GraphQL document associated with an error. @@ -180,14 +184,12 @@ groups: note: > This is the GraphQL type name that contains the field definition. - id: graphql.field.schema_coordinate - brief: "The schema coordinate of the field that is being resolved." + brief: > + The schema coordinate of the field that is being resolved, in the + form `{ParentType}.{fieldName}`. type: string stability: development examples: [ "Person.address", "Query.findBookById" ] - note: > - The schema coordinate follows the format - "{ParentType}.{fieldName}" and uniquely identifies the field within - the GraphQL schema. - id: registry.graphql.error type: attribute_group diff --git a/model/graphql/spans.yaml b/model/graphql/spans.yaml index da834be3c5..d9ddb8cd90 100644 --- a/model/graphql/spans.yaml +++ b/model/graphql/spans.yaml @@ -251,7 +251,8 @@ groups: SHOULD NOT set `error.type`. - ref: graphql.processing.type requirement_level: required - brief: MUST be `parse`. + examples: [ "parse" ] + note: MUST be `parse`. - ref: graphql.document.hash requirement_level: recommended - ref: graphql.document.id @@ -284,7 +285,8 @@ groups: SHOULD NOT set `error.type`. - ref: graphql.processing.type requirement_level: required - brief: MUST be `validate`. + examples: [ "validate" ] + note: MUST be `validate`. - ref: graphql.document.hash requirement_level: recommended - ref: graphql.document.id @@ -318,7 +320,8 @@ groups: SHOULD NOT set `error.type`. - ref: graphql.processing.type requirement_level: required - brief: MUST be `variable_coercion`. + examples: [ "variable_coercion" ] + note: MUST be `variable_coercion`. - ref: graphql.operation.type requirement_level: required - ref: graphql.operation.name @@ -353,7 +356,8 @@ groups: attributes: - ref: graphql.processing.type requirement_level: required - brief: MUST be `plan`. + examples: [ "plan" ] + note: MUST be `plan`. - ref: error.type requirement_level: conditionally_required: If the planning phase ended with an error. @@ -413,7 +417,8 @@ groups: attributes: - ref: graphql.processing.type requirement_level: required - brief: MUST be `step_execute`. + examples: [ "step_execute" ] + note: MUST be `step_execute`. - ref: error.type requirement_level: conditionally_required: If the step execution ended with an error. @@ -472,7 +477,8 @@ groups: SHOULD NOT set `error.type`. - ref: graphql.processing.type requirement_level: required - brief: MUST be `execute`. + examples: [ "execute" ] + note: MUST be `execute`. - ref: graphql.operation.type requirement_level: required - ref: graphql.operation.name @@ -529,7 +535,8 @@ groups: class name, if applicable) or the `graphql.error.code` if available. - ref: graphql.processing.type requirement_level: required - brief: MUST be `resolve`. + examples: [ "resolve" ] + note: MUST be `resolve`. - ref: graphql.field.alias requirement_level: recommended - ref: graphql.field.path @@ -563,7 +570,8 @@ groups: attributes: - ref: graphql.processing.type requirement_level: required - brief: MUST be `dataloader_dispatch`. + examples: [ "dataloader_dispatch" ] + note: MUST be `dataloader_dispatch`. - id: span.graphql.dataloader.batch.internal type: span @@ -602,7 +610,8 @@ groups: attributes: - ref: graphql.processing.type requirement_level: required - brief: MUST be `dataloader_batch`. + examples: [ "dataloader_batch" ] + note: MUST be `dataloader_batch`. - ref: graphql.dataloader.name requirement_level: recommended - ref: graphql.dataloader.batch.size @@ -655,12 +664,19 @@ groups: SHOULD NOT set `error.type`. - ref: graphql.processing.type requirement_level: required - brief: MUST be `execute`. + examples: [ "subscription_event" ] + note: MUST be `subscription_event`. - ref: graphql.operation.type requirement_level: required - brief: MUST be `subscription`. + examples: [ "subscription" ] + note: MUST be `subscription`. - ref: graphql.operation.name requirement_level: conditionally_required: If available and not empty. + - ref: graphql.document.hash + requirement_level: recommended + - ref: graphql.document.id + requirement_level: + conditionally_required: If using trusted documents. - ref: graphql.subscription.id requirement_level: recommended From 81ed071b05cf1a42299488107d623cee5817c3a1 Mon Sep 17 00:00:00 2001 From: Pascal Senn Date: Thu, 25 Jun 2026 19:19:01 +0200 Subject: [PATCH 07/10] Refine GraphQL error documentation and improve span descriptions --- model/graphql/events.yaml | 3 ++- model/graphql/metrics.yaml | 2 +- model/graphql/spans.yaml | 15 ++++++++++----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/model/graphql/events.yaml b/model/graphql/events.yaml index d39a8f048b..dc9b898022 100644 --- a/model/graphql/events.yaml +++ b/model/graphql/events.yaml @@ -10,6 +10,7 @@ groups: parsing, validation, and execution. Errors SHOULD be recorded on the span that represents the work during which the error occurred: + - For queries and mutations, errors SHOULD be recorded on the root `graphql.server` span. - For subscriptions, errors that occur during the initial subscription @@ -49,7 +50,7 @@ groups: **Relationship to span status and `error.type`:** GraphQL error events provide detailed per-error information from the response `errors` - array. They are complementary to span-level error signals: + - `error.type` on the parent span provides a low-cardinality error classification for the overall operation outcome. It SHOULD be set based on the `graphql.error.code` diff --git a/model/graphql/metrics.yaml b/model/graphql/metrics.yaml index 7477367a1d..545b350e6e 100644 --- a/model/graphql/metrics.yaml +++ b/model/graphql/metrics.yaml @@ -316,5 +316,5 @@ groups: measures the transport-level duration. Histogram bucket boundaries SHOULD be chosen to capture expected - request durations. + request durations. extends: metric_attributes.graphql.client diff --git a/model/graphql/spans.yaml b/model/graphql/spans.yaml index d9ddb8cd90..ad1fdc8c9f 100644 --- a/model/graphql/spans.yaml +++ b/model/graphql/spans.yaml @@ -38,7 +38,7 @@ groups: `graphql.operation.name` is available and the operation is successfully identified in the document. - > **Warning** + > [!Warning] > The `graphql.operation.name` value is provided by the client and can have high > cardinality. Using it in the GraphQL server span name is NOT RECOMMENDED for > ad-hoc operations without careful consideration of cardinality implications. @@ -62,7 +62,7 @@ groups: is non-empty), and partial success (where both `data` and `errors` are present). Individual GraphQL errors SHOULD still be recorded as events. - > **Note** + > [!Note] > Instrumentations that have additional context about specific errors MAY > use this context to set the span status more precisely. For example, if > an instrumentation knows that the errors only affect optional, @@ -74,6 +74,7 @@ groups: additional context not already captured by `error.type` or error events. Recommended cases for setting a description: + - When the error is ambiguous without additional context - To distinguish between complete failure and partial success when both set `Error` status (e.g., "Partial success: 2 of 5 fields had errors") @@ -86,7 +87,7 @@ groups: When a GraphQL operation uses `@defer` or `@stream` directives, the response is delivered incrementally. The server span SHOULD encompass the entire response delivery, including all deferred and streamed payloads. If the instrumentation - cannot keep the span open for the full incremental delivery, it SHOULD: + - End the span after the initial payload and document this behavior - Record deferred/streamed errors as separate events if the span @@ -501,8 +502,7 @@ groups: This span covers the execution of an individual field, including both synchronous and asynchronous resolvers. The span ends when the resolver result is available. - - > **Warning** + > [!Warning] > Creating spans for every resolver execution can result in traces with > hundreds or thousands of spans, severely impacting performance and > trace readability. Instrumentations MUST NOT create resolver execution @@ -511,6 +511,7 @@ groups: Instrumentations SHOULD provide configuration options to control which resolvers generate spans. Recommended strategies include: + - **Manual selection**: Allow developers to explicitly mark specific resolvers for tracing (e.g., via annotations, decorators, or configuration) - **Asynchronous resolvers only**: Only trace resolvers that return @@ -600,6 +601,8 @@ groups: the causal relationship between resolvers and the batch is preserved. Each link SHOULD include the following attributes: + + - `graphql.field.schema_coordinate`: The schema coordinate of the field that triggered the load request (e.g., `User.avatar`). @@ -644,6 +647,8 @@ groups: subscription span. Context propagation for subscriptions: + + - The initial subscription request carries context from the client - Each subscription event SHOULD propagate context from the originating subscription where possible From 3d8fc0390125aef96ff85ead0daf3b98a4924408 Mon Sep 17 00:00:00 2001 From: Pascal Senn Date: Thu, 25 Jun 2026 23:28:57 +0200 Subject: [PATCH 08/10] Enhance GraphQL documentation with new metrics and improved error handling details --- docs/graphql/graphql-events.md | 1 + docs/graphql/graphql-metrics.md | 89 +++++++++++++++++++++++++++++++++ docs/graphql/graphql-spans.md | 13 +++-- model/graphql/events.yaml | 2 +- model/graphql/metrics.yaml | 37 ++++++++++++++ model/graphql/registry.yaml | 2 +- model/graphql/spans.yaml | 6 +-- 7 files changed, 141 insertions(+), 9 deletions(-) diff --git a/docs/graphql/graphql-events.md b/docs/graphql/graphql-events.md index 7e2d5f1821..0149fa077f 100644 --- a/docs/graphql/graphql-events.md +++ b/docs/graphql/graphql-events.md @@ -28,6 +28,7 @@ The event name MUST be `graphql.error`. This event describes a GraphQL error that occurred during operation execution. GraphQL errors can occur during various phases of request processing including parsing, validation, and execution. Errors SHOULD be recorded on the span that represents the work during which the error occurred: + - For queries and mutations, errors SHOULD be recorded on the root `graphql.server` span. - For subscriptions, errors that occur during the initial subscription diff --git a/docs/graphql/graphql-metrics.md b/docs/graphql/graphql-metrics.md index 3b434281ae..d167241dc5 100644 --- a/docs/graphql/graphql-metrics.md +++ b/docs/graphql/graphql-metrics.md @@ -16,6 +16,7 @@ This document defines semantic conventions for GraphQL client and server metrics - [Metric: `graphql.server.processing.duration`](#metric-graphqlserverprocessingduration) - [Metric: `graphql.server.response.error_count`](#metric-graphqlserverresponseerror_count) - [Metric: `graphql.server.subscription.active`](#metric-graphqlserversubscriptionactive) + - [Metric: `graphql.server.subscription.event_count`](#metric-graphqlserversubscriptionevent_count) - [Metric: `graphql.server.subscription.event.duration`](#metric-graphqlserversubscriptioneventduration) - [GraphQL client](#graphql-client) - [Metric: `graphql.client.request.duration`](#metric-graphqlclientrequestduration) @@ -346,6 +347,82 @@ operations or trusted documents. +### Metric: `graphql.server.subscription.event_count` + + + + + + +| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | +| -------- | --------------- | ----------- | -------------- | --------- | ------ | +| `graphql.server.subscription.event_count` | Counter | `{event}` | Number of GraphQL subscription events processed. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | + +**[1]:** This metric is incremented once for each subscription event processed +by the server, including events whose processing ends with an error. + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` [1] | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The hash of the operation document. [3] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The document identifier for trusted documents. [4] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [5] | `FindBookById`; `GetUserProfile` | + +**[1] `error.type`:** If the subscription event processing ended with an error. + +**[2] `error.type`:** The `error.type` SHOULD be predictable, and SHOULD have low cardinality. + +When `error.type` is set to a type (e.g., an exception type), its +canonical class name identifying the type within the artifact SHOULD be used. + +If the recorded error type is a wrapper that is not meaningful for +failure classification, instrumentation MAY use the type of the inner +error instead. For example, in Go, errors created with `fmt.Errorf` +using `%w` MAY be unwrapped when the wrapper type does not help +classify the failure. + +Instrumentations SHOULD document the list of errors they report. + +The cardinality of `error.type` within one instrumentation library SHOULD be low. +Telemetry consumers that aggregate data from multiple instrumentation libraries and applications +should be prepared for `error.type` to have high cardinality at query time when no +additional filters are applied. + +If the operation has completed successfully, instrumentations SHOULD NOT set `error.type`. + +If a specific domain defines its own set of error identifiers (such as HTTP or RPC status codes), +it's RECOMMENDED to: + +- Use a domain-specific attribute +- Set `error.type` to capture all errors, regardless of whether they are defined within the domain-specific set or not. + +**[3] `graphql.document.hash`:** The `graphql.document.hash` MUST only be enabled when the set of +document hashes has bounded cardinality, such as when using +persisted operations or trusted documents. + +**[4] `graphql.document.id`:** The `graphql.document.id` MUST only be enabled when the set of +document identifiers has bounded cardinality, such as when using +trusted documents or persisted operations. + +**[5] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have +unbounded cardinality. It MUST only be enabled when the operation +namespace has bounded cardinality, such as when using persisted +operations or trusted documents. + +--- + +`error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + + + + + ### Metric: `graphql.server.subscription.event.duration` @@ -380,6 +457,12 @@ subscription event processing times. When `error.type` is set to a type (e.g., an exception type), its canonical class name identifying the type within the artifact SHOULD be used. +If the recorded error type is a wrapper that is not meaningful for +failure classification, instrumentation MAY use the type of the inner +error instead. For example, in Go, errors created with `fmt.Errorf` +using `%w` MAY be unwrapped when the wrapper type does not help +classify the failure. + Instrumentations SHOULD document the list of errors they report. The cardinality of `error.type` within one instrumentation library SHOULD be low. @@ -458,6 +541,12 @@ request durations. When `error.type` is set to a type (e.g., an exception type), its canonical class name identifying the type within the artifact SHOULD be used. +If the recorded error type is a wrapper that is not meaningful for +failure classification, instrumentation MAY use the type of the inner +error instead. For example, in Go, errors created with `fmt.Errorf` +using `%w` MAY be unwrapped when the wrapper type does not help +classify the failure. + Instrumentations SHOULD document the list of errors they report. The cardinality of `error.type` within one instrumentation library SHOULD be low. diff --git a/docs/graphql/graphql-spans.md b/docs/graphql/graphql-spans.md index 136c9b2a0e..ea1cfad8e2 100644 --- a/docs/graphql/graphql-spans.md +++ b/docs/graphql/graphql-spans.md @@ -50,7 +50,7 @@ the `{graphql.operation.type} {graphql.operation.name}` format when `graphql.operation.name` is available and the operation is successfully identified in the document. -> **Warning** +> [!Warning] > The `graphql.operation.name` value is provided by the client and can have high > cardinality. Using it in the GraphQL server span name is NOT RECOMMENDED for > ad-hoc operations without careful consideration of cardinality implications. @@ -74,7 +74,7 @@ request errors, total execution failure (where `data` is `null` and `errors` is non-empty), and partial success (where both `data` and `errors` are present). Individual GraphQL errors SHOULD still be recorded as events. -> **Note** +> [!Note] > Instrumentations that have additional context about specific errors MAY > use this context to set the span status more precisely. For example, if > an instrumentation knows that the errors only affect optional, @@ -86,6 +86,7 @@ Span status descriptions are optional and SHOULD only be set when they provide additional context not already captured by `error.type` or error events. Recommended cases for setting a description: + - When the error is ambiguous without additional context - To distinguish between complete failure and partial success when both set `Error` status (e.g., "Partial success: 2 of 5 fields had errors") @@ -779,8 +780,7 @@ This span represents the execution of a GraphQL field. This span covers the execution of an individual field, including both synchronous and asynchronous resolvers. The span ends when the resolver result is available. - -> **Warning** +> [!Warning] > Creating spans for every resolver execution can result in traces with > hundreds or thousands of spans, severely impacting performance and > trace readability. Instrumentations MUST NOT create resolver execution @@ -789,6 +789,7 @@ result is available. Instrumentations SHOULD provide configuration options to control which resolvers generate spans. Recommended strategies include: + - **Manual selection**: Allow developers to explicitly mark specific resolvers for tracing (e.g., via annotations, decorators, or configuration) - **Asynchronous resolvers only**: Only trace resolvers that return @@ -952,6 +953,8 @@ whose resolvers contributed load requests to this batch, so that the causal relationship between resolvers and the batch is preserved. Each link SHOULD include the following attributes: + + - `graphql.field.schema_coordinate`: The schema coordinate of the field that triggered the load request (e.g., `User.avatar`). @@ -1035,6 +1038,8 @@ instrumentations SHOULD create a new root span or link to the original subscription span. Context propagation for subscriptions: + + - The initial subscription request carries context from the client - Each subscription event SHOULD propagate context from the originating subscription where possible diff --git a/model/graphql/events.yaml b/model/graphql/events.yaml index dc9b898022..f91dda6573 100644 --- a/model/graphql/events.yaml +++ b/model/graphql/events.yaml @@ -50,7 +50,7 @@ groups: **Relationship to span status and `error.type`:** GraphQL error events provide detailed per-error information from the response `errors` - + array. They are complementary to span-level error signals: - `error.type` on the parent span provides a low-cardinality error classification for the overall operation outcome. It SHOULD be set based on the `graphql.error.code` diff --git a/model/graphql/metrics.yaml b/model/graphql/metrics.yaml index 545b350e6e..dac70d8cca 100644 --- a/model/graphql/metrics.yaml +++ b/model/graphql/metrics.yaml @@ -229,6 +229,43 @@ groups: namespace has bounded cardinality, such as when using persisted operations or trusted documents. + - id: metric.graphql.server.subscription.event_count + type: metric + metric_name: graphql.server.subscription.event_count + annotations: + code_generation: + metric_value_type: int + brief: "Number of GraphQL subscription events processed." + instrument: counter + unit: "{event}" + stability: development + note: | + This metric is incremented once for each subscription event processed + by the server, including events whose processing ends with an error. + attributes: + - ref: graphql.document.id + requirement_level: opt_in + note: | + The `graphql.document.id` MUST only be enabled when the set of + document identifiers has bounded cardinality, such as when using + trusted documents or persisted operations. + - ref: graphql.document.hash + requirement_level: opt_in + note: | + The `graphql.document.hash` MUST only be enabled when the set of + document hashes has bounded cardinality, such as when using + persisted operations or trusted documents. + - ref: graphql.operation.name + requirement_level: opt_in + note: | + The `graphql.operation.name` is provided by the client and can have + unbounded cardinality. It MUST only be enabled when the operation + namespace has bounded cardinality, such as when using persisted + operations or trusted documents. + - ref: error.type + requirement_level: + conditionally_required: If the subscription event processing ended with an error. + - id: metric.graphql.server.subscription.event.duration type: metric metric_name: graphql.server.subscription.event.duration diff --git a/model/graphql/registry.yaml b/model/graphql/registry.yaml index 171674dfd8..4a12147afd 100644 --- a/model/graphql/registry.yaml +++ b/model/graphql/registry.yaml @@ -126,7 +126,7 @@ groups: This is a hash or identifier of the document provided by the user to identify trusted documents. - id: graphql.document.locations - type: any[] + type: any stability: development brief: > The locations in the GraphQL document associated with an error. diff --git a/model/graphql/spans.yaml b/model/graphql/spans.yaml index ad1fdc8c9f..64cd1077d8 100644 --- a/model/graphql/spans.yaml +++ b/model/graphql/spans.yaml @@ -87,7 +87,7 @@ groups: When a GraphQL operation uses `@defer` or `@stream` directives, the response is delivered incrementally. The server span SHOULD encompass the entire response delivery, including all deferred and streamed payloads. If the instrumentation - + cannot keep the span open for the full incremental delivery, it SHOULD: - End the span after the initial payload and document this behavior - Record deferred/streamed errors as separate events if the span @@ -102,9 +102,9 @@ groups: `graphql.operation.name` attributes. attributes: - ref: graphql.processing.type - requirement_level: - required: MUST be `request`. + requirement_level: required examples: ["request"] + note: MUST be `request`. - ref: error.type requirement_level: conditionally_required: If the GraphQL operation ended with an error. From fc15adf4c707167d3646570050697d464add6396 Mon Sep 17 00:00:00 2001 From: Pascal Senn Date: Fri, 26 Jun 2026 14:04:09 +0200 Subject: [PATCH 09/10] Refactor GraphQL metrics and spans documentation to clarify subscription handling and remove deprecated metrics --- docs/graphql/graphql-metrics.md | 55 +++++---------------------------- docs/graphql/graphql-spans.md | 16 +++++----- model/graphql/metrics.yaml | 49 +++++------------------------ model/graphql/spans.yaml | 16 +++++----- 4 files changed, 32 insertions(+), 104 deletions(-) diff --git a/docs/graphql/graphql-metrics.md b/docs/graphql/graphql-metrics.md index d167241dc5..81a40f59cc 100644 --- a/docs/graphql/graphql-metrics.md +++ b/docs/graphql/graphql-metrics.md @@ -15,7 +15,6 @@ This document defines semantic conventions for GraphQL client and server metrics - [Metric: `graphql.server.request.active`](#metric-graphqlserverrequestactive) - [Metric: `graphql.server.processing.duration`](#metric-graphqlserverprocessingduration) - [Metric: `graphql.server.response.error_count`](#metric-graphqlserverresponseerror_count) - - [Metric: `graphql.server.subscription.active`](#metric-graphqlserversubscriptionactive) - [Metric: `graphql.server.subscription.event_count`](#metric-graphqlserversubscriptionevent_count) - [Metric: `graphql.server.subscription.event.duration`](#metric-graphqlserversubscriptioneventduration) - [GraphQL client](#graphql-client) @@ -40,6 +39,10 @@ This document defines semantic conventions for GraphQL client and server metrics request on the server. It starts when the server begins processing the GraphQL operation and ends when the response is complete. +For subscription operations, the response is complete when the +subscription terminates. This metric therefore records the full +subscription duration, not only the initial subscription setup. + When this metric is reported alongside a GraphQL server span, the metric value SHOULD be the same as the GraphQL server span duration. @@ -118,6 +121,9 @@ or `graphql.document.id` MAY be used as lower-cardinality alternatives. processed by the server. It is incremented when processing begins and decremented when processing is complete. +For subscription operations, the request remains active until the +subscription terminates. + Since `graphql.operation.type` is not available before parsing, instrumentations SHOULD delay the increment until after parsing completes and the operation type is known. This means requests that fail during @@ -300,53 +306,6 @@ Histogram bucket boundaries for error counts. -### Metric: `graphql.server.subscription.active` - - - - - - -| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | -| -------- | --------------- | ----------- | -------------- | --------- | ------ | -| `graphql.server.subscription.active` | UpDownCounter | `{subscription}` | Number of active GraphQL subscriptions. [1] | ![Development](https://img.shields.io/badge/-development-blue) | | - -**[1]:** This metric tracks the number of GraphQL subscriptions currently active -on the server. It is incremented when a subscription is created and -decremented when a subscription is terminated (either by client -disconnect or server-side cancellation). - -This is distinct from `graphql.server.request.active`, which tracks -short-lived request processing (parse → execute → respond). For -subscriptions, `request.active` only covers the initial setup request, -while this metric tracks the long-lived subscription connection that -may remain open for hours or days delivering events. - -**Attributes:** - -| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | -| --- | --- | --- | --- | --- | --- | -| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The hash of the operation document. [1] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | -| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The document identifier for trusted documents. [2] | `aa3e37c1bf54708e93f12c137afba004` | -| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [3] | `FindBookById`; `GetUserProfile` | - -**[1] `graphql.document.hash`:** The `graphql.document.hash` MUST only be enabled when the set of -document hashes has bounded cardinality, such as when using -persisted operations or trusted documents. - -**[2] `graphql.document.id`:** The `graphql.document.id` MUST only be enabled when the set of -document identifiers has bounded cardinality, such as when using -trusted documents or persisted operations. - -**[3] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have -unbounded cardinality. It MUST only be enabled when the operation -namespace has bounded cardinality, such as when using persisted -operations or trusted documents. - - - - - ### Metric: `graphql.server.subscription.event_count` diff --git a/docs/graphql/graphql-spans.md b/docs/graphql/graphql-spans.md index ea1cfad8e2..776fe51a7f 100644 --- a/docs/graphql/graphql-spans.md +++ b/docs/graphql/graphql-spans.md @@ -61,10 +61,13 @@ in the document. > the operation was not found or could not be identified in the document. > This prevents potential security issues and ensures span names remain meaningful. -For subscription operations, the server span represents the initial subscription -request and setup. Individual subscription events are represented by separate -`graphql.subscription.event` spans. Long-lived subscriptions MAY end the server -span after successful setup, with subsequent events creating their own spans. +For subscription operations, the server span represents the entire subscription +operation. It starts when the server begins processing the subscription request +and ends when the subscription terminates. This mirrors HTTP server spans for +long-lived transports such as Server-Sent Events and WebSockets, where the +transport-level request may remain active while events are delivered. +Individual subscription events are represented by separate +`graphql.subscription.event` spans. **Span status** guidance: @@ -1031,9 +1034,8 @@ GraphQL subscription. Each time the subscription source emits an event, a new span SHOULD be created to track the execution of the subscription field selection set against that event. -The span SHOULD be a child of the `graphql.server` span that initiated -the subscription, if that span is still available. When the originating -server span has ended (as is common with long-lived subscriptions), +The span SHOULD be a child of the `graphql.server` span for the +subscription. When the originating server span is not available, instrumentations SHOULD create a new root span or link to the original subscription span. diff --git a/model/graphql/metrics.yaml b/model/graphql/metrics.yaml index dac70d8cca..e51d7f65e5 100644 --- a/model/graphql/metrics.yaml +++ b/model/graphql/metrics.yaml @@ -69,6 +69,10 @@ groups: request on the server. It starts when the server begins processing the GraphQL operation and ends when the response is complete. + For subscription operations, the response is complete when the + subscription terminates. This metric therefore records the full + subscription duration, not only the initial subscription setup. + When this metric is reported alongside a GraphQL server span, the metric value SHOULD be the same as the GraphQL server span duration. @@ -95,6 +99,9 @@ groups: processed by the server. It is incremented when processing begins and decremented when processing is complete. + For subscription operations, the request remains active until the + subscription terminates. + Since `graphql.operation.type` is not available before parsing, instrumentations SHOULD delay the increment until after parsing completes and the operation type is known. This means requests that fail during @@ -187,48 +194,6 @@ groups: - ref: graphql.operation.name requirement_level: opt_in - - id: metric.graphql.server.subscription.active - type: metric - metric_name: graphql.server.subscription.active - annotations: - code_generation: - metric_value_type: int - brief: "Number of active GraphQL subscriptions." - instrument: updowncounter - unit: "{subscription}" - stability: development - note: | - This metric tracks the number of GraphQL subscriptions currently active - on the server. It is incremented when a subscription is created and - decremented when a subscription is terminated (either by client - disconnect or server-side cancellation). - - This is distinct from `graphql.server.request.active`, which tracks - short-lived request processing (parse → execute → respond). For - subscriptions, `request.active` only covers the initial setup request, - while this metric tracks the long-lived subscription connection that - may remain open for hours or days delivering events. - attributes: - - ref: graphql.document.id - requirement_level: opt_in - note: | - The `graphql.document.id` MUST only be enabled when the set of - document identifiers has bounded cardinality, such as when using - trusted documents or persisted operations. - - ref: graphql.document.hash - requirement_level: opt_in - note: | - The `graphql.document.hash` MUST only be enabled when the set of - document hashes has bounded cardinality, such as when using - persisted operations or trusted documents. - - ref: graphql.operation.name - requirement_level: opt_in - note: | - The `graphql.operation.name` is provided by the client and can have - unbounded cardinality. It MUST only be enabled when the operation - namespace has bounded cardinality, such as when using persisted - operations or trusted documents. - - id: metric.graphql.server.subscription.event_count type: metric metric_name: graphql.server.subscription.event_count diff --git a/model/graphql/spans.yaml b/model/graphql/spans.yaml index 64cd1077d8..ebc2210dc2 100644 --- a/model/graphql/spans.yaml +++ b/model/graphql/spans.yaml @@ -49,10 +49,13 @@ groups: > the operation was not found or could not be identified in the document. > This prevents potential security issues and ensures span names remain meaningful. - For subscription operations, the server span represents the initial subscription - request and setup. Individual subscription events are represented by separate - `graphql.subscription.event` spans. Long-lived subscriptions MAY end the server - span after successful setup, with subsequent events creating their own spans. + For subscription operations, the server span represents the entire subscription + operation. It starts when the server begins processing the subscription request + and ends when the subscription terminates. This mirrors HTTP server spans for + long-lived transports such as Server-Sent Events and WebSockets, where the + transport-level request may remain active while events are delivered. + Individual subscription events are represented by separate + `graphql.subscription.event` spans. **Span status** guidance: @@ -640,9 +643,8 @@ groups: a new span SHOULD be created to track the execution of the subscription field selection set against that event. - The span SHOULD be a child of the `graphql.server` span that initiated - the subscription, if that span is still available. When the originating - server span has ended (as is common with long-lived subscriptions), + The span SHOULD be a child of the `graphql.server` span for the + subscription. When the originating server span is not available, instrumentations SHOULD create a new root span or link to the original subscription span. From 0f08ec501ac8d5f051ff60f94d56b93bc049e77b Mon Sep 17 00:00:00 2001 From: Pascal Senn Date: Fri, 26 Jun 2026 20:38:35 +0200 Subject: [PATCH 10/10] cleanup --- docs/graphql/graphql-metrics.md | 30 ++++++++++++++++++----- model/graphql/metrics.yaml | 42 ++++++++++----------------------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/docs/graphql/graphql-metrics.md b/docs/graphql/graphql-metrics.md index 81a40f59cc..d7f93c94e3 100644 --- a/docs/graphql/graphql-metrics.md +++ b/docs/graphql/graphql-metrics.md @@ -59,8 +59,9 @@ request durations. | --- | --- | --- | --- | --- | --- | | [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the GraphQL request ended with an error. | string | Describes a class of error the operation ended with. [1] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | | [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | -| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The document identifier for trusted documents. [2] | `aa3e37c1bf54708e93f12c137afba004` | -| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [3] | `FindBookById`; `GetUserProfile` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The hash of the operation document. [2] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The document identifier for trusted documents. [3] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [4] | `FindBookById`; `GetUserProfile` | **[1] `error.type`:** For GraphQL, `error.type` captures the category of error that caused the request to fail. Since GraphQL can return partial data with errors @@ -71,11 +72,15 @@ The `error.type` value SHOULD be predictable and SHOULD have low cardinality. Instrumentations SHOULD document the list of errors they report. -**[2] `graphql.document.id`:** The `graphql.document.id` MUST only be enabled when the set of +**[2] `graphql.document.hash`:** The `graphql.document.hash` MUST only be enabled when the set of +document hashes has bounded cardinality, such as when using +persisted operations or trusted documents. + +**[3] `graphql.document.id`:** The `graphql.document.id` MUST only be enabled when the set of document identifiers has bounded cardinality, such as when using trusted documents or persisted operations. -**[3] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have +**[4] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have unbounded cardinality. It MUST only be enabled when the operation namespace has bounded cardinality, such as when using persisted operations or trusted documents. @@ -152,6 +157,9 @@ unbounded cardinality. It MUST only be enabled when the operation namespace has bounded cardinality, such as when using persisted operations or trusted documents. +When `graphql.operation.name` is not enabled, `graphql.document.hash` +or `graphql.document.id` MAY be used as lower-cardinality alternatives. + --- `graphql.operation.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. @@ -201,7 +209,9 @@ durations for the respective processing phases. | [`graphql.processing.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The type of processing represented by this span. [1] | `parse`; `validate`; `variable_coercion`; `plan`; `execute` | | [`error.type`](/docs/registry/attributes/error.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Conditionally Required` If the processing phase ended with an error. | string | Describes a class of error the operation ended with. [2] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | | [`graphql.operation.type`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` If available. | string | The type of the operation being executed. | `query`; `mutation`; `subscription` | -| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [3] | `FindBookById`; `GetUserProfile` | +| [`graphql.document.hash`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The hash of the operation document. [3] | `sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c` | +| [`graphql.document.id`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The document identifier for trusted documents. [4] | `aa3e37c1bf54708e93f12c137afba004` | +| [`graphql.operation.name`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The name of the operation being executed. [5] | `FindBookById`; `GetUserProfile` | **[1] `graphql.processing.type`:** Identifies the request-level processing phase being measured. Values SHOULD be one of `parse`, `validate`, `variable_coercion`, `plan`, or @@ -211,7 +221,15 @@ SHOULD be one of `parse`, `validate`, `variable_coercion`, `plan`, or that caused the phase to fail. This allows monitoring error rates per phase independently of the overall request outcome. -**[3] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have +**[3] `graphql.document.hash`:** The `graphql.document.hash` MUST only be enabled when the set of +document hashes has bounded cardinality, such as when using +persisted operations or trusted documents. + +**[4] `graphql.document.id`:** The `graphql.document.id` MUST only be enabled when the set of +document identifiers has bounded cardinality, such as when using +trusted documents or persisted operations. + +**[5] `graphql.operation.name`:** The `graphql.operation.name` is provided by the client and can have unbounded cardinality. It MUST only be enabled when the operation namespace has bounded cardinality, such as when using persisted operations or trusted documents. diff --git a/model/graphql/metrics.yaml b/model/graphql/metrics.yaml index e51d7f65e5..5134506ac6 100644 --- a/model/graphql/metrics.yaml +++ b/model/graphql/metrics.yaml @@ -6,6 +6,18 @@ groups: - ref: graphql.operation.type requirement_level: conditionally_required: If available. + - ref: graphql.document.id + requirement_level: opt_in + note: | + The `graphql.document.id` MUST only be enabled when the set of + document identifiers has bounded cardinality, such as when using + trusted documents or persisted operations. + - ref: graphql.document.hash + requirement_level: opt_in + note: | + The `graphql.document.hash` MUST only be enabled when the set of + document hashes has bounded cardinality, such as when using + persisted operations or trusted documents. - ref: graphql.operation.name requirement_level: opt_in note: | @@ -22,12 +34,6 @@ groups: brief: "Common attributes for GraphQL server request-level metrics." extends: metric_attributes.graphql.server.base attributes: - - ref: graphql.document.id - requirement_level: opt_in - note: | - The `graphql.document.id` MUST only be enabled when the set of - document identifiers has bounded cardinality, such as when using - trusted documents or persisted operations. - ref: error.type requirement_level: conditionally_required: If the GraphQL request ended with an error. @@ -107,29 +113,7 @@ groups: and the operation type is known. This means requests that fail during parsing will not be reflected in this metric, but it ensures consistent attribute sets on increment and decrement. - attributes: - - ref: graphql.operation.type - requirement_level: - conditionally_required: If available. - - ref: graphql.document.id - requirement_level: opt_in - note: | - The `graphql.document.id` MUST only be enabled when the set of - document identifiers has bounded cardinality, such as when using - trusted documents or persisted operations. - - ref: graphql.document.hash - requirement_level: opt_in - note: | - The `graphql.document.hash` MUST only be enabled when the set of - document hashes has bounded cardinality, such as when using - persisted operations or trusted documents. - - ref: graphql.operation.name - requirement_level: opt_in - note: | - The `graphql.operation.name` is provided by the client and can have - unbounded cardinality. It MUST only be enabled when the operation - namespace has bounded cardinality, such as when using persisted - operations or trusted documents. + extends: metric_attributes.graphql.server.base - id: metric.graphql.server.processing.duration type: metric