Skip to content

Commit a1bf688

Browse files
umaannamalaiTimPansino
authored andcommitted
Add GraphQL Framework Info to Metrics (#367)
* Add graphene and ariadne product info. * Add strawberry product info. * Add framework info to resolver metrics. * Correct Ariadne tests. * Update Starlette tests with Graphene details. * Add protobuf version dependency to agent_streaming tests. * Specify python version for protobuf dependency. * Remove graphql node product property.
1 parent acf43cb commit a1bf688

14 files changed

+141
-93
lines changed

newrelic/api/graphql_trace.py

+24
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def __init__(self, **kwargs):
3636
self.graphql = None
3737
self.graphql_format = None
3838
self.statement = None
39+
self.product = "GraphQL"
3940

4041
def __repr__(self):
4142
return "<%s object at 0x%x %s>" % (
@@ -89,6 +90,7 @@ def create_node(self):
8990
operation_type=self.operation_type,
9091
deepest_path=self.deepest_path,
9192
graphql=self.graphql,
93+
product=self.product,
9294
)
9395

9496
def set_transaction_name(self, priority=None):
@@ -135,10 +137,31 @@ class GraphQLResolverTrace(TimeTrace):
135137
def __init__(self, field_name=None, **kwargs):
136138
super(GraphQLResolverTrace, self).__init__(**kwargs)
137139
self.field_name = field_name
140+
self._product = None
138141

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

145+
def __enter__(self):
146+
super(GraphQLResolverTrace, self).__enter__()
147+
_ = self.product # Cache product value
148+
return self
149+
150+
@property
151+
def product(self):
152+
if not self._product:
153+
# Find GraphQLOperationTrace to obtain stored product info
154+
parent = self # init to self for loop start
155+
while parent is not None and not isinstance(parent, GraphQLOperationTrace):
156+
parent = getattr(parent, "parent", None)
157+
158+
if parent is not None:
159+
self._product = getattr(parent, "product", "GraphQL")
160+
else:
161+
self._product = "GraphQL"
162+
163+
return self._product
164+
142165
def finalize_data(self, *args, **kwargs):
143166
self._add_agent_attribute("graphql.field.name", self.field_name)
144167

@@ -155,6 +178,7 @@ def create_node(self):
155178
guid=self.guid,
156179
agent_attributes=self.agent_attributes,
157180
user_attributes=self.user_attributes,
181+
product=self.product,
158182
)
159183

160184

newrelic/core/graphql_node.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,13 @@
2323
_GraphQLOperationNode = namedtuple('_GraphQLNode',
2424
['operation_type', 'operation_name', 'deepest_path', 'graphql',
2525
'children', 'start_time', 'end_time', 'duration', 'exclusive', 'guid',
26-
'agent_attributes', 'user_attributes'])
26+
'agent_attributes', 'user_attributes', 'product'])
2727

2828
_GraphQLResolverNode = namedtuple('_GraphQLNode',
2929
['field_name', 'children', 'start_time', 'end_time', 'duration',
30-
'exclusive', 'guid', 'agent_attributes', 'user_attributes'])
30+
'exclusive', 'guid', 'agent_attributes', 'user_attributes', 'product'])
3131

3232
class GraphQLNodeMixin(GenericNodeMixin):
33-
@property
34-
def product(self):
35-
return "GraphQL"
36-
3733
def trace_node(self, stats, root, connections):
3834
name = root.string_table.cache(self.name)
3935

newrelic/hooks/framework_ariadne.py

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def wrap_graphql_sync(wrapped, instance, args, kwargs):
6060
transaction.set_transaction_name(callable_name(wrapped), "GraphQL", priority=10)
6161

6262
with GraphQLOperationTrace() as trace:
63+
trace.product = "Ariadne"
6364
trace.statement = graphql_statement(query)
6465
with ErrorTrace(ignore=ignore_graphql_duplicate_exception):
6566
return wrapped(*args, **kwargs)
@@ -93,6 +94,7 @@ async def wrap_graphql(wrapped, instance, args, kwargs):
9394
transaction.set_transaction_name(callable_name(wrapped), "GraphQL", priority=10)
9495

9596
with GraphQLOperationTrace() as trace:
97+
trace.product = "Ariadne"
9698
trace.statement = graphql_statement(query)
9799
with ErrorTrace(ignore=ignore_graphql_duplicate_exception):
98100
result = wrapped(*args, **kwargs)

