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

Commit f157a9d

Browse files
authored
Export span once it's finished (#68)
1 parent 403ee06 commit f157a9d

11 files changed

Lines changed: 146 additions & 185 deletions

File tree

trace/opencensus/trace/ext/django/middleware.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ def _set_django_labels(tracer, request):
6565

6666
# User id is the django autofield for User model as the primary key
6767
if user_id is not None:
68-
tracer.add_label_to_spans('/django/user/id', str(user_id))
68+
tracer.add_label_to_current_span('/django/user/id', str(user_id))
6969

7070
if user_name is not None:
71-
tracer.add_label_to_spans('/django/user/name', user_name)
71+
tracer.add_label_to_current_span('/django/user/name', user_name)
7272

7373

7474
def get_django_header():
@@ -148,10 +148,10 @@ def process_request(self, request):
148148

149149
# Span name is being set at process_view
150150
tracer.start_span()
151-
tracer.add_label_to_spans(
151+
tracer.add_label_to_current_span(
152152
label_key=HTTP_METHOD,
153153
label_value=request.method)
154-
tracer.add_label_to_spans(
154+
tracer.add_label_to_current_span(
155155
label_key=HTTP_URL,
156156
label_value=request.path)
157157
except Exception: # pragma: NO COVER
@@ -173,7 +173,7 @@ def process_view(self, request, view_func, *args, **kwargs):
173173
def process_response(self, request, response):
174174
try:
175175
tracer = _get_current_request_tracer()
176-
tracer.add_label_to_spans(
176+
tracer.add_label_to_current_span(
177177
label_key=HTTP_STATUS_CODE,
178178
label_value=str(response.status_code))
179179

trace/opencensus/trace/ext/flask/flask_middleware.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ def _before_request(self):
9090
span.name = '[{}]{}'.format(
9191
flask.request.method,
9292
flask.request.url)
93-
tracer.add_label_to_spans(HTTP_METHOD, flask.request.method)
94-
tracer.add_label_to_spans(HTTP_URL, flask.request.url)
93+
tracer.add_label_to_current_span(HTTP_METHOD, flask.request.method)
94+
tracer.add_label_to_current_span(HTTP_URL, flask.request.url)
9595
except Exception: # pragma: NO COVER
9696
log.error('Failed to trace request', exc_info=True)
9797

@@ -102,7 +102,7 @@ def _after_request(self, response):
102102
"""
103103
try:
104104
tracer = execution_context.get_opencensus_tracer()
105-
tracer.add_label_to_spans(
105+
tracer.add_label_to_current_span(
106106
HTTP_STATUS_CODE,
107107
str(response.status_code))
108108

trace/opencensus/trace/request_tracer.py

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ def get_tracer(self):
8181
sampled = self.should_sample()
8282

8383
if sampled:
84-
return context_tracer.ContextTracer(self.span_context)
84+
return context_tracer.ContextTracer(
85+
exporter=self.exporter,
86+
span_context=self.span_context)
8587
else:
8688
return noop_tracer.NoopTracer()
8789

@@ -90,11 +92,8 @@ def store_tracer(self):
9092
execution_context.set_opencensus_tracer(self)
9193

9294
def finish(self):
93-
"""End all spans and send spans using reporter."""
94-
trace = self.tracer.finish()
95-
96-
if trace is not None:
97-
self.exporter.export(trace)
95+
"""End all spans."""
96+
self.tracer.finish()
9897

9998
def span(self, name=None):
10099
"""Create a new span with the trace using the context information.
@@ -111,9 +110,8 @@ def start_span(self, name=None):
111110
return self.tracer.start_span(name)
112111

113112
def end_span(self):
114-
"""End a span. Remove the span from the span stack, and update the
115-
span_id in TraceContext as the current span_id which is the peek
116-
element in the span stack.
113+
"""End a span. Update the span_id in SpanContext to the current span's
114+
parent span id; Update the current span; Send the span to exporter.
117115
"""
118116
self.tracer.end_span()
119117

@@ -132,9 +130,6 @@ def add_label_to_current_span(self, label_key, label_value):
132130
"""
133131
self.tracer.add_label_to_current_span(label_key, label_value)
134132

135-
def add_label_to_spans(self, label_key, label_value):
136-
self.tracer.add_label_to_spans(label_key, label_value)
137-
138133
def trace_decorator(self):
139134
"""Decorator to trace a function."""
140135

trace/opencensus/trace/tracer/base.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,6 @@ def current_span(self):
5858
def add_label_to_current_span(self, label_key, label_value):
5959
raise NotImplementedError
6060

61-
def add_label_to_spans(self, label_key, label_value):
62-
"""Add label to the spans in current trace.
63-
64-
:type label_key: str
65-
:param label_key: Label key.
66-
67-
:type label_value:str
68-
:param label_value: Label value.
69-
"""
70-
raise NotImplementedError
71-
7261
def list_collected_spans(self):
7362
"""List collected spans."""
7463
raise NotImplementedError

trace/opencensus/trace/tracer/context_tracer.py

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from opencensus.trace import execution_context
1818
from opencensus.trace.span_context import SpanContext
1919
from opencensus.trace import span as trace_span
20+
from opencensus.trace.exporters import print_exporter
2021
from opencensus.trace.tracer import base
2122

2223

@@ -27,10 +28,14 @@ class ContextTracer(base.Tracer):
2728
:param span_context: SpanContext encapsulates the current context within
2829
the request's trace.
2930
"""
30-
def __init__(self, span_context=None):
31+
def __init__(self, exporter=None, span_context=None):
32+
if exporter is None:
33+
exporter = print_exporter.PrintExporter()
34+
3135
if span_context is None:
3236
span_context = SpanContext()
3337

38+
self.exporter = exporter
3439
self.span_context = span_context
3540
self.trace_id = span_context.trace_id
3641
self.root_span_id = span_context.span_id
@@ -44,11 +49,8 @@ def finish(self):
4449
:rtype: dict
4550
:returns: JSON format trace.
4651
"""
47-
trace = self._get_trace_json()
4852
self._spans_list = []
4953

50-
return trace
51-
5254
def span(self, name='span'):
5355
"""Create a new span with the trace using the context information.
5456
@@ -88,10 +90,9 @@ def start_span(self, name='span'):
8890
span.start()
8991
return span
9092

91-
def end_span(self):
92-
"""End a span. Remove the span from the span stack, and update the
93-
span_id in TraceContext as the current span_id which is the peek
94-
element in the span stack.
93+
def end_span(self, *args, **kwargs):
94+
"""End a span. Update the span_id in SpanContext to the current span's
95+
parent span id; Update the current span.
9596
"""
9697
cur_span = self.current_span()
9798

@@ -107,6 +108,11 @@ def end_span(self):
107108
else:
108109
execution_context.set_current_span(None)
109110

111+
span_json = self.get_trace_json(cur_span)
112+
self.exporter.export(span_json)
113+
114+
return cur_span
115+
110116
def current_span(self):
111117
"""Return the current span."""
112118
current_span = execution_context.get_current_span()
@@ -128,33 +134,13 @@ def add_label_to_current_span(self, label_key, label_value):
128134
current_span = self.current_span()
129135
current_span.add_label(label_key, label_value)
130136

131-
def add_label_to_spans(self, label_key, label_value):
132-
"""Add label to the spans in current trace.
133-
134-
:type label_key: str
135-
:param label_key: Label key.
136-
137-
:type label_value:str
138-
:param label_value: Label value.
139-
"""
140-
for span in self._spans_list:
141-
span.add_label(label_key, label_value)
142-
143-
def _get_trace_json(self):
137+
def get_trace_json(self, span):
144138
"""Get the JSON format trace."""
145-
spans_list = []
146-
for root_span in self._spans_list:
147-
span_tree = list(iter(root_span))
148-
span_tree_json = [trace_span.format_span_json(span)
149-
for span in span_tree]
150-
spans_list.extend(span_tree_json)
151-
152-
if len(spans_list) == 0:
153-
return
139+
span_json = trace_span.format_span_json(span)
154140

155141
trace = {
156142
'traceId': self.trace_id,
157-
'spans': spans_list,
143+
'spans': [span_json],
158144
}
159145

160146
return trace

trace/opencensus/trace/tracer/noop_tracer.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def end_span(self):
5050
span_id in TraceContext as the current span_id which is the peek
5151
element in the span stack.
5252
"""
53-
return
53+
return base.NullContextManager()
5454

5555
def current_span(self):
5656
"""Return the current span."""
@@ -67,17 +67,6 @@ def add_label_to_current_span(self, label_key, label_value):
6767
"""
6868
return
6969

70-
def add_label_to_spans(self, label_key, label_value):
71-
"""Add label to the spans in current trace.
72-
73-
:type label_key: str
74-
:param label_key: Label key.
75-
76-
:type label_value:str
77-
:param label_value: Label value.
78-
"""
79-
return
80-
8170
def list_collected_spans(self):
8271
"""List collected spans."""
8372
return None

trace/tests/system/basic_trace/basic_trace_system_test.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
def func_to_trace():
1919
import time
20-
2120
print('Test simple tracing...')
2221
time.sleep(2)
2322

@@ -64,7 +63,7 @@ def test_request_tracer(self):
6463
spans = trace_json.get('spans')
6564

6665
self.assertEqual(trace_json.get('traceId'), trace_id)
67-
self.assertEqual(len(spans), 2)
66+
self.assertEqual(len(spans), 1)
6867

6968
for span in spans:
7069
if span.get('name') == 'root_span':

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,6 @@ def test_process_response(self):
238238
middleware_obj.process_response(django_request, django_response)
239239

240240
self.assertEqual(span.labels, expected_labels)
241-
self.assertTrue(exporter_mock.export.called)
242241

243242

244243
class Test__set_django_labels(unittest.TestCase):
@@ -247,7 +246,7 @@ class Tracer(object):
247246
def __init__(self):
248247
self.labels = {}
249248

250-
def add_label_to_spans(self, key, value):
249+
def add_label_to_current_span(self, key, value):
251250
self.labels[key] = value
252251

253252
def test__set_django_labels_no_user(self):

trace/tests/unit/test_span.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,14 @@ def test_finish_with_context_tracer(self):
156156

157157
self.assertTrue(context_tracer.end_span.called)
158158

159+
def test_finish(self):
160+
span_name = 'root_span'
161+
span = self._make_one(span_name)
162+
self.assertIsNone(span.end_time)
163+
164+
span.finish()
165+
self.assertIsNotNone(span.end_time)
166+
159167
def test___iter__(self):
160168
root_span_name = 'root_span_name'
161169
child1_span_name = 'child1_span_name'
@@ -175,3 +183,75 @@ def test___iter__(self):
175183
self.assertEqual(
176184
span_iter_list,
177185
[child1_child1_span, child1_span, child2_span, root_span])
186+
187+
188+
class Test_format_span_json(unittest.TestCase):
189+
190+
def test_format_span_json_no_parent_span(self):
191+
from opencensus.trace.span import format_span_json
192+
from opencensus.trace.enums import Enum
193+
194+
name = 'test span'
195+
kind = Enum.SpanKind.SPAN_KIND_UNSPECIFIED
196+
span_id = 1234
197+
start_time = '2017-06-25'
198+
end_time = '2017-06-26'
199+
200+
span = mock.Mock()
201+
span.name = name
202+
span.kind = kind
203+
span.span_id = span_id
204+
span.start_time = start_time
205+
span.end_time = end_time
206+
span.parent_span = None
207+
span.labels = None
208+
209+
expected_span_json = {
210+
'name': name,
211+
'kind': kind,
212+
'spanId': span_id,
213+
'startTime': start_time,
214+
'endTime': end_time,
215+
}
216+
217+
span_json = format_span_json(span)
218+
self.assertEqual(span_json, expected_span_json)
219+
220+
def test_format_span_json_with_parent_span(self):
221+
from opencensus.trace.span import format_span_json
222+
from opencensus.trace.enums import Enum
223+
224+
name = 'test span'
225+
kind = Enum.SpanKind.SPAN_KIND_UNSPECIFIED
226+
span_id = 1234
227+
labels = {
228+
'/http/status_code': '200',
229+
'/component': 'HTTP load balancer',
230+
}
231+
start_time = '2017-06-25'
232+
end_time = '2017-06-26'
233+
parent_span = mock.Mock()
234+
parent_span_id = 5678
235+
parent_span.span_id = parent_span_id
236+
237+
span = mock.Mock()
238+
span.parent_span = parent_span
239+
span.name = name
240+
span.kind = kind
241+
span.labels = labels
242+
span.span_id = span_id
243+
span.start_time = start_time
244+
span.end_time = end_time
245+
246+
expected_span_json = {
247+
'name': name,
248+
'kind': kind,
249+
'spanId': span_id,
250+
'parentSpanId': parent_span_id,
251+
'startTime': start_time,
252+
'endTime': end_time,
253+
'labels': labels,
254+
}
255+
256+
span_json = format_span_json(span)
257+
self.assertEqual(span_json, expected_span_json)

trace/tests/unit/tracer/test_base_tracer.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,6 @@ def test_add_label_to_current_span(self):
5757
with self.assertRaises(NotImplementedError):
5858
tracer.add_label_to_current_span(label_key, label_value)
5959

60-
def test_add_label_to_spans_abstract(self):
61-
tracer = base.Tracer()
62-
label_key = 'key'
63-
label_value = 'value'
64-
65-
with self.assertRaises(NotImplementedError):
66-
tracer.add_label_to_spans(label_key, label_value)
67-
6860
def test_list_collected_spans_abstract(self):
6961
tracer = base.Tracer()
7062

0 commit comments

Comments
 (0)