Skip to content

Commit

Permalink
Add framework info to resolver metrics.
Browse files Browse the repository at this point in the history
  • Loading branch information
umaannamalai committed Sep 15, 2021
1 parent 6560530 commit c42c8b1
Show file tree
Hide file tree
Showing 12 changed files with 65 additions and 39 deletions.
22 changes: 22 additions & 0 deletions newrelic/api/graphql_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,31 @@ class GraphQLResolverTrace(TimeTrace):
def __init__(self, field_name=None, **kwargs):
super(GraphQLResolverTrace, self).__init__(**kwargs)
self.field_name = field_name
self._product = None

def __repr__(self):
return "<%s object at 0x%x %s>" % (self.__class__.__name__, id(self), dict(field_name=self.field_name))

def __enter__(self):
super(GraphQLResolverTrace, self).__enter__()
_ = self.product # Cache product value
return self

@property
def product(self):
if not self._product:
# Find GraphQLOperationTrace to obtain stored product info
parent = self # init to self for loop start
while parent is not None and not isinstance(parent, GraphQLOperationTrace):
parent = getattr(parent, "parent", None)

if parent is not None:
self._product = getattr(parent, "product", "GraphQL")
else:
self._product = "GraphQL"

return self._product

def finalize_data(self, *args, **kwargs):
self._add_agent_attribute("graphql.field.name", self.field_name)

Expand All @@ -157,6 +178,7 @@ def create_node(self):
guid=self.guid,
agent_attributes=self.agent_attributes,
user_attributes=self.user_attributes,
product=self.product,
)


Expand Down
6 changes: 3 additions & 3 deletions newrelic/core/graphql_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

_GraphQLResolverNode = namedtuple('_GraphQLNode',
['field_name', 'children', 'start_time', 'end_time', 'duration',
'exclusive', 'guid', 'agent_attributes', 'user_attributes'])
'exclusive', 'guid', 'agent_attributes', 'user_attributes', 'product'])

class GraphQLNodeMixin(GenericNodeMixin):
@property
Expand Down Expand Up @@ -62,7 +62,7 @@ class GraphQLResolverNode(_GraphQLResolverNode, GraphQLNodeMixin):
@property
def name(self):
field_name = self.field_name or "<unknown>"
product = "GraphQL"
product = self.product

name = 'GraphQL/resolve/%s/%s' % (product, field_name)

