3939# pylint: disable=too-many-public-methods
4040class TestLoggingHandler (unittest .TestCase ):
4141 def test_handler_default_log_level (self ):
42- processor , logger = set_up_test_logging (logging .NOTSET )
42+ processor , logger , handler = set_up_test_logging (logging .NOTSET )
4343
4444 # Make sure debug messages are ignored by default
4545 logger .debug ("Debug message" )
@@ -50,8 +50,10 @@ def test_handler_default_log_level(self):
5050 logger .warning ("Warning message" )
5151 self .assertEqual (processor .emit_count (), 1 )
5252
53+ logger .removeHandler (handler )
54+
5355 def test_handler_custom_log_level (self ):
54- processor , logger = set_up_test_logging (logging .ERROR )
56+ processor , logger , handler = set_up_test_logging (logging .ERROR )
5557
5658 with self .assertLogs (level = logging .WARNING ):
5759 logger .warning ("Warning message test custom log level" )
@@ -64,6 +66,8 @@ def test_handler_custom_log_level(self):
6466 logger .critical ("No Time For Caution" )
6567 self .assertEqual (processor .emit_count (), 2 )
6668
69+ logger .removeHandler (handler )
70+
6771 # pylint: disable=protected-access
6872 def test_log_record_emit_noop (self ):
6973 noop_logger_provder = NoOpLoggerProvider ()
@@ -78,9 +82,10 @@ def test_log_record_emit_noop(self):
7882 with self .assertLogs (level = logging .WARNING ):
7983 logger .warning ("Warning message" )
8084
85+ logger .removeHandler (handler_mock )
86+
8187 def test_log_flush_noop (self ):
8288 no_op_logger_provider = NoOpLoggerProvider ()
83- no_op_logger_provider .force_flush = Mock ()
8489
8590 logger = logging .getLogger ("foo" )
8691 handler = LoggingHandler (
@@ -91,11 +96,19 @@ def test_log_flush_noop(self):
9196 with self .assertLogs (level = logging .WARNING ):
9297 logger .warning ("Warning message" )
9398
94- logger .handlers [0 ].flush ()
95- no_op_logger_provider .force_flush .assert_not_called ()
99+ # the LoggingHandler flush method will call the force_flush method of LoggerProvider in
100+ # a separate thread if present. NoOpLoggerProvider is not supposed to have that
101+ with patch (
102+ "opentelemetry.sdk._logs._internal.threading"
103+ ) as threading_mock :
104+ logger .handlers [0 ].flush ()
105+
106+ threading_mock .Thread .assert_not_called ()
107+
108+ logger .removeHandler (handler )
96109
97110 def test_log_record_no_span_context (self ):
98- processor , logger = set_up_test_logging (logging .WARNING )
111+ processor , logger , handler = set_up_test_logging (logging .WARNING )
99112
100113 # Assert emit gets called for warning message
101114 with self .assertLogs (level = logging .WARNING ):
@@ -115,18 +128,22 @@ def test_log_record_no_span_context(self):
115128 INVALID_SPAN_CONTEXT .trace_flags ,
116129 )
117130
131+ logger .removeHandler (handler )
132+
118133 def test_log_record_observed_timestamp (self ):
119- processor , logger = set_up_test_logging (logging .WARNING )
134+ processor , logger , handler = set_up_test_logging (logging .WARNING )
120135
121136 with self .assertLogs (level = logging .WARNING ):
122137 logger .warning ("Warning message" )
123138
124139 record = processor .get_log_record (0 )
125140 self .assertIsNotNone (record .log_record .observed_timestamp )
126141
142+ logger .removeHandler (handler )
143+
127144 def test_log_record_user_attributes (self ):
128145 """Attributes can be injected into logs by adding them to the ReadWriteLogRecord"""
129- processor , logger = set_up_test_logging (logging .WARNING )
146+ processor , logger , handler = set_up_test_logging (logging .WARNING )
130147
131148 # Assert emit gets called for warning message
132149 with self .assertLogs (level = logging .WARNING ):
@@ -155,9 +172,11 @@ def test_log_record_user_attributes(self):
155172 isinstance (record .log_record .attributes , BoundedAttributes )
156173 )
157174
175+ logger .removeHandler (handler )
176+
158177 def test_log_record_exception (self ):
159178 """Exception information will be included in attributes"""
160- processor , logger = set_up_test_logging (logging .ERROR )
179+ processor , logger , handler = set_up_test_logging (logging .ERROR )
161180
162181 try :
163182 raise ZeroDivisionError ("division by zero" )
@@ -189,9 +208,11 @@ def test_log_record_exception(self):
189208 self .assertTrue ("division by zero" in stack_trace )
190209 self .assertTrue (__file__ in stack_trace )
191210
211+ logger .removeHandler (handler )
212+
192213 def test_log_record_recursive_exception (self ):
193214 """Exception information will be included in attributes even though it is recursive"""
194- processor , logger = set_up_test_logging (logging .ERROR )
215+ processor , logger , handler = set_up_test_logging (logging .ERROR )
195216
196217 try :
197218 raise ZeroDivisionError (
@@ -224,9 +245,11 @@ def test_log_record_recursive_exception(self):
224245 self .assertTrue ("division by zero" in stack_trace )
225246 self .assertTrue (__file__ in stack_trace )
226247
248+ logger .removeHandler (handler )
249+
227250 def test_log_exc_info_false (self ):
228251 """Exception information will not be included in attributes"""
229- processor , logger = set_up_test_logging (logging .NOTSET )
252+ processor , logger , handler = set_up_test_logging (logging .NOTSET )
230253
231254 try :
232255 raise ZeroDivisionError ("division by zero" )
@@ -251,8 +274,10 @@ def test_log_exc_info_false(self):
251274 record .log_record .attributes ,
252275 )
253276
277+ logger .removeHandler (handler )
278+
254279 def test_log_record_exception_with_object_payload (self ):
255- processor , logger = set_up_test_logging (logging .ERROR )
280+ processor , logger , handler = set_up_test_logging (logging .ERROR )
256281
257282 class CustomException (Exception ):
258283 def __str__ (self ):
@@ -287,8 +312,10 @@ def __str__(self):
287312 self .assertTrue ("CustomException" in stack_trace )
288313 self .assertTrue (__file__ in stack_trace )
289314
315+ logger .removeHandler (handler )
316+
290317 def test_log_record_trace_correlation (self ):
291- processor , logger = set_up_test_logging (logging .WARNING )
318+ processor , logger , handler = set_up_test_logging (logging .WARNING )
292319
293320 tracer = trace .TracerProvider ().get_tracer (__name__ )
294321 with tracer .start_as_current_span ("test" ) as span :
@@ -325,8 +352,10 @@ def test_log_record_trace_correlation(self):
325352 span_context .trace_flags ,
326353 )
327354
355+ logger .removeHandler (handler )
356+
328357 def test_log_record_trace_correlation_deprecated (self ):
329- processor , logger = set_up_test_logging (logging .WARNING )
358+ processor , logger , handler = set_up_test_logging (logging .WARNING )
330359
331360 tracer = trace .TracerProvider ().get_tracer (__name__ )
332361 with tracer .start_as_current_span ("test" ) as span :
@@ -349,22 +378,28 @@ def test_log_record_trace_correlation_deprecated(self):
349378 record .log_record .trace_flags , span_context .trace_flags
350379 )
351380
381+ logger .removeHandler (handler )
382+
352383 def test_warning_without_formatter (self ):
353- processor , logger = set_up_test_logging (logging .WARNING )
384+ processor , logger , handler = set_up_test_logging (logging .WARNING )
354385 logger .warning ("Test message" )
355386
356387 record = processor .get_log_record (0 )
357388 self .assertEqual (record .log_record .body , "Test message" )
358389
390+ logger .removeHandler (handler )
391+
359392 def test_exception_without_formatter (self ):
360- processor , logger = set_up_test_logging (logging .WARNING )
393+ processor , logger , handler = set_up_test_logging (logging .WARNING )
361394 logger .exception ("Test exception" )
362395
363396 record = processor .get_log_record (0 )
364397 self .assertEqual (record .log_record .body , "Test exception" )
365398
399+ logger .removeHandler (handler )
400+
366401 def test_warning_with_formatter (self ):
367- processor , logger = set_up_test_logging (
402+ processor , logger , handler = set_up_test_logging (
368403 logging .WARNING ,
369404 formatter = logging .Formatter (
370405 "%(name)s - %(levelname)s - %(message)s"
@@ -377,8 +412,10 @@ def test_warning_with_formatter(self):
377412 record .log_record .body , "foo - WARNING - Test message"
378413 )
379414
415+ logger .removeHandler (handler )
416+
380417 def test_log_body_is_always_string_with_formatter (self ):
381- processor , logger = set_up_test_logging (
418+ processor , logger , handler = set_up_test_logging (
382419 logging .WARNING ,
383420 formatter = logging .Formatter (
384421 "%(name)s - %(levelname)s - %(message)s"
@@ -389,17 +426,21 @@ def test_log_body_is_always_string_with_formatter(self):
389426 record = processor .get_log_record (0 )
390427 self .assertIsInstance (record .log_record .body , str )
391428
429+ logger .removeHandler (handler )
430+
392431 @patch .dict (os .environ , {"OTEL_SDK_DISABLED" : "true" })
393432 def test_handler_root_logger_with_disabled_sdk_does_not_go_into_recursion_error (
394433 self ,
395434 ):
396- processor , logger = set_up_test_logging (
435+ processor , logger , handler = set_up_test_logging (
397436 logging .NOTSET , root_logger = True
398437 )
399438 logger .warning ("hello" )
400439
401440 self .assertEqual (processor .emit_count (), 0 )
402441
442+ logger .removeHandler (handler )
443+
403444 @patch .dict (os .environ , {OTEL_ATTRIBUTE_COUNT_LIMIT : "3" })
404445 def test_otel_attribute_count_limit_respected_in_logging_handler (self ):
405446 """Test that OTEL_ATTRIBUTE_COUNT_LIMIT is properly respected by LoggingHandler."""
@@ -439,6 +480,8 @@ def test_otel_attribute_count_limit_respected_in_logging_handler(self):
439480 f"Should have 10 dropped attributes, got { record .dropped_attributes } " ,
440481 )
441482
483+ logger .removeHandler (handler )
484+
442485 @patch .dict (os .environ , {OTEL_ATTRIBUTE_COUNT_LIMIT : "5" })
443486 def test_otel_attribute_count_limit_includes_code_attributes (self ):
444487 """Test that OTEL_ATTRIBUTE_COUNT_LIMIT applies to all attributes including code attributes."""
@@ -476,9 +519,11 @@ def test_otel_attribute_count_limit_includes_code_attributes(self):
476519 f"Should have 6 dropped attributes, got { record .dropped_attributes } " ,
477520 )
478521
522+ logger .removeHandler (handler )
523+
479524 def test_logging_handler_without_env_var_uses_default_limit (self ):
480525 """Test that without OTEL_ATTRIBUTE_COUNT_LIMIT, default limit (128) should apply."""
481- processor , logger = set_up_test_logging (logging .WARNING )
526+ processor , logger , handler = set_up_test_logging (logging .WARNING )
482527
483528 # Create a log record with many attributes (more than default limit of 128)
484529 extra_attrs = {f"attr_{ i } " : f"value_{ i } " for i in range (150 )}
@@ -505,6 +550,8 @@ def test_logging_handler_without_env_var_uses_default_limit(self):
505550 f"Should have 25 dropped attributes, got { record .dropped_attributes } " ,
506551 )
507552
553+ logger .removeHandler (handler )
554+
508555
509556def set_up_test_logging (level , formatter = None , root_logger = False ):
510557 logger_provider = LoggerProvider ()
@@ -515,7 +562,7 @@ def set_up_test_logging(level, formatter=None, root_logger=False):
515562 if formatter :
516563 handler .setFormatter (formatter )
517564 logger .addHandler (handler )
518- return processor , logger
565+ return processor , logger , handler
519566
520567
521568class FakeProcessor (LogRecordProcessor ):
0 commit comments