diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0cee69e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "editor.rulers": [ + 80 + ], + "yaml.schemas": { + "https://raw.githubusercontent.com/open-telemetry/weaver/v0.16.1/schemas/semconv.schema.json": [ + "spec/**/*.yaml" + ] + }, + "json.schemaDownload.enable": true, + "markdown.extension.toc.levels": "2..6" +} \ No newline at end of file diff --git a/spec/registry.yaml b/spec/registry.yaml new file mode 100644 index 0000000..755c4ae --- /dev/null +++ b/spec/registry.yaml @@ -0,0 +1,88 @@ +groups: + - id: registry.graphql + type: attribute_group + display_name: GraphQL Attributes + 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", "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 + type: + members: + - id: query + value: "query" + brief: "GraphQL query operation" + stability: development + - id: mutation + value: "mutation" + brief: "GraphQL mutation operation" + stability: development + - id: subscription + value: "subscription" + brief: "GraphQL subscription operation" + stability: development + examples: ["query", "mutation", "subscription"] + - id: graphql.document.hash + brief: "The hash of the operation document." + type: string + stability: development + examples: + [ + "sha256:400483f38c08e8a3d3b972409c9dfb8e4a326e1b1940864932acd9f873d8664c", + ] + note: > + The hash algorithm used SHOULD be specified as part of the value (e.g., "sha256:..."). + This can be used for monitoring operation distribution and caching strategies. + - id: graphql.document.id + brief: > + The document identifier for persisted operations. + type: string + stability: development + examples: ["aa3e37c1bf54708e93f12c137afba004"] + note: > + This is a hash or identifier of the document provided by the user to identify + persisted operations. This attribute SHOULD only be set when using persisted operations. + - id: graphql.selection.name + brief: "The name of the selection that is being resolved. Either the field name or an alias." + type: string + stability: development + examples: ["newAddress", "bookTitle"] + note: > + If the field has an alias, this SHOULD be the alias name. Otherwise, it SHOULD be the field name. + - id: graphql.selection.path + brief: "The path of the selection that is being resolved." + type: string + stability: development + examples: ["person[0].address"] + note: > + The path represents the location of the field being resolved within the result structure. + - id: graphql.selection.field.name + brief: "The name of the field that is being resolved." + type: string + stability: development + examples: ["address", "name", "id"] + note: > + This is always the actual field name as defined in the schema, not an alias. + - id: graphql.selection.field.parent_type + brief: "The type that declares the field that is being resolved." + type: string + stability: development + examples: ["Person", "Query", "Mutation"] + note: > + This is the GraphQL type name that contains the field definition. + - id: graphql.selection.field.coordinate + brief: "The coordinate of the field that is being resolved." + type: string + stability: development + examples: ["Person.address", "Query.findBookById"] + note: > + The coordinate follows the format "{ParentType}.{fieldName}" and uniquely + identifies the field within the GraphQL schema. diff --git a/spec/spans.yml b/spec/spans.yml new file mode 100644 index 0000000..34abd63 --- /dev/null +++ b/spec/spans.yml @@ -0,0 +1,177 @@ +groups: + - id: span.graphql.server.request + type: span + stability: development + span_kind: server + brief: > + This span represents an incoming GraphQL request on a server implementation. + note: | + **Span name** SHOULD be of the format `{graphql.operation.type}` provided + `graphql.operation.type` is available. If `graphql.operation.type` is not available, + the span SHOULD be named `GraphQL Operation`. + + For persisted operations with a specified operation name, instrumentations 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 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 persisted operations, 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. + attributes: + - ref: graphql.operation.type + 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 persisted operations. + + - id: span.graphql.server.document.parsing + 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). + attributes: + - ref: graphql.operation.type + 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 persisted operations. + + - id: span.graphql.server.document.validation + 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. + attributes: + - ref: graphql.operation.type + requirement_level: required + - 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 persisted operations. + + - id: span.graphql.server.document.variable_coercion + 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. + attributes: + - ref: graphql.operation.type + requirement_level: required + - 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 persisted operations. + + - id: span.graphql.server.operation.execution + 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. + attributes: + - 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 persisted operations. + + - id: span.graphql.server.field.execution + type: span + stability: development + span_kind: internal + brief: > + This span represents the execution of a GraphQL field. + note: | + **Span name** SHOULD be `{graphql.selection.field.coordinate}`. + + This span covers the execution of an individual field, including both + synchronous and asynchronous resolvers. The span ends when the resolver + result is available. + + > **Warning** + > Creating spans for every resolver execution can result in traces with + > hundreds or thousands of spans, severely impacting performance and + > trace readability. Instrumentations MUST NOT create resolver execution + > spans by default for all resolvers. + + Instrumentations SHOULD provide configuration options to control which + resolvers generate spans. Recommended strategies include: + + - **Manual selection**: Allow developers to explicitly mark specific + resolvers for tracing (e.g., via annotations, decorators, or configuration) + - **Asynchronous resolvers only**: Only trace resolvers that return + promises or other asynchronous constructs + - **Depth-based filtering**: Only trace resolvers at the top N levels + of the query (e.g., top 2 levels) + - **Performance-based filtering**: Only trace resolvers that exceed + a certain execution time threshold + + The selection criteria SHOULD be documented clearly for users to + understand which resolvers will generate spans. + attributes: + - ref: graphql.selection.name + requirement_level: recommended + - ref: graphql.selection.path + requirement_level: recommended + - ref: graphql.selection.field.name + requirement_level: required + - ref: graphql.selection.field.parent_type + requirement_level: required + - ref: graphql.selection.field.coordinate + requirement_level: required