Skip to content

Commit 497daa3

Browse files
authored
add tracing for validation and parsing (#42)
1 parent 7ddfaeb commit 497daa3

File tree

6 files changed

+81
-42
lines changed

6 files changed

+81
-42
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Change Log
22

3+
# 2.6.1
4+
5+
* Tracing support for validation and parsing
6+
37
# 2.6.0
48

59
* Dropping support for Python 2

graphene_tornado/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = '2.6.0'
1+
__version__ = '2.6.1'
22

33
__all__ = [
44
'__version__'

graphene_tornado/ext/opencensus/opencensus_tracing_extension.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,26 @@ async def on_request_ended(errors):
4242
return on_request_ended
4343

4444
async def parsing_started(self, query_string):
45-
pass
45+
tracer = execution_context.get_opencensus_tracer()
46+
47+
tracer.start_span('gql_parsing')
48+
49+
async def on_parsing_ended(errors):
50+
tracer = execution_context.get_opencensus_tracer()
51+
tracer.end_span()
52+
53+
return on_parsing_ended
4654

4755
async def validation_started(self):
48-
pass
56+
tracer = execution_context.get_opencensus_tracer()
57+
58+
tracer.start_span('gql_validation')
59+
60+
async def on_validation_ended(errors):
61+
tracer = execution_context.get_opencensus_tracer()
62+
tracer.end_span()
63+
64+
return on_validation_ended
4965

5066
async def execution_started(self, schema, document, root, context, variables, operation_name, request_context):
5167
if operation_name:

graphene_tornado/ext/opencensus/tests/test_opencensus_tracing.py

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
from typing import Optional, Any, List, Union, Callable
2+
13
import pytest
2-
import six
34
import tornado
4-
from graphql import parse
5-
from opencensus.trace import tracer as tracer_module
5+
from graphql import parse, GraphQLBackend
6+
from opencensus.trace import tracer as tracer_module, execution_context
67
from opencensus.trace.base_exporter import Exporter
78
from opencensus.trace.propagation.google_cloud_format import GoogleCloudFormatPropagator
89
from opencensus.trace.samplers import AlwaysOnSampler
@@ -11,25 +12,49 @@
1112
from graphene_tornado.ext.apollo_engine_reporting.tests.schema import schema
1213
from graphene_tornado.ext.apollo_engine_reporting.tests.test_engine_extension import QUERY
1314
from graphene_tornado.ext.opencensus.opencensus_tracing_extension import OpenCensusExtension
15+
from graphene_tornado.graphql_extension import GraphQLExtension
1416
from graphene_tornado.tests.http_helper import HttpHelper
1517
from graphene_tornado.tests.test_graphql import response_json, url_string, GRAPHQL_HEADER
1618
from graphene_tornado.tornado_graphql_handler import TornadoGraphQLHandler
1719

1820

21+
class GQLHandler(TornadoGraphQLHandler):
22+
23+
def initialize(self, schema=None, executor=None, middleware: Optional[Any] = None, root_value: Any = None,
24+
graphiql: bool = False, pretty: bool = False, batch: bool = False, backend: GraphQLBackend = None,
25+
extensions: List[Union[Callable[[], GraphQLExtension], GraphQLExtension]] = None,
26+
exporter=None):
27+
super().initialize(schema, executor, middleware, root_value, graphiql, pretty, batch, backend, extensions)
28+
execution_context.set_opencensus_tracer(tracer_module.Tracer(
29+
sampler=AlwaysOnSampler(),
30+
exporter=exporter,
31+
propagator=GoogleCloudFormatPropagator()
32+
)
33+
)
34+
35+
def on_finish(self) -> None:
36+
tracer = execution_context.get_opencensus_tracer()
37+
tracer.finish()
38+
39+
1940
class ExampleOpenCensusApplication(tornado.web.Application):
2041

21-
def __init__(self):
42+
def __init__(self, exporter):
2243
extension = lambda: OpenCensusExtension()
2344
handlers = [
24-
(r'/graphql', TornadoGraphQLHandler, dict(graphiql=True, schema=schema, extensions=[extension])),
25-
(r'/graphql/batch', TornadoGraphQLHandler, dict(graphiql=True, schema=schema, batch=True)),
45+
(r'/graphql', GQLHandler, dict(graphiql=True, schema=schema, extensions=[extension], exporter=exporter)),
2646
]
2747
tornado.web.Application.__init__(self, handlers)
2848

2949

3050
@pytest.fixture
31-
def app():
32-
return ExampleOpenCensusApplication()
51+
def app(exporter):
52+
return ExampleOpenCensusApplication(exporter)
53+
54+
55+
@pytest.fixture
56+
def app(exporter):
57+
return ExampleOpenCensusApplication(exporter)
3358

3459

3560
@pytest.fixture
@@ -39,13 +64,7 @@ def http_helper(http_client, base_url):
3964

4065
@pytest.fixture
4166
def exporter():
42-
exporter = CapturingExporter()
43-
tracer_module.Tracer(
44-
sampler=AlwaysOnSampler(),
45-
exporter=exporter,
46-
propagator=GoogleCloudFormatPropagator()
47-
)
48-
return exporter
67+
return CapturingExporter()
4968

5069

5170
@pytest.mark.gen_test()
@@ -54,28 +73,28 @@ def test_traces_match_query(http_helper, exporter):
5473
assert response.code == 200
5574
assert 'data' in response_json(response)
5675

57-
# OpenCensus is quite ready for Python3+Tornado yet
58-
if six.PY3:
59-
return
60-
61-
spans = exporter.spans
62-
63-
assert spans[0][0].name == 'author'
64-
assert spans[0][0].parent_span_id == spans[6][0].span_id
65-
assert spans[1][0].name == 'aBoolean'
66-
assert spans[1][0].parent_span_id == spans[6][0].span_id
67-
assert spans[2][0].name == 'author.name'
68-
assert spans[2][0].parent_span_id == spans[6][0].span_id
69-
assert spans[3][0].name == 'author.posts'
70-
assert spans[3][0].parent_span_id == spans[6][0].span_id
71-
assert spans[4][0].name == 'author.posts.0.id'
72-
assert spans[4][0].parent_span_id == spans[6][0].span_id
73-
assert spans[5][0].name == 'author.posts.1.id'
74-
assert spans[5][0].parent_span_id == spans[6][0].span_id
75-
76-
assert spans[6][0].name == 'gql[b5c7307ba564]'
77-
assert spans[6][0].parent_span_id is None
78-
assert spans[6][0].attributes.get('signature', None) == default_engine_reporting_signature(parse(QUERY), '')
76+
parent = exporter.spans.pop()[0]
77+
78+
assert parent.name == 'gql[b5c7307ba564]'
79+
assert parent.parent_span_id is None
80+
assert parent.attributes.get('signature', None) == default_engine_reporting_signature(parse(QUERY), '')
81+
82+
spans = [span for span_list in exporter.spans for span in span_list]
83+
84+
expected = [
85+
'gql_parsing',
86+
'gql_validation',
87+
'author',
88+
'aBoolean',
89+
'author.name',
90+
'author.posts',
91+
'author.posts.0.id',
92+
'author.posts.1.id'
93+
]
94+
95+
for span, exp in zip(spans, expected):
96+
assert span.name == exp
97+
assert span.parent_span_id == parent.span_id
7998

8099

81100
class CapturingExporter(Exporter):

requirements-test.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ coveralls==1.5.1
22
mock==2.0.0
33
pytest==4.4.1
44
pytest-cov==2.6.1
5-
pytest-tornado==0.7.0
5+
pytest-tornado==0.8.0

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ six>=1.10.0
22
json-stable-stringify-python==0.2
33
graphene>=2.0.1
44
Jinja2>=2.10.1, <2.11.0
5-
opencensus>=0.7.3
5+
opencensus>=0.7.7
66
protobuf>=3.7.1
77
tornado>=5.1.0
88
tornado-retry-client==0.6.1

0 commit comments

Comments
 (0)