@@ -32,6 +32,10 @@ def span_event_start_fmt(span_processor_name, span_name):
3232 return span_processor_name + ":" + span_name + ":start"
3333
3434
35+ def span_event_ending_fmt (span_processor_name , span_name ):
36+ return span_processor_name + ":" + span_name + ":ending"
37+
38+
3539def span_event_end_fmt (span_processor_name , span_name ):
3640 return span_processor_name + ":" + span_name + ":end"
3741
@@ -50,6 +54,11 @@ def on_end(self, span: "trace.Span") -> None:
5054 self .span_list .append (span_event_end_fmt (self .name , span .name ))
5155
5256
57+ class MyExtendedSpanProcessor (MySpanProcessor ):
58+ def _on_ending (self , span : "trace.Span" ) -> None :
59+ self .span_list .append (span_event_ending_fmt (self .name , span .name ))
60+
61+
5362class TestSpanProcessor (unittest .TestCase ):
5463 def test_span_processor (self ):
5564 tracer_provider = trace .TracerProvider ()
@@ -120,6 +129,85 @@ def test_span_processor(self):
120129 # compare if two lists are the same
121130 self .assertListEqual (spans_calls_list , expected_list )
122131
132+ # pylint: disable=too-many-statements
133+ def test_span_processor_with_on_ending (self ):
134+ tracer_provider = trace .TracerProvider ()
135+ tracer = tracer_provider .get_tracer (__name__ )
136+
137+ spans_calls_list = [] # filled by MySpanProcessor
138+ expected_list = [] # filled by hand
139+
140+ # Span processors are created but not added to the tracer yet
141+ sp1 = MyExtendedSpanProcessor ("SP1" , spans_calls_list )
142+ sp2 = MyExtendedSpanProcessor ("SP2" , spans_calls_list )
143+
144+ with tracer .start_as_current_span ("foo" ):
145+ with tracer .start_as_current_span ("bar" ):
146+ with tracer .start_as_current_span ("baz" ):
147+ pass
148+
149+ # at this point lists must be empty
150+ self .assertEqual (len (spans_calls_list ), 0 )
151+
152+ # add single span processor
153+ tracer_provider .add_span_processor (sp1 )
154+
155+ with tracer .start_as_current_span ("foo" ):
156+ expected_list .append (span_event_start_fmt ("SP1" , "foo" ))
157+
158+ with tracer .start_as_current_span ("bar" ):
159+ expected_list .append (span_event_start_fmt ("SP1" , "bar" ))
160+
161+ with tracer .start_as_current_span ("baz" ):
162+ expected_list .append (span_event_start_fmt ("SP1" , "baz" ))
163+
164+ expected_list .append (span_event_ending_fmt ("SP1" , "baz" ))
165+ expected_list .append (span_event_end_fmt ("SP1" , "baz" ))
166+
167+ expected_list .append (span_event_ending_fmt ("SP1" , "bar" ))
168+ expected_list .append (span_event_end_fmt ("SP1" , "bar" ))
169+
170+ expected_list .append (span_event_ending_fmt ("SP1" , "foo" ))
171+ expected_list .append (span_event_end_fmt ("SP1" , "foo" ))
172+
173+ self .assertListEqual (spans_calls_list , expected_list )
174+
175+ spans_calls_list .clear ()
176+ expected_list .clear ()
177+
178+ # go for multiple span processors
179+ tracer_provider .add_span_processor (sp2 )
180+
181+ with tracer .start_as_current_span ("foo" ):
182+ expected_list .append (span_event_start_fmt ("SP1" , "foo" ))
183+ expected_list .append (span_event_start_fmt ("SP2" , "foo" ))
184+
185+ with tracer .start_as_current_span ("bar" ):
186+ expected_list .append (span_event_start_fmt ("SP1" , "bar" ))
187+ expected_list .append (span_event_start_fmt ("SP2" , "bar" ))
188+
189+ with tracer .start_as_current_span ("baz" ):
190+ expected_list .append (span_event_start_fmt ("SP1" , "baz" ))
191+ expected_list .append (span_event_start_fmt ("SP2" , "baz" ))
192+
193+ expected_list .append (span_event_ending_fmt ("SP1" , "baz" ))
194+ expected_list .append (span_event_ending_fmt ("SP2" , "baz" ))
195+ expected_list .append (span_event_end_fmt ("SP1" , "baz" ))
196+ expected_list .append (span_event_end_fmt ("SP2" , "baz" ))
197+
198+ expected_list .append (span_event_ending_fmt ("SP1" , "bar" ))
199+ expected_list .append (span_event_ending_fmt ("SP2" , "bar" ))
200+ expected_list .append (span_event_end_fmt ("SP1" , "bar" ))
201+ expected_list .append (span_event_end_fmt ("SP2" , "bar" ))
202+
203+ expected_list .append (span_event_ending_fmt ("SP1" , "foo" ))
204+ expected_list .append (span_event_ending_fmt ("SP2" , "foo" ))
205+ expected_list .append (span_event_end_fmt ("SP1" , "foo" ))
206+ expected_list .append (span_event_end_fmt ("SP2" , "foo" ))
207+
208+ # compare if two lists are the same
209+ self .assertListEqual (spans_calls_list , expected_list )
210+
123211 def test_add_span_processor_after_span_creation (self ):
124212 tracer_provider = trace .TracerProvider ()
125213 tracer = tracer_provider .get_tracer (__name__ )
@@ -144,6 +232,37 @@ def test_add_span_processor_after_span_creation(self):
144232
145233 self .assertListEqual (spans_calls_list , expected_list )
146234
235+ def test_on_ending_not_implemented_does_not_raise (self ):
236+ tracer_provider = trace .TracerProvider ()
237+ tracer = tracer_provider .get_tracer (__name__ )
238+
239+ spans_calls_list = [] # filled by MySpanProcessor
240+ expected_list = [] # filled by hand
241+
242+ # Does not implement _on_ending
243+ sp = MySpanProcessor ("SP1" , spans_calls_list )
244+ tracer_provider .add_span_processor (sp )
245+
246+ try :
247+ with tracer .start_as_current_span ("foo" ):
248+ expected_list .append (span_event_start_fmt ("SP1" , "foo" ))
249+
250+ with tracer .start_as_current_span ("bar" ):
251+ expected_list .append (span_event_start_fmt ("SP1" , "bar" ))
252+
253+ with tracer .start_as_current_span ("baz" ):
254+ expected_list .append (
255+ span_event_start_fmt ("SP1" , "baz" )
256+ )
257+
258+ expected_list .append (span_event_end_fmt ("SP1" , "baz" ))
259+ expected_list .append (span_event_end_fmt ("SP1" , "bar" ))
260+ expected_list .append (span_event_end_fmt ("SP1" , "foo" ))
261+ except NotImplementedError :
262+ self .fail ("_on_ending() should not raise an exception" )
263+
264+ self .assertListEqual (spans_calls_list , expected_list )
265+
147266
148267class MultiSpanProcessorTestBase (abc .ABC ):
149268 @abc .abstractmethod
@@ -176,6 +295,22 @@ def test_on_start(self):
176295 )
177296 multi_processor .shutdown ()
178297
298+ def test_on_ending (self ):
299+ multi_processor = self .create_multi_span_processor ()
300+
301+ mocks = [mock .Mock (spec = trace .SpanProcessor ) for _ in range (0 , 5 )]
302+ for mock_processor in mocks :
303+ multi_processor .add_span_processor (mock_processor )
304+
305+ span = self .create_default_span ()
306+ # pylint: disable=protected-access
307+ multi_processor ._on_ending (span )
308+
309+ for mock_processor in mocks :
310+ # pylint: disable=protected-access
311+ mock_processor ._on_ending .assert_called_once_with (span )
312+ multi_processor .shutdown ()
313+
179314 def test_on_end (self ):
180315 multi_processor = self .create_multi_span_processor ()
181316
@@ -219,6 +354,43 @@ def test_force_flush(self):
219354 self .assertEqual (1 , mock_processor .force_flush .call_count )
220355 multi_processor .shutdown ()
221356
357+ def test_on_ending_not_implemented_does_not_raise (self ):
358+ tracer_provider = trace .TracerProvider ()
359+ tracer = tracer_provider .get_tracer (__name__ )
360+
361+ spans_calls_list = [] # filled by MySpanProcessor
362+ expected_list = [] # filled by hand
363+
364+ multi_processor = self .create_multi_span_processor ()
365+ # Does not implement _on_ending
366+ multi_processor .add_span_processor (
367+ MySpanProcessor ("SP1" , spans_calls_list )
368+ )
369+
370+ tracer_provider .add_span_processor (multi_processor )
371+
372+ try :
373+ with tracer .start_as_current_span ("foo" ):
374+ expected_list .append (span_event_start_fmt ("SP1" , "foo" ))
375+
376+ with tracer .start_as_current_span ("bar" ):
377+ expected_list .append (span_event_start_fmt ("SP1" , "bar" ))
378+
379+ with tracer .start_as_current_span ("baz" ):
380+ expected_list .append (
381+ span_event_start_fmt ("SP1" , "baz" )
382+ )
383+
384+ expected_list .append (span_event_end_fmt ("SP1" , "baz" ))
385+ expected_list .append (span_event_end_fmt ("SP1" , "bar" ))
386+ expected_list .append (span_event_end_fmt ("SP1" , "foo" ))
387+ except NotImplementedError :
388+ # pylint: disable=no-member
389+ self .fail ("_on_ending() should not raise an exception" )
390+
391+ # pylint: disable=no-member
392+ self .assertListEqual (spans_calls_list , expected_list )
393+
222394
223395class TestSynchronousMultiSpanProcessor (
224396 MultiSpanProcessorTestBase , unittest .TestCase
0 commit comments