Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

opentelemetry: use semconv attributes #3786

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Release type: patch

Use OTEL's semconv attribute registry for span attributes. Stops setting `component`, which has been deprecated.
27 changes: 24 additions & 3 deletions strawberry/extensions/tracing/opentelemetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
)

from opentelemetry import trace
from opentelemetry.semconv._incubating.attributes import graphql_attributes
from opentelemetry.trace import SpanKind

from strawberry.extensions import LifecycleStep, SchemaExtension
Expand Down Expand Up @@ -60,11 +61,16 @@ def on_operation(self) -> Generator[None, None, None]:
self._span_holder[LifecycleStep.OPERATION] = self._tracer.start_span(
span_name, kind=SpanKind.SERVER
)
self._span_holder[LifecycleStep.OPERATION].set_attribute("component", "graphql")

# set the name if we have it. if we don't, we might populate it after parsing.
if self._operation_name:
self._span_holder[LifecycleStep.OPERATION].set_attribute(
graphql_attributes.GRAPHQL_OPERATION_NAME, self._operation_name
)

if self.execution_context.query:
self._span_holder[LifecycleStep.OPERATION].set_attribute(
"query", self.execution_context.query
graphql_attributes.GRAPHQL_DOCUMENT, self.execution_context.query
)

yield
Expand All @@ -76,6 +82,22 @@ def on_operation(self) -> Generator[None, None, None]:
if not self._operation_name and self.execution_context.operation_name:
span_name = f"GraphQL Query: {self.execution_context.operation_name}"
self._span_holder[LifecycleStep.OPERATION].update_name(span_name)
self._span_holder[LifecycleStep.OPERATION].set_attribute(
graphql_attributes.GRAPHQL_OPERATION_NAME,
self.execution_context.operation_name,
)

# likewise for the operation type; we'll know it for sure after parsing.
# note that this means ``self.execution_context.operation_type`` must
# be kept in sync with ``graphql_attributes.GraphqlOperationTypeValues``.
if self.execution_context.operation_type:
self._span_holder[LifecycleStep.OPERATION].set_attribute(
graphql_attributes.GRAPHQL_OPERATION_TYPE,
graphql_attributes.GraphqlOperationTypeValues(
self.execution_context.operation_type.value.lower()
).value,
)

self._span_holder[LifecycleStep.OPERATION].end()

def on_validate(self) -> Generator[None, None, None]:
Expand Down Expand Up @@ -139,7 +161,6 @@ def convert_list_or_tuple_to_allowed_types(self, value: Iterable) -> str:
def add_tags(self, span: Span, info: GraphQLResolveInfo, kwargs: Any) -> None:
graphql_path = ".".join(map(str, get_path_from_info(info)))

span.set_attribute("component", "graphql")
span.set_attribute("graphql.parentType", info.parent_type.name)
span.set_attribute("graphql.path", graphql_path)

Expand Down
20 changes: 16 additions & 4 deletions tests/schema/extensions/test_opentelemetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
from unittest.mock import MagicMock

import pytest
from opentelemetry.semconv._incubating.attributes.graphql_attributes import (
GRAPHQL_DOCUMENT,
GRAPHQL_OPERATION_NAME,
GRAPHQL_OPERATION_TYPE,
GraphqlOperationTypeValues,
)
from opentelemetry.trace import SpanKind
from pytest_mock import MockerFixture

Expand Down Expand Up @@ -66,12 +72,14 @@ async def test_opentelemetry_sync_uses_global_tracer(global_tracer_mock):
def _instrumentation_stages(mocker, query):
return [
mocker.call("GraphQL Query", kind=SpanKind.SERVER),
mocker.call().set_attribute("component", "graphql"),
mocker.call().set_attribute("query", query),
mocker.call().set_attribute(GRAPHQL_DOCUMENT, query),
mocker.call("GraphQL Parsing", context=mocker.ANY),
mocker.call().end(),
mocker.call("GraphQL Validation", context=mocker.ANY),
mocker.call().end(),
mocker.call().set_attribute(
GRAPHQL_OPERATION_TYPE, GraphqlOperationTypeValues.QUERY.value
),
mocker.call().end(),
]

Expand Down Expand Up @@ -101,7 +109,6 @@ async def test_open_tracing(global_tracer_mock, mocker):
[
mocker.call("GraphQL Resolving: person", context=mocker.ANY),
mocker.call().__enter__(),
mocker.call().__enter__().set_attribute("component", "graphql"),
mocker.call().__enter__().set_attribute("graphql.parentType", "Query"),
mocker.call().__enter__().set_attribute("graphql.path", "person"),
mocker.call().__exit__(None, None, None),
Expand All @@ -126,6 +133,7 @@ async def test_open_tracing_uses_operation_name(global_tracer_mock, mocker):
[
# if operation_name is supplied it is added to this span's tag
mocker.call("GraphQL Query: Example", kind=SpanKind.SERVER),
mocker.call().set_attribute(GRAPHQL_OPERATION_NAME, "Example"),
*_instrumentation_stages(mocker, query)[1:],
]
)
Expand Down Expand Up @@ -154,12 +162,16 @@ def generate_trace(*args: str, **kwargs: Any):

await schema.execute(query)

# if operation_name is in the query, it is added to this span's name
tracers[0].update_name.assert_has_calls(
[
# if operation_name is supplied it is added to this span's tag
mocker.call("GraphQL Query: Example"),
]
)
# and the span's attributes
tracers[0].set_attribute.assert_has_calls(
[mocker.call(GRAPHQL_OPERATION_NAME, "Example")]
)


@pytest.mark.asyncio
Expand Down
Loading