Skip to content
This repository was archived by the owner on Sep 17, 2025. It is now read-only.

Commit 039fe6c

Browse files
Liudmila Molkovaliyanhui1228
authored andcommitted
Set status on django request span even if child span was not finished (#308)
1 parent 3e41879 commit 039fe6c

2 files changed

Lines changed: 80 additions & 17 deletions

File tree

opencensus/trace/ext/django/middleware.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
HTTP_STATUS_CODE = attributes_helper.COMMON_ATTRIBUTES['HTTP_STATUS_CODE']
3434

3535
REQUEST_THREAD_LOCAL_KEY = 'django_request'
36+
SPAN_THREAD_LOCAL_KEY = 'django_span'
3637

3738
BLACKLIST_PATHS = 'BLACKLIST_PATHS'
3839
GCP_EXPORTER_PROJECT = 'GCP_EXPORTER_PROJECT'
@@ -70,12 +71,21 @@ def _get_django_request():
7071
return execution_context.get_opencensus_attr(REQUEST_THREAD_LOCAL_KEY)
7172

7273

74+
def _get_django_span():
75+
"""Get Django span from thread local.
76+
77+
:rtype: str
78+
:returns: Django request.
79+
"""
80+
return execution_context.get_opencensus_attr(SPAN_THREAD_LOCAL_KEY)
81+
82+
7383
def _get_current_tracer():
7484
"""Get the current request tracer."""
7585
return execution_context.get_opencensus_tracer()
7686

7787

78-
def _set_django_attributes(tracer, request):
88+
def _set_django_attributes(span, request):
7989
"""Set the django related attributes."""
8090
django_user = getattr(request, 'user', None)
8191

@@ -87,10 +97,10 @@ def _set_django_attributes(tracer, request):
8797

8898
# User id is the django autofield for User model as the primary key
8999
if user_id is not None:
90-
tracer.add_attribute_to_current_span('django.user.id', str(user_id))
100+
span.add_attribute('django.user.id', str(user_id))
91101

92102
if user_name is not None:
93-
tracer.add_attribute_to_current_span('django.user.name', user_name)
103+
span.add_attribute('django.user.name', user_name)
94104

95105

96106
class OpencensusMiddleware(MiddlewareMixin):
@@ -185,13 +195,24 @@ def process_request(self, request):
185195
tracer.add_attribute_to_current_span(
186196
attribute_key=HTTP_URL,
187197
attribute_value=request.path)
198+
199+
# Add the span to thread local
200+
# in some cases (exceptions, timeouts) currentspan in
201+
# response event will be one of a child spans.
202+
# let's keep reference to 'django' span and
203+
# use it in response event
204+
execution_context.set_opencensus_attr(
205+
SPAN_THREAD_LOCAL_KEY,
206+
span)
207+
188208
except Exception: # pragma: NO COVER
189209
log.error('Failed to trace request', exc_info=True)
190210

191211
def process_view(self, request, view_func, *args, **kwargs):
192212
"""Process view is executed before the view function, here we get the
193213
function name add set it as the span name.
194214
"""
215+
195216
# Do not trace if the url is blacklisted
196217
if utils.disable_tracing_url(request.path, self._blacklist_paths):
197218
return
@@ -211,13 +232,14 @@ def process_response(self, request, response):
211232
return response
212233

213234
try:
214-
tracer = _get_current_tracer()
215-
tracer.add_attribute_to_current_span(
235+
span = _get_django_span()
236+
span.add_attribute(
216237
attribute_key=HTTP_STATUS_CODE,
217238
attribute_value=str(response.status_code))
218239

219-
_set_django_attributes(tracer, request)
240+
_set_django_attributes(span, request)
220241

242+
tracer = _get_current_tracer()
221243
tracer.end_span()
222244
tracer.finish()
223245
except Exception: # pragma: NO COVER

tests/unit/trace/ext/django/test_middleware.py

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -392,50 +392,91 @@ def test_process_response(self):
392392

393393
self.assertEqual(span.attributes, expected_attributes)
394394

395+
def test_process_response_unfinished_child_span(self):
396+
from opencensus.trace.ext.django import middleware
397+
398+
trace_id = '2dd43a1d6b2549c6bc2a1a54c2fc0b05'
399+
span_id = '6e0c63257de34c92'
400+
django_trace_id = '{}/{}'.format(trace_id, span_id)
401+
402+
django_request = RequestFactory().get('/', **{
403+
google_cloud_format._TRACE_CONTEXT_HEADER_NAME: django_trace_id})
404+
405+
middleware_obj = middleware.OpencensusMiddleware()
406+
407+
middleware_obj.process_request(django_request)
408+
tracer = middleware._get_current_tracer()
409+
span = tracer.current_span()
410+
411+
exporter_mock = mock.Mock()
412+
tracer.exporter = exporter_mock
413+
414+
django_response = mock.Mock()
415+
django_response.status_code = 500
416+
417+
expected_attributes = {
418+
'http.url': u'/',
419+
'http.method': 'GET',
420+
'http.status_code': '500',
421+
'django.user.id': '123',
422+
'django.user.name': 'test_name'
423+
}
424+
425+
mock_user = mock.Mock()
426+
mock_user.pk = 123
427+
mock_user.get_username.return_value = 'test_name'
428+
django_request.user = mock_user
429+
430+
tracer.start_span()
431+
self.assertNotEqual(span, tracer.current_span())
432+
middleware_obj.process_response(django_request, django_response)
433+
434+
self.assertEqual(span.attributes, expected_attributes)
435+
395436

396437
class Test__set_django_attributes(unittest.TestCase):
397-
class Tracer(object):
438+
class Span(object):
398439
def __init__(self):
399440
self.attributes = {}
400441

401-
def add_attribute_to_current_span(self, key, value):
442+
def add_attribute(self, key, value):
402443
self.attributes[key] = value
403444

404445
def test__set_django_attributes_no_user(self):
405446
from opencensus.trace.ext.django.middleware import \
406447
_set_django_attributes
407-
tracer = self.Tracer()
448+
span = self.Span()
408449
request = mock.Mock()
409450

410451
request.user = None
411452

412-
_set_django_attributes(tracer, request)
453+
_set_django_attributes(span, request)
413454

414455
expected_attributes = {}
415456

416-
self.assertEqual(tracer.attributes, expected_attributes)
457+
self.assertEqual(span.attributes, expected_attributes)
417458

418459
def test__set_django_attributes_no_user_info(self):
419460
from opencensus.trace.ext.django.middleware import \
420461
_set_django_attributes
421-
tracer = self.Tracer()
462+
span = self.Span()
422463
request = mock.Mock()
423464
django_user = mock.Mock()
424465

425466
request.user = django_user
426467
django_user.pk = None
427468
django_user.get_username.return_value = None
428469

429-
_set_django_attributes(tracer, request)
470+
_set_django_attributes(span, request)
430471

431472
expected_attributes = {}
432473

433-
self.assertEqual(tracer.attributes, expected_attributes)
474+
self.assertEqual(span.attributes, expected_attributes)
434475

435476
def test__set_django_attributes_with_user_info(self):
436477
from opencensus.trace.ext.django.middleware import \
437478
_set_django_attributes
438-
tracer = self.Tracer()
479+
span = self.Span()
439480
request = mock.Mock()
440481
django_user = mock.Mock()
441482

@@ -445,10 +486,10 @@ def test__set_django_attributes_with_user_info(self):
445486
django_user.pk = test_id
446487
django_user.get_username.return_value = test_name
447488

448-
_set_django_attributes(tracer, request)
489+
_set_django_attributes(span, request)
449490

450491
expected_attributes = {
451492
'django.user.id': '123',
452493
'django.user.name': test_name}
453494

454-
self.assertEqual(tracer.attributes, expected_attributes)
495+
self.assertEqual(span.attributes, expected_attributes)

0 commit comments

Comments
 (0)