From c42c8b11a353c3036782ed89e3de580de5af3de9 Mon Sep 17 00:00:00 2001 From: Uma Annamalai Date: Wed, 15 Sep 2021 15:05:21 -0700 Subject: [PATCH] Add framework info to resolver metrics. --- newrelic/api/graphql_trace.py | 22 +++++++++++++++++++ newrelic/core/graphql_node.py | 6 ++--- newrelic/hooks/framework_graphql.py | 19 +++++++++------- tests/framework_ariadne/test_application.py | 14 ++++++------ .../test_application_async.py | 4 ++-- tests/framework_ariadne/test_asgi.py | 4 ++-- tests/framework_ariadne/test_wsgi.py | 4 ++-- tests/framework_graphene/test_application.py | 12 +++++----- .../framework_strawberry/test_application.py | 8 +++---- .../test_application_async.py | 4 ++-- tests/framework_strawberry/test_asgi.py | 4 ++-- tox.ini | 3 ++- 12 files changed, 65 insertions(+), 39 deletions(-) diff --git a/newrelic/api/graphql_trace.py b/newrelic/api/graphql_trace.py index 6405c92e81..9355a65dbc 100644 --- a/newrelic/api/graphql_trace.py +++ b/newrelic/api/graphql_trace.py @@ -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) @@ -157,6 +178,7 @@ def create_node(self): guid=self.guid, agent_attributes=self.agent_attributes, user_attributes=self.user_attributes, + product=self.product, ) diff --git a/newrelic/core/graphql_node.py b/newrelic/core/graphql_node.py index 53622378a5..e7e46f5cb6 100644 --- a/newrelic/core/graphql_node.py +++ b/newrelic/core/graphql_node.py @@ -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 @@ -62,7 +62,7 @@ class GraphQLResolverNode(_GraphQLResolverNode, GraphQLNodeMixin): @property def name(self): field_name = self.field_name or "" - product = "GraphQL" + product = self.product name = 'GraphQL/resolve/%s/%s' % (product, field_name) @@ -74,7 +74,7 @@ def time_metrics(self, stats, root, parent): """ field_name = self.field_name or "" - product = "GraphQL" + product = self.product # Determine the scoped metric diff --git a/newrelic/hooks/framework_graphql.py b/newrelic/hooks/framework_graphql.py index 6835fca77d..688b309c1b 100644 --- a/newrelic/hooks/framework_graphql.py +++ b/newrelic/hooks/framework_graphql.py @@ -442,12 +442,6 @@ 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 @@ -455,8 +449,17 @@ def wrap_graphql_impl(wrapped, instance, args, kwargs): with GraphQLOperationTrace() as trace: trace.statement = graphql_statement(query) - if is_graphene: # ex: "" - 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 diff --git a/tests/framework_ariadne/test_application.py b/tests/framework_ariadne/test_application.py index 79e84bbe98..0dbeecbce7 100644 --- a/tests/framework_ariadne/test_application.py +++ b/tests/framework_ariadne/test_application.py @@ -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//storage", 1), ("GraphQL/operation/Ariadne/mutation//storage_add.string", 1), ] @@ -181,7 +181,7 @@ def _test(): def test_middleware(app, graphql_run, is_graphql_2): _test_middleware_metrics = [ ("GraphQL/operation/Ariadne/query//hello", 1), - ("GraphQL/resolve/GraphQL/hello", 1), + ("GraphQL/resolve/Strawberry/hello", 1), ("Function/test_application:example_middleware", 1), ] @@ -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), @@ -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), @@ -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///', 1), + ('GraphQL/operation/Ariadne///', 1), ] _test_exception_rollup_metrics = [ ("Errors/all", 1), @@ -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", diff --git a/tests/framework_ariadne/test_application_async.py b/tests/framework_ariadne/test_application_async.py index d7ea98a75d..7b9cf18c4b 100644 --- a/tests/framework_ariadne/test_application_async.py +++ b/tests/framework_ariadne/test_application_async.py @@ -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//storage", 1), ("GraphQL/operation/Ariadne/mutation//storage_add.string", 1), ] diff --git a/tests/framework_ariadne/test_asgi.py b/tests/framework_ariadne/test_asgi.py index e7a8e9e994..8bcd81216a 100644 --- a/tests/framework_ariadne/test_asgi.py +++ b/tests/framework_ariadne/test_asgi.py @@ -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//storage_add.string", 1), ] _test_query_scoped_metrics = [ - ("GraphQL/resolve/GraphQL/storage", 1), + ("GraphQL/resolve/Ariadne/storage", 1), ("GraphQL/operation/Ariadne/query//storage", 1), ] _test_unscoped_metrics = [ diff --git a/tests/framework_ariadne/test_wsgi.py b/tests/framework_ariadne/test_wsgi.py index 6b3b664a97..29e7d9259c 100644 --- a/tests/framework_ariadne/test_wsgi.py +++ b/tests/framework_ariadne/test_wsgi.py @@ -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//storage_add.string", 1), ] _test_query_scoped_metrics = [ - ("GraphQL/resolve/GraphQL/storage", 1), + ("GraphQL/resolve/Ariadne/storage", 1), ("GraphQL/operation/Ariadne/query//storage", 1), ] _test_unscoped_metrics = [ diff --git a/tests/framework_graphene/test_application.py b/tests/framework_graphene/test_application.py index 719f1834c0..0592463f7f 100644 --- a/tests/framework_graphene/test_application.py +++ b/tests/framework_graphene/test_application.py @@ -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//storage", 1), ("GraphQL/operation/Graphene/mutation//storage_add.string", 1), ] @@ -181,7 +181,7 @@ def _test(): def test_middleware(app, graphql_run, is_graphql_2): _test_middleware_metrics = [ ("GraphQL/operation/Graphene/query//hello", 1), - ("GraphQL/resolve/GraphQL/hello", 1), + ("GraphQL/resolve/Graphene/hello", 1), ("Function/test_application:example_middleware", 1), ] @@ -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), @@ -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), @@ -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", diff --git a/tests/framework_strawberry/test_application.py b/tests/framework_strawberry/test_application.py index e505dcb222..b68b825fff 100644 --- a/tests/framework_strawberry/test_application.py +++ b/tests/framework_strawberry/test_application.py @@ -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//storage", 1), ("GraphQL/operation/Strawberry/mutation//storage_add", 1), ] @@ -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), @@ -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", diff --git a/tests/framework_strawberry/test_application_async.py b/tests/framework_strawberry/test_application_async.py index 8ad77759ae..3af55b6d46 100644 --- a/tests/framework_strawberry/test_application_async.py +++ b/tests/framework_strawberry/test_application_async.py @@ -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//storage", 1), ("GraphQL/operation/Strawberry/mutation//storage_add", 1), ] diff --git a/tests/framework_strawberry/test_asgi.py b/tests/framework_strawberry/test_asgi.py index 84d168a037..c64fdcd563 100644 --- a/tests/framework_strawberry/test_asgi.py +++ b/tests/framework_strawberry/test_asgi.py @@ -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//storage_add", 1), ] _test_query_scoped_metrics = [ - ("GraphQL/resolve/GraphQL/storage", 1), + ("GraphQL/resolve/Strawberry/storage", 1), ("GraphQL/operation/Strawberry/query//storage", 1), ] _test_unscoped_metrics = [ diff --git a/tox.ini b/tox.ini index 54c6e0ce85..b759336776 100644 --- a/tox.ini +++ b/tox.ini @@ -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, @@ -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