newrelic/hooks/framework_graphql.py

+20-4
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,12 @@ def bind_resolve_field_v2(exe_context, parent_type, source, field_asts, parent_i
369369
return parent_type, field_asts, field_path
370370

371371

372+
def graphene_framework_details():
373+
import graphene
374+
375+
return ("Graphene", getattr(graphene, "__version__", None))
376+
377+
372378
def wrap_resolve_field(wrapped, instance, args, kwargs):
373379
transaction = current_transaction()
374380
if transaction is None:
@@ -402,7 +408,7 @@ def wrap_resolve_field(wrapped, instance, args, kwargs):
402408

403409

404410
def bind_graphql_impl_query(schema, source, *args, **kwargs):
405-
return source
411+
return schema, source
406412

407413

408414
def bind_execute_graphql_query(
@@ -416,8 +422,7 @@ def bind_execute_graphql_query(
416422
backend=None,
417423
**execute_options
418424
):
419-
420-
return request_string
425+
return schema, request_string
421426

422427

423428
def wrap_graphql_impl(wrapped, instance, args, kwargs):
@@ -433,7 +438,7 @@ def wrap_graphql_impl(wrapped, instance, args, kwargs):
433438
bind_query = bind_graphql_impl_query
434439

435440
try:
436-
query = bind_query(*args, **kwargs)
441+
schema, query = bind_query(*args, **kwargs)
437442
except TypeError:
438443
return wrapped(*args, **kwargs)
439444

@@ -444,6 +449,17 @@ def wrap_graphql_impl(wrapped, instance, args, kwargs):
444449

445450
with GraphQLOperationTrace() as trace:
446451
trace.statement = graphql_statement(query)
452+
453+
# Handle Graphene Schemas
454+
try:
455+
from graphene.types.schema import Schema as GrapheneSchema
456+
if isinstance(schema, GrapheneSchema):
457+
trace.product = "Graphene"
458+
framework = graphene_framework_details()
459+
transaction.add_framework_info(name=framework[0], version=framework[1])
460+
except ImportError:
461+
pass
462+
447463
with ErrorTrace(ignore=ignore_graphql_duplicate_exception):
448464
result = wrapped(*args, **kwargs)
449465
return result

newrelic/hooks/framework_strawberry.py

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ def wrap_execute_sync(wrapped, instance, args, kwargs):
5757
transaction.set_transaction_name(callable_name(wrapped), "GraphQL", priority=10)
5858

5959
with GraphQLOperationTrace() as trace:
60+
trace.product = "Strawberry"
6061
trace.statement = graphql_statement(query)
6162
with ErrorTrace(ignore=ignore_graphql_duplicate_exception):
6263
return wrapped(*args, **kwargs)
@@ -83,6 +84,7 @@ async def wrap_execute(wrapped, instance, args, kwargs):
8384
transaction.set_transaction_name(callable_name(wrapped), "GraphQL", priority=10)
8485

8586
with GraphQLOperationTrace() as trace:
87+
trace.product = "Strawberry"
8688
trace.statement = graphql_statement(query)
8789
with ErrorTrace(ignore=ignore_graphql_duplicate_exception):
8890
return await wrapped(*args, **kwargs)

tests/framework_ariadne/test_application.py

+17-17
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ def error_middleware(next, root, info, **args): # pylint: disable=W0622
8282
("OtherTransaction/all", 1),
8383
("GraphQL/all", 1),
8484
("GraphQL/allOther", 1),
85-
("GraphQL/GraphQL/all", 1),
86-
("GraphQL/GraphQL/allOther", 1),
85+
("GraphQL/Ariadne/all", 1),
86+
("GraphQL/Ariadne/allOther", 1),
8787
]
8888

8989

@@ -118,17 +118,17 @@ def test_query_and_mutation(app, graphql_run):
118118
("Python/Framework/GraphQL/%s" % version, 1),
119119
]
120120
_test_mutation_scoped_metrics = [
121-
("GraphQL/resolve/GraphQL/storage", 1),
122-
("GraphQL/resolve/GraphQL/storage_add", 1),
123-
("GraphQL/operation/GraphQL/query/<anonymous>/storage", 1),
124-
("GraphQL/operation/GraphQL/mutation/<anonymous>/storage_add.string", 1),
121+
("GraphQL/resolve/Ariadne/storage", 1),
122+
("GraphQL/resolve/Ariadne/storage_add", 1),
123+
("GraphQL/operation/Ariadne/query/<anonymous>/storage", 1),
124+
("GraphQL/operation/Ariadne/mutation/<anonymous>/storage_add.string", 1),
125125
]
126126
_test_mutation_unscoped_metrics = [
127127
("OtherTransaction/all", 1),
128128
("GraphQL/all", 2),
129-
("GraphQL/GraphQL/all", 2),
129+
("GraphQL/Ariadne/all", 2),
130130
("GraphQL/allOther", 2),
131-
("GraphQL/GraphQL/allOther", 2),
131+
("GraphQL/Ariadne/allOther", 2),
132132
] + _test_mutation_scoped_metrics
133133

