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..7e2d5f1821 --- /dev/null +++ b/docs/graphql/graphql-events.md @@ -0,0 +1,112 @@ + + +# 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 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. +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` + 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.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, 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. + +**[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.document.locations`:** Should be included when the error can be associated with a specific location in the GraphQL document. + +**[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.field.path`:** Must be included when the error can be associated with a particular field in the GraphQL result. + +**[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.field.schema_coordinate`:** Should be included when the error can be associated with a particular field in 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.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] `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..3b434281ae --- /dev/null +++ b/docs/graphql/graphql-metrics.md @@ -0,0 +1,511 @@ + + +# 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.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.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.request.active` + + + + + + +| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | +| -------- | --------------- | ----------- | -------------- | --------- | ------ | +| `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 +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.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. + +--- + +`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.processing.duration` + + + + + + +| Name | Instrument Type | Unit (UCUM) | Description | Stability | Entity Associations | +| -------- | --------------- | ----------- | -------------- | --------- | ------ | +| `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 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. + +Histogram bucket boundaries SHOULD be chosen to capture expected +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 | +| --- | --- | --- | --- | --- | --- | +| [`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 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 +per phase independently of the overall request outcome. + +**[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) | + +--- + +`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) | +| `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) | + + + + + +### 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.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.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 6b428307ae..136c9b2a0e 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,1172 @@ 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. -**Span kind** SHOULD be `SERVER`. +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** SHOULD follow the [Recording Errors](/docs/general/recording-errors.md) document. +**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 `INTERNAL`. **Attributes:** | Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | --- | -| [`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.document`](/docs/registry/attributes/graphql.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The GraphQL document being executed. [1] | `query findBookById { bookById(id: ?) { name } }` | +| [`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` | +| [`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` [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`:** MUST be `request`. + +**[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 +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. + +**[3] `graphql.document.id`:** This is a hash or identifier of the document provided by the user to identify trusted documents. + +**[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] `server.port`:** If not the default port for the scheme (e.g., not 443 for HTTPS). + +**[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. + +**[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. + +**[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): + +* [`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.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) | +| `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) | + + + + + +### 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.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`:** 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 +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) | +| `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) | + + + + + +### 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 | 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`:** 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 +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. -**[1] `graphql.document`:** If instrumentation can reliably identify and redact sensitive information it SHOULD do it. +| 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) | +| `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) | + + + + + +### 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 | 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`:** 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 +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) | +| `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) | + + + + + +### 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 | 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`:** 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 +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.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) | +| `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) | + + + + + +### 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 | 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`:** 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 +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) | +| `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) | + + + + + +### 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 | 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` | +| [`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_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. + +**[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 +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.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. + +**[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.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. + +**[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. + +**[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). + +**[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`. + +**[11] `graphql.source_schema.operation.name`:** The operation name of the query or mutation that the gateway sends to the source schema. + +--- + +`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) | +| `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) | + + + + + +### 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.field.schema_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.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, 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.processing.type`:** MUST be `resolve`. + +**[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. + +**[5] `graphql.field.alias`:** If the field has an alias, this SHOULD be the alias name. Otherwise, it SHOULD be the 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. + +--- + +`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) | +| `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) | + + + + + +### 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 | The type of processing represented by this span. [1] | `dataloader_dispatch` | + +**[1] `graphql.processing.type`:** MUST be `dataloader_dispatch`. + +--- + +`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) | +| `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) | + + + + + +### 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.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, +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 | 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` | +| [`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`:** 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. + +**[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) | +| `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) | + + + + + +### 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 | 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.operation.type`:** MUST be `subscription`. + +**[2] `graphql.processing.type`:** MUST be `subscription_event`. + +**[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`. + +**[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. + +**[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. + +--- + +`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) | +| `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) | + + + + + +## 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 +1211,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 d5b010ba21..b70292455f 100644 --- a/docs/registry/attributes/graphql.md +++ b/docs/registry/attributes/graphql.md @@ -3,19 +3,40 @@ # GraphQL +- [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 Source Schema Attributes](#graphql-source-schema-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.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. [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.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. -**[1] `graphql.document`:** If instrumentation can reliably identify and redact sensitive information it SHOULD do it. +**[5] `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 +44,137 @@ 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) | +| `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) | + +## 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. [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` | + +**[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. + +**[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. + +**[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. + +**[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. + +**[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 + +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. [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.` | + +**[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. + +## GraphQL Field Attributes + +Attributes for GraphQL fields + +**Attributes:** + +| Key | Stability | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | +| `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, 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. + +**[14] `graphql.field.name`:** This is always the actual field name as defined in the schema, not an alias. + +**[15] `graphql.field.parent_type`:** This is the GraphQL type name that contains the field definition. + +**[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. + +## 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. [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` | + +**[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. + +**[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. + +**[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 + +This document defines attributes for GraphQL source schemas (subgraphs) in distributed GraphQL architectures. + +**Attributes:** + +| 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. [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` | + +**[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). + +**[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`. + +**[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 + +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. [23] | `sub-abc123`; `ws-conn-42-sub-1` | + +**[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/events.yaml b/model/graphql/events.yaml new file mode 100644 index 0000000000..d39a8f048b --- /dev/null +++ b/model/graphql/events.yaml @@ -0,0 +1,122 @@ +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 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. + + 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` + 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.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.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 + 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.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 + 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..7477367a1d --- /dev/null +++ b/model/graphql/metrics.yaml @@ -0,0 +1,320 @@ +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.request.active + type: metric + metric_name: graphql.server.request.active + 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. + - 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 + metric_name: graphql.server.processing.duration + annotations: + code_generation: + metric_value_type: double + brief: "Duration of a GraphQL server processing phase." + instrument: histogram + unit: "s" + stability: development + note: | + 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. + + Histogram bucket boundaries SHOULD be chosen to capture expected + durations for the respective processing phases. + extends: metric_attributes.graphql.server.phase + attributes: + - ref: graphql.processing.type + requirement_level: required + examples: [ "parse", "validate", "variable_coercion", "plan", "execute" ] + note: | + 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 + 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.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.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 c1b6136a3c..171674dfd8 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,375 @@ 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: request + value: "request" + brief: "Processing the entire GraphQL request." + stability: development + - 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: 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." + 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:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f87\ + 3d8664c", + ] + 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: 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.field + type: attribute_group + stability: development + display_name: GraphQL Field Attributes + brief: > + Attributes for GraphQL fields + attributes: + - id: graphql.field.alias + brief: "The alias of the field that is being resolved." + 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.field.path + brief: "The path of the field 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.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.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.field.schema_coordinate + 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" ] + + - 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 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-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` + 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.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: 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_schema + type: attribute_group + stability: development + display_name: GraphQL Source Schema Attributes + brief: > + This document defines attributes for GraphQL source schemas (subgraphs) in + distributed GraphQL architectures. + attributes: + - id: graphql.source_schema.name + brief: "The name of the source schema." + type: string + stability: development + examples: [ "accounts", "products", "reviews" ] + note: > + 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" ] + note: > + The operation name of the query or mutation that the gateway sends to + 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" ] + note: > + 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`. + + - 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: If instrumentation can reliably identify and redact sensitive information it SHOULD do it. - 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..d9ddb8cd90 --- /dev/null +++ b/model/graphql/spans.yaml @@ -0,0 +1,682 @@ +# GraphQL Span Hierarchy +# ====================== +# +# The following diagram shows the normative parent-child relationships +# between GraphQL spans. Spans marked [optional] are not required. +# +# graphql.server (INTERNAL) +# ├── 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: internal + 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: graphql.processing.type + requirement_level: + required: MUST be `request`. + examples: ["request"] + - 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 + examples: [ "parse" ] + note: 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 + examples: [ "validate" ] + note: 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 + examples: [ "variable_coercion" ] + note: 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 + examples: [ "plan" ] + note: 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 + - ref: graphql.document.id + requirement_level: + conditionally_required: If using trusted documents. + + - 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 + examples: [ "step_execute" ] + note: 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.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_schema.name + requirement_level: opt_in + - ref: graphql.source_schema.operation.name + requirement_level: opt_in + - ref: graphql.source_schema.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 + examples: [ "execute" ] + note: 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.field.schema_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 + examples: [ "resolve" ] + note: MUST be `resolve`. + - ref: graphql.field.alias + requirement_level: recommended + - ref: graphql.field.path + requirement_level: recommended + - ref: graphql.field.name + requirement_level: required + - ref: graphql.field.parent_type + requirement_level: required + - ref: graphql.field.schema_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 + examples: [ "dataloader_dispatch" ] + note: 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.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, + 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 + examples: [ "dataloader_batch" ] + note: 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 + examples: [ "subscription_event" ] + note: MUST be `subscription_event`. + - ref: graphql.operation.type + requirement_level: required + 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 diff --git a/model/graphql/spans.yml b/model/graphql/spans.yml deleted file mode 100644 index 3846135898..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: opt_in