Skip to content

Commit be02f98

Browse files
authored
[sdk/logs] Replace mocks with real instances where possible (#4071)
1 parent 8749168 commit be02f98

File tree

1 file changed

+73
-70
lines changed

1 file changed

+73
-70
lines changed

opentelemetry-sdk/tests/logs/test_handler.py

Lines changed: 73 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -19,50 +19,42 @@
1919
from opentelemetry._logs import get_logger as APIGetLogger
2020
from opentelemetry.attributes import BoundedAttributes
2121
from 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+
)
2328
from opentelemetry.semconv.trace import SpanAttributes
2429
from 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-
3432
class 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

Comments
 (0)