134134
_expected_mutation_operation_attributes = {
@@ -180,8 +180,8 @@ def _test():
180180
@dt_enabled
181181
def test_middleware(app, graphql_run, is_graphql_2):
182182
_test_middleware_metrics = [
183-
("GraphQL/operation/GraphQL/query/<anonymous>/hello", 1),
184-
("GraphQL/resolve/GraphQL/hello", 1),
183+
("GraphQL/operation/Ariadne/query/<anonymous>/hello", 1),
184+
("GraphQL/resolve/Ariadne/hello", 1),
185185
("Function/test_application:example_middleware", 1),
186186
]
187187

@@ -212,8 +212,8 @@ def test_exception_in_middleware(app, graphql_run):
212212

213213
# Metrics
214214
_test_exception_scoped_metrics = [
215-
("GraphQL/operation/GraphQL/query/MyQuery/%s" % field, 1),
216-
("GraphQL/resolve/GraphQL/%s" % field, 1),
215+
("GraphQL/operation/Ariadne/query/MyQuery/%s" % field, 1),
216+
("GraphQL/resolve/Ariadne/%s" % field, 1),
217217
]
218218
_test_exception_rollup_metrics = [
219219
("Errors/all", 1),
@@ -262,8 +262,8 @@ def test_exception_in_resolver(app, graphql_run, field):
262262

263263
# Metrics
264264
_test_exception_scoped_metrics = [
265-
("GraphQL/operation/GraphQL/query/MyQuery/%s" % field, 1),
266-
("GraphQL/resolve/GraphQL/%s" % field, 1),
265+
("GraphQL/operation/Ariadne/query/MyQuery/%s" % field, 1),
266+
("GraphQL/resolve/Ariadne/%s" % field, 1),
267267
]
268268
_test_exception_rollup_metrics = [
269269
("Errors/all", 1),
@@ -326,7 +326,7 @@ def test_exception_in_validation(app, graphql_run, is_graphql_2, query, exc_clas
326326
exc_class = callable_name(GraphQLError)
327327

328328
_test_exception_scoped_metrics = [
329-
# ('GraphQL/operation/GraphQL/<unknown>/<anonymous>/<unknown>', 1),
329+
('GraphQL/operation/Ariadne/<unknown>/<anonymous>/<unknown>', 1),
330330
]
331331
_test_exception_rollup_metrics = [
332332
("Errors/all", 1),
@@ -360,7 +360,7 @@ def _test():
360360

361361
@dt_enabled
362362
def test_operation_metrics_and_attrs(app, graphql_run):
363-
operation_metrics = [("GraphQL/operation/GraphQL/query/MyQuery/library", 1)]
363+
operation_metrics = [("GraphQL/operation/Ariadne/query/MyQuery/library", 1)]
364364
operation_attrs = {
365365
"graphql.operation.type": "query",
366366
"graphql.operation.name": "MyQuery",
@@ -388,7 +388,7 @@ def _test():
388388

389389
@dt_enabled
390390
def test_field_resolver_metrics_and_attrs(app, graphql_run):
391-
field_resolver_metrics = [("GraphQL/resolve/GraphQL/hello", 1)]
391+
field_resolver_metrics = [("GraphQL/resolve/Ariadne/hello", 1)]
392392
graphql_attrs = {
393393
"graphql.field.name": "hello",
394394
"graphql.field.parentType": "Query",

tests/framework_ariadne/test_application_async.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,17 @@ def test_query_and_mutation_async(app, graphql_run_async):
2828
("Python/Framework/GraphQL/%s" % version, 1),
2929
]
3030
_test_mutation_scoped_metrics = [
31-
("GraphQL/resolve/GraphQL/storage", 1),
32-
("GraphQL/resolve/GraphQL/storage_add", 1),
33-
("GraphQL/operation/GraphQL/query/<anonymous>/storage", 1),
34-
("GraphQL/operation/GraphQL/mutation/<anonymous>/storage_add.string", 1),
31+
("GraphQL/resolve/Ariadne/storage", 1),
32+
("GraphQL/resolve/Ariadne/storage_add", 1),
33+
("GraphQL/operation/Ariadne/query/<anonymous>/storage", 1),
34+
("GraphQL/operation/Ariadne/mutation/<anonymous>/storage_add.string", 1),
3535
]
3636
_test_mutation_unscoped_metrics = [
3737
("OtherTransaction/all", 1),
3838
("GraphQL/all", 2),
39-
("GraphQL/GraphQL/all", 2),
39+
("GraphQL/Ariadne/all", 2),
4040
("GraphQL/allOther", 2),
41-
("GraphQL/GraphQL/allOther", 2),
41+
("GraphQL/Ariadne/allOther", 2),
4242
] + _test_mutation_scoped_metrics
4343

4444
_expected_mutation_operation_attributes = {

tests/framework_ariadne/test_asgi.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,19 @@ def test_query_and_mutation_asgi(graphql_asgi_run):
3030
("Python/Framework/GraphQL/%s" % version, 1),
3131
]
3232
_test_mutation_scoped_metrics = [
33-
("GraphQL/resolve/GraphQL/storage_add", 1),
34-
("GraphQL/operation/GraphQL/mutation/<anonymous>/storage_add.string", 1),
33+
("GraphQL/resolve/Ariadne/storage_add", 1),
34+
("GraphQL/operation/Ariadne/mutation/<anonymous>/storage_add.string", 1),
3535
]
3636
_test_query_scoped_metrics = [
37-
("GraphQL/resolve/GraphQL/storage", 1),
38-
("GraphQL/operation/GraphQL/query/<anonymous>/storage", 1),
37+
("GraphQL/resolve/Ariadne/storage", 1),
38+
("GraphQL/operation/Ariadne/query/<anonymous>/storage", 1),
3939
]
4040
_test_unscoped_metrics = [
4141
("WebTransaction", 1),
4242
("GraphQL/all", 1),
43-
("GraphQL/GraphQL/all", 1),
43+
("GraphQL/Ariadne/all", 1),
4444
("GraphQL/allWeb", 1),
45-
("GraphQL/GraphQL/allWeb", 1),
45+
("GraphQL/Ariadne/allWeb", 1),
4646
]
4747
_test_mutation_unscoped_metrics = _test_unscoped_metrics + _test_mutation_scoped_metrics
4848
_test_query_unscoped_metrics = _test_unscoped_metrics + _test_query_scoped_metrics

tests/framework_ariadne/test_wsgi.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,20 @@ def test_query_and_mutation_wsgi(graphql_wsgi_run):
2626
("Python/Framework/GraphQL/%s" % version, 1),
2727
]
2828
_test_mutation_scoped_metrics = [
29-
("GraphQL/resolve/GraphQL/storage_add", 1),
30-
("GraphQL/operation/GraphQL/mutation/<anonymous>/storage_add.string", 1),
29+
("GraphQL/resolve/Ariadne/storage_add", 1),
30+
("GraphQL/operation/Ariadne/mutation/<anonymous>/storage_add.string", 1),
3131
]
3232
_test_query_scoped_metrics = [
33-
("GraphQL/resolve/GraphQL/storage", 1),
34-
("GraphQL/operation/GraphQL/query/<anonymous>/storage", 1),
33+
("GraphQL/resolve/Ariadne/storage", 1),
34+
("GraphQL/operation/Ariadne/query/<anonymous>/storage", 1),
3535
]
3636
_test_unscoped_metrics = [
3737
("WebTransaction", 1),
3838
("Python/WSGI/Response", 1),
3939
("GraphQL/all", 1),
40-
("GraphQL/GraphQL/all", 1),
40+
("GraphQL/Ariadne/all", 1),
4141
("GraphQL/allWeb", 1),
42-
("GraphQL/GraphQL/allWeb", 1),
42+
("GraphQL/Ariadne/allWeb", 1),
4343
]
4444
_test_mutation_unscoped_metrics = _test_unscoped_metrics + _test_mutation_scoped_metrics
4545
_test_query_unscoped_metrics = _test_unscoped_metrics + _test_query_scoped_metrics

0 commit comments

Comments
 (0)