Expand All @@ -74,7 +74,7 @@ def time_metrics(self, stats, root, parent):
"""

field_name = self.field_name or "<unknown>"
product = "GraphQL"
product = self.product

# Determine the scoped metric

Expand Down
19 changes: 11 additions & 8 deletions newrelic/hooks/framework_graphql.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,21 +442,24 @@ def wrap_graphql_impl(wrapped, instance, args, kwargs):
except TypeError:
return wrapped(*args, **kwargs)

is_graphene = "graphene" in str(type(schema))

if is_graphene:
framework = graphene_framework_details()
transaction.add_framework_info(name=framework[0], version=framework[1])

if hasattr(query, "body"):
query = query.body

transaction.set_transaction_name(callable_name(wrapped), "GraphQL", priority=10)

with GraphQLOperationTrace() as trace:
trace.statement = graphql_statement(query)
if is_graphene: # ex: "<class 'graphene.types.schema.Schema'>"
trace.product = "Graphene"

# Handle Graphene Schemas
try:
from graphene.types.schema import Schema as GrapheneSchema
if isinstance(schema, GrapheneSchema):
trace.product = "Graphene"
framework = graphene_framework_details()
transaction.add_framework_info(name=framework[0], version=framework[1])
except ImportError:
pass

with ErrorTrace(ignore=ignore_graphql_duplicate_exception):
result = wrapped(*args, **kwargs)
return result
Expand Down
14 changes: 7 additions & 7 deletions tests/framework_ariadne/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ def test_query_and_mutation(app, graphql_run):
("Python/Framework/GraphQL/%s" % version, 1),
]
_test_mutation_scoped_metrics = [
("GraphQL/resolve/GraphQL/storage", 1),
("GraphQL/resolve/GraphQL/storage_add", 1),
("GraphQL/resolve/Ariadne/storage", 1),
("GraphQL/resolve/Ariadne/storage_add", 1),
("GraphQL/operation/Ariadne/query/<anonymous>/storage", 1),
("GraphQL/operation/Ariadne/mutation/<anonymous>/storage_add.string", 1),
]
Expand Down Expand Up @@ -181,7 +181,7 @@ def _test():
def test_middleware(app, graphql_run, is_graphql_2):
_test_middleware_metrics = [
("GraphQL/operation/Ariadne/query/<anonymous>/hello", 1),
("GraphQL/resolve/GraphQL/hello", 1),
("GraphQL/resolve/Strawberry/hello", 1),
("Function/test_application:example_middleware", 1),
]

Expand Down Expand Up @@ -213,7 +213,7 @@ def test_exception_in_middleware(app, graphql_run):
# Metrics
_test_exception_scoped_metrics = [
("GraphQL/operation/Ariadne/query/MyQuery/%s" % field, 1),
("GraphQL/resolve/GraphQL/%s" % field, 1),
("GraphQL/resolve/Strawberry/%s" % field, 1),
]
_test_exception_rollup_metrics = [
("Errors/all", 1),
Expand Down Expand Up @@ -263,7 +263,7 @@ def test_exception_in_resolver(app, graphql_run, field):
# Metrics
_test_exception_scoped_metrics = [
("GraphQL/operation/Ariadne/query/MyQuery/%s" % field, 1),
("GraphQL/resolve/GraphQL/%s" % field, 1),
("GraphQL/resolve/Ariadne/%s" % field, 1),
]
_test_exception_rollup_metrics = [
("Errors/all", 1),
Expand Down Expand Up @@ -326,7 +326,7 @@ def test_exception_in_validation(app, graphql_run, is_graphql_2, query, exc_clas
exc_class = callable_name(GraphQLError)

_test_exception_scoped_metrics = [
('GraphQL/operation/GraphQL/<unknown>/<anonymous>/<unknown>', 1),
('GraphQL/operation/Ariadne/<unknown>/<anonymous>/<unknown>', 1),
]
_test_exception_rollup_metrics = [
("Errors/all", 1),
Expand Down Expand Up @@ -388,7 +388,7 @@ def _test():

@dt_enabled
def test_field_resolver_metrics_and_attrs(app, graphql_run):
field_resolver_metrics = [("GraphQL/resolve/GraphQL/hello", 1)]
field_resolver_metrics = [("GraphQL/resolve/Ariadne/hello", 1)]
graphql_attrs = {
"graphql.field.name": "hello",
"graphql.field.parentType": "Query",
Expand Down
4 changes: 2 additions & 2 deletions tests/framework_ariadne/test_application_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ def test_query_and_mutation_async(app, graphql_run_async):
("Python/Framework/GraphQL/%s" % version, 1),
]
_test_mutation_scoped_metrics = [
("GraphQL/resolve/GraphQL/storage", 1),
("GraphQL/resolve/GraphQL/storage_add", 1),
("GraphQL/resolve/Ariadne/storage", 1),
("GraphQL/resolve/Ariadne/storage_add", 1),
("GraphQL/operation/Ariadne/query/<anonymous>/storage", 1),
("GraphQL/operation/Ariadne/mutation/<anonymous>/storage_add.string", 1),
]
Expand Down
4 changes: 2 additions & 2 deletions tests/framework_ariadne/test_asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ def test_query_and_mutation_asgi(graphql_asgi_run):
("Python/Framework/GraphQL/%s" % version, 1),
]
_test_mutation_scoped_metrics = [
("GraphQL/resolve/GraphQL/storage_add", 1),
("GraphQL/resolve/Ariadne/storage_add", 1),
("GraphQL/operation/Ariadne/mutation/<anonymous>/storage_add.string", 1),
]
_test_query_scoped_metrics = [
("GraphQL/resolve/GraphQL/storage", 1),
("GraphQL/resolve/Ariadne/storage", 1),
("GraphQL/operation/Ariadne/query/<anonymous>/storage", 1),
]
_test_unscoped_metrics = [
Expand Down
4 changes: 2 additions & 2 deletions tests/framework_ariadne/test_wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ def test_query_and_mutation_wsgi(graphql_wsgi_run):
("Python/Framework/GraphQL/%s" % version, 1),
]
_test_mutation_scoped_metrics = [
("GraphQL/resolve/GraphQL/storage_add", 1),
("GraphQL/resolve/Ariadne/storage_add", 1),
("GraphQL/operation/Ariadne/mutation/<anonymous>/storage_add.string", 1),
]
_test_query_scoped_metrics = [
("GraphQL/resolve/GraphQL/storage", 1),
("GraphQL/resolve/Ariadne/storage", 1),
("GraphQL/operation/Ariadne/query/<anonymous>/storage", 1),
]
_test_unscoped_metrics = [
Expand Down
12 changes: 6 additions & 6 deletions tests/framework_graphene/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ def test_query_and_mutation(app, graphql_run):
("Python/Framework/GraphQL/%s" % version, 1),
]
_test_mutation_scoped_metrics = [
("GraphQL/resolve/GraphQL/storage", 1),
("GraphQL/resolve/GraphQL/storage_add", 1),
("GraphQL/resolve/Graphene/storage", 1),
("GraphQL/resolve/Graphene/storage_add", 1),
("GraphQL/operation/Graphene/query/<anonymous>/storage", 1),
("GraphQL/operation/Graphene/mutation/<anonymous>/storage_add.string", 1),
]
Expand Down Expand Up @@ -181,7 +181,7 @@ def _test():
def test_middleware(app, graphql_run, is_graphql_2):
_test_middleware_metrics = [
("GraphQL/operation/Graphene/query/<anonymous>/hello", 1),
("GraphQL/resolve/GraphQL/hello", 1),
("GraphQL/resolve/Graphene/hello", 1),
("Function/test_application:example_middleware", 1),
]

Expand Down Expand Up @@ -211,7 +211,7 @@ def test_exception_in_middleware(app, graphql_run):
# Metrics
_test_exception_scoped_metrics = [
("GraphQL/operation/Graphene/query/MyQuery/%s" % field, 1),
("GraphQL/resolve/GraphQL/%s" % field, 1),
("GraphQL/resolve/Graphene/%s" % field, 1),
]
_test_exception_rollup_metrics = [
("Errors/all", 1),
Expand Down Expand Up @@ -263,7 +263,7 @@ def test_exception_in_resolver(app, graphql_run, field):
# Metrics
_test_exception_scoped_metrics = [
("GraphQL/operation/Graphene/query/MyQuery/%s" % field, 1),
("GraphQL/resolve/GraphQL/%s" % field, 1),
("GraphQL/resolve/Graphene/%s" % field, 1),
]
_test_exception_rollup_metrics = [
("Errors/all", 1),
Expand Down Expand Up @@ -388,7 +388,7 @@ def _test():

@dt_enabled
def test_field_resolver_metrics_and_attrs(app, graphql_run):
field_resolver_metrics = [("GraphQL/resolve/GraphQL/hello", 1)]
field_resolver_metrics = [("GraphQL/resolve/Graphene/hello", 1)]
graphql_attrs = {
"graphql.field.name": "hello",
"graphql.field.parentType": "Query",
Expand Down
8 changes: 4 additions & 4 deletions tests/framework_strawberry/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ def test_query_and_mutation(app, graphql_run):
("Python/Framework/GraphQL/%s" % version, 1),
]
_test_mutation_scoped_metrics = [
("GraphQL/resolve/GraphQL/storage", 1),
("GraphQL/resolve/GraphQL/storage_add", 1),
("GraphQL/resolve/Strawberry/storage", 1),
("GraphQL/resolve/Strawberry/storage_add", 1),
("GraphQL/operation/Strawberry/query/<anonymous>/storage", 1),
("GraphQL/operation/Strawberry/mutation/<anonymous>/storage_add", 1),
]
Expand Down Expand Up @@ -189,7 +189,7 @@ def test_exception_in_resolver(app, graphql_run, field):
# Metrics
_test_exception_scoped_metrics = [
("GraphQL/operation/Strawberry/query/MyQuery/%s" % field, 1),
("GraphQL/resolve/GraphQL/%s" % field, 1),
("GraphQL/resolve/Strawberry/%s" % field, 1),
]
_test_exception_rollup_metrics = [
("Errors/all", 1),
Expand Down Expand Up @@ -314,7 +314,7 @@ def _test():

@dt_enabled
def test_field_resolver_metrics_and_attrs(app, graphql_run):
field_resolver_metrics = [("GraphQL/resolve/GraphQL/hello", 1)]
field_resolver_metrics = [("GraphQL/resolve/Strawberry/hello", 1)]
graphql_attrs = {
"graphql.field.name": "hello",
"graphql.field.parentType": "Query",
Expand Down
4 changes: 2 additions & 2 deletions tests/framework_strawberry/test_application_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ def test_query_and_mutation_async(app, graphql_run_async):
("Python/Framework/GraphQL/%s" % version, 1),
]
_test_mutation_scoped_metrics = [
("GraphQL/resolve/GraphQL/storage", 1),
("GraphQL/resolve/GraphQL/storage_add", 1),
("GraphQL/resolve/Strawberry/storage", 1),
("GraphQL/resolve/Strawberry/storage_add", 1),
("GraphQL/operation/Strawberry/query/<anonymous>/storage", 1),
("GraphQL/operation/Strawberry/mutation/<anonymous>/storage_add", 1),
]
Expand Down
4 changes: 2 additions & 2 deletions tests/framework_strawberry/test_asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ def test_query_and_mutation_asgi(graphql_asgi_run):
("Python/Framework/GraphQL/%s" % version, 1),
]
_test_mutation_scoped_metrics = [
("GraphQL/resolve/GraphQL/storage_add", 1),
("GraphQL/resolve/Strawberry/storage_add", 1),
("GraphQL/operation/Strawberry/mutation/<anonymous>/storage_add", 1),
]
_test_query_scoped_metrics = [
("GraphQL/resolve/GraphQL/storage", 1),
("GraphQL/resolve/Strawberry/storage", 1),
("GraphQL/operation/Strawberry/query/<anonymous>/storage", 1),
]
_test_unscoped_metrics = [
Expand Down
3 changes: 2 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ envlist =
python-framework_graphql-{py36,py37,py38,py39,pypy3}-graphql03,
python-framework_graphql-py37-graphql{0202,0203,0300,0301,master},
grpc-framework_grpc-{py27,py36}-grpc0125,
grpc-framework_grpc-{py27,py36,py37,py38,py39}-grpclatest,
grpc-framework_grpc-{py36,py37,py38,py39}-grpclatest,
python-framework_pyramid-{pypy,py27,py38}-Pyramid0104,
python-framework_pyramid-{pypy,py27,pypy3,py36,py37,py38,py39}-Pyramid0110-cornice,
python-framework_pyramid-{pypy3,py36,py37,py38,py39}-Pyramidmaster,
Expand Down Expand Up @@ -266,6 +266,7 @@ deps =
framework_graphql-graphqlmaster: https://github.com/graphql-python/graphql-core/archive/main.zip
framework_grpc-grpc0125: grpcio<1.26
framework_grpc-grpc0125: grpcio-tools<1.26
framework_grpc-grpc0125: protobuf<3.18.0
framework_grpc-grpclatest: grpcio
framework_grpc-grpclatest: grpcio-tools
framework_pyramid: routes
Expand Down

0 comments on commit c42c8b1

Please sign in to comment.