1919from opentelemetry ._logs import get_logger as APIGetLogger
2020from opentelemetry .attributes import BoundedAttributes
2121from opentelemetry .sdk import trace
22- from opentelemetry .sdk ._logs import LoggerProvider , LoggingHandler
22+ from opentelemetry .sdk ._logs import (
23+ LogData ,
24+ LoggerProvider ,
25+ LoggingHandler ,
26+ LogRecordProcessor ,
27+ )
2328from opentelemetry .semconv .trace import SpanAttributes
2429from opentelemetry .trace import INVALID_SPAN_CONTEXT
2530
2631
27- def get_logger (level = logging .NOTSET , logger_provider = None ):
28- logger = logging .getLogger (__name__ )
29- handler = LoggingHandler (level = level , logger_provider = logger_provider )
30- logger .addHandler (handler )
31- return logger
32-
33-
3432class TestLoggingHandler (unittest .TestCase ):
3533 def test_handler_default_log_level (self ):
36- emitter_provider_mock = Mock (spec = LoggerProvider )
37- emitter_mock = APIGetLogger (
38- __name__ , logger_provider = emitter_provider_mock
39- )
40- logger = get_logger (logger_provider = emitter_provider_mock )
34+ processor , logger = set_up_test_logging (logging .NOTSET )
35+
4136 # Make sure debug messages are ignored by default
4237 logger .debug ("Debug message" )
43- self .assertEqual (emitter_mock .emit .call_count , 0 )
38+ assert processor .emit_count () == 0
39+
4440 # Assert emit gets called for warning message
4541 with self .assertLogs (level = logging .WARNING ):
4642 logger .warning ("Warning message" )
47- self .assertEqual (emitter_mock . emit . call_count , 1 )
43+ self .assertEqual (processor . emit_count () , 1 )
4844
4945 def test_handler_custom_log_level (self ):
50- emitter_provider_mock = Mock (spec = LoggerProvider )
51- emitter_mock = APIGetLogger (
52- __name__ , logger_provider = emitter_provider_mock
53- )
54- logger = get_logger (
55- level = logging .ERROR , logger_provider = emitter_provider_mock
56- )
46+ processor , logger = set_up_test_logging (logging .ERROR )
47+
5748 with self .assertLogs (level = logging .WARNING ):
5849 logger .warning ("Warning message test custom log level" )
5950 # Make sure any log with level < ERROR is ignored
60- self .assertEqual (emitter_mock .emit .call_count , 0 )
51+ assert processor .emit_count () == 0
52+
6153 with self .assertLogs (level = logging .ERROR ):
6254 logger .error ("Mumbai, we have a major problem" )
6355 with self .assertLogs (level = logging .CRITICAL ):
6456 logger .critical ("No Time For Caution" )
65- self .assertEqual (emitter_mock . emit . call_count , 2 )
57+ self .assertEqual (processor . emit_count () , 2 )
6658
6759 # pylint: disable=protected-access
6860 def test_log_record_emit_noop (self ):
@@ -77,14 +69,16 @@ def test_log_record_emit_noop(self):
7769 logger .addHandler (handler_mock )
7870 with self .assertLogs (level = logging .WARNING ):
7971 logger .warning ("Warning message" )
80- handler_mock ._translate .assert_not_called ()
8172
8273 def test_log_flush_noop (self ):
83-
8474 no_op_logger_provider = NoOpLoggerProvider ()
8575 no_op_logger_provider .force_flush = Mock ()
8676
87- logger = get_logger (logger_provider = no_op_logger_provider )
77+ logger = logging .getLogger ("foo" )
78+ handler = LoggingHandler (
79+ level = logging .NOTSET , logger_provider = no_op_logger_provider
80+ )
81+ logger .addHandler (handler )
8882
8983 with self .assertLogs (level = logging .WARNING ):
9084 logger .warning ("Warning message" )
@@ -93,16 +87,13 @@ def test_log_flush_noop(self):
9387 no_op_logger_provider .force_flush .assert_not_called ()
9488
9589 def test_log_record_no_span_context (self ):
96- emitter_provider_mock = Mock (spec = LoggerProvider )
97- emitter_mock = APIGetLogger (
98- __name__ , logger_provider = emitter_provider_mock
99- )
100- logger = get_logger (logger_provider = emitter_provider_mock )
90+ processor , logger = set_up_test_logging (logging .WARNING )
91+
10192 # Assert emit gets called for warning message
10293 with self .assertLogs (level = logging .WARNING ):
10394 logger .warning ("Warning message" )
104- args , _ = emitter_mock . emit . call_args_list [ 0 ]
105- log_record = args [ 0 ]
95+
96+ log_record = processor . get_log_record ( 0 )
10697
10798 self .assertIsNotNone (log_record )
10899 self .assertEqual (log_record .trace_id , INVALID_SPAN_CONTEXT .trace_id )
@@ -112,31 +103,23 @@ def test_log_record_no_span_context(self):
112103 )
113104
114105 def test_log_record_observed_timestamp (self ):
115- emitter_provider_mock = Mock (spec = LoggerProvider )
116- emitter_mock = APIGetLogger (
117- __name__ , logger_provider = emitter_provider_mock
118- )
119- logger = get_logger (logger_provider = emitter_provider_mock )
120- # Assert emit gets called for warning message
106+ processor , logger = set_up_test_logging (logging .WARNING )
107+
121108 with self .assertLogs (level = logging .WARNING ):
122109 logger .warning ("Warning message" )
123- args , _ = emitter_mock .emit .call_args_list [0 ]
124- log_record = args [0 ]
125110
111+ log_record = processor .get_log_record (0 )
126112 self .assertIsNotNone (log_record .observed_timestamp )
127113
128114 def test_log_record_user_attributes (self ):
129115 """Attributes can be injected into logs by adding them to the LogRecord"""
130- emitter_provider_mock = Mock (spec = LoggerProvider )
131- emitter_mock = APIGetLogger (
132- __name__ , logger_provider = emitter_provider_mock
133- )
134- logger = get_logger (logger_provider = emitter_provider_mock )
116+ processor , logger = set_up_test_logging (logging .WARNING )
117+
135118 # Assert emit gets called for warning message
136119 with self .assertLogs (level = logging .WARNING ):
137120 logger .warning ("Warning message" , extra = {"http.status_code" : 200 })
138- args , _ = emitter_mock . emit . call_args_list [ 0 ]
139- log_record = args [ 0 ]
121+
122+ log_record = processor . get_log_record ( 0 )
140123
141124 self .assertIsNotNone (log_record )
142125 self .assertEqual (len (log_record .attributes ), 4 )
@@ -157,18 +140,15 @@ def test_log_record_user_attributes(self):
157140
158141 def test_log_record_exception (self ):
159142 """Exception information will be included in attributes"""
160- emitter_provider_mock = Mock (spec = LoggerProvider )
161- emitter_mock = APIGetLogger (
162- __name__ , logger_provider = emitter_provider_mock
163- )
164- logger = get_logger (logger_provider = emitter_provider_mock )
143+ processor , logger = set_up_test_logging (logging .ERROR )
144+
165145 try :
166146 raise ZeroDivisionError ("division by zero" )
167147 except ZeroDivisionError :
168148 with self .assertLogs (level = logging .ERROR ):
169149 logger .exception ("Zero Division Error" )
170- args , _ = emitter_mock . emit . call_args_list [ 0 ]
171- log_record = args [ 0 ]
150+
151+ log_record = processor . get_log_record ( 0 )
172152
173153 self .assertIsNotNone (log_record )
174154 self .assertEqual (log_record .body , "Zero Division Error" )
@@ -191,18 +171,15 @@ def test_log_record_exception(self):
191171
192172 def test_log_exc_info_false (self ):
193173 """Exception information will be included in attributes"""
194- emitter_provider_mock = Mock (spec = LoggerProvider )
195- emitter_mock = APIGetLogger (
196- __name__ , logger_provider = emitter_provider_mock
197- )
198- logger = get_logger (logger_provider = emitter_provider_mock )
174+ processor , logger = set_up_test_logging (logging .NOTSET )
175+
199176 try :
200177 raise ZeroDivisionError ("division by zero" )
201178 except ZeroDivisionError :
202179 with self .assertLogs (level = logging .ERROR ):
203180 logger .error ("Zero Division Error" , exc_info = False )
204- args , _ = emitter_mock . emit . call_args_list [ 0 ]
205- log_record = args [ 0 ]
181+
182+ log_record = processor . get_log_record ( 0 )
206183
207184 self .assertIsNotNone (log_record )
208185 self .assertEqual (log_record .body , "Zero Division Error" )
@@ -215,23 +192,49 @@ def test_log_exc_info_false(self):
215192 )
216193
217194 def test_log_record_trace_correlation (self ):
218- emitter_provider_mock = Mock (spec = LoggerProvider )
219- emitter_mock = APIGetLogger (
220- __name__ , logger_provider = emitter_provider_mock
221- )
222- logger = get_logger (logger_provider = emitter_provider_mock )
195+ processor , logger = set_up_test_logging (logging .WARNING )
223196
224197 tracer = trace .TracerProvider ().get_tracer (__name__ )
225198 with tracer .start_as_current_span ("test" ) as span :
226199 with self .assertLogs (level = logging .CRITICAL ):
227200 logger .critical ("Critical message within span" )
228201
229- args , _ = emitter_mock . emit . call_args_list [ 0 ]
230- log_record = args [ 0 ]
202+ log_record = processor . get_log_record ( 0 )
203+
231204 self .assertEqual (log_record .body , "Critical message within span" )
232205 self .assertEqual (log_record .severity_text , "CRITICAL" )
233206 self .assertEqual (log_record .severity_number , SeverityNumber .FATAL )
234207 span_context = span .get_span_context ()
235208 self .assertEqual (log_record .trace_id , span_context .trace_id )
236209 self .assertEqual (log_record .span_id , span_context .span_id )
237210 self .assertEqual (log_record .trace_flags , span_context .trace_flags )
211+
212+
213+ def set_up_test_logging (level ):
214+ logger_provider = LoggerProvider ()
215+ processor = FakeProcessor ()
216+ logger_provider .add_log_record_processor (processor )
217+ logger = logging .getLogger ("foo" )
218+ handler = LoggingHandler (level = level , logger_provider = logger_provider )
219+ logger .addHandler (handler )
220+ return processor , logger
221+
222+
223+ class FakeProcessor (LogRecordProcessor ):
224+ def __init__ (self ):
225+ self .log_data_emitted = []
226+
227+ def emit (self , log_data : LogData ):
228+ self .log_data_emitted .append (log_data )
229+
230+ def shutdown (self ):
231+ pass
232+
233+ def force_flush (self , timeout_millis : int = 30000 ):
234+ pass
235+
236+ def emit_count (self ):
237+ return len (self .log_data_emitted )
238+
239+ def get_log_record (self , i ):
240+ return self .log_data_emitted [i ].log_record
0 commit comments