Skip to content

Commit 5dddeed

Browse files
committed
update logger tests
1 parent 4979ff0 commit 5dddeed

File tree

1 file changed

+55
-177
lines changed

1 file changed

+55
-177
lines changed

tests/tracer/test_logger.py

Lines changed: 55 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@
33
import mock
44
import pytest
55

6-
from ddtrace.internal.logger import DDLogger
6+
import ddtrace.internal.logger
77
from ddtrace.internal.logger import get_logger
8+
from ddtrace.internal.logger import LoggingBucket
89
from tests.utils import BaseTestCase
910

1011

1112
ALL_LEVEL_NAMES = ("debug", "info", "warning", "error", "exception", "critical", "fatal")
1213

1314

14-
class DDLoggerTestCase(BaseTestCase):
15+
class LoggerTestCase(BaseTestCase):
1516
def setUp(self):
16-
super(DDLoggerTestCase, self).setUp()
17+
super(LoggerTestCase, self).setUp()
1718

1819
self.root = logging.root
1920
self.manager = self.root.manager
@@ -26,7 +27,11 @@ def tearDown(self):
2627
self.root = None
2728
self.manager = None
2829

29-
super(DDLoggerTestCase, self).tearDown()
30+
# Reset to default values
31+
ddtrace.internal.logger._buckets.clear()
32+
ddtrace.internal.logger._rate_limit = 60
33+
34+
super(LoggerTestCase, self).tearDown()
3035

3136
def _make_record(
3237
self,
@@ -42,42 +47,31 @@ def _make_record(
4247
):
4348
return logger.makeRecord(logger.name, level, fn, lno, msg, args, exc_info, func, extra)
4449

45-
@mock.patch("ddtrace.internal.logger.DDLogger.handle")
46-
def assert_log_records(self, log, expected_levels, handle):
47-
for name in ALL_LEVEL_NAMES:
48-
method = getattr(log, name)
49-
method("test")
50-
51-
records = [args[0][0] for args in handle.call_args_list]
52-
for record in records:
53-
self.assertIsInstance(record, logging.LogRecord)
54-
self.assertTrue("test.logger" in record.name or "ddtrace" in record.name)
55-
56-
levels = [r.levelname for r in records]
57-
self.assertEqual(levels, expected_levels)
58-
5950
def test_get_logger(self):
6051
"""
6152
When using `get_logger` to get a logger
6253
When the logger does not exist
63-
We create a new DDLogger
54+
We create a new logging.Logger
6455
When the logger exists
6556
We return the expected logger
6657
When a different logger is requested
67-
We return a new DDLogger
58+
We return a new logging.Logger
6859
When a Placeholder exists
69-
We return DDLogger
60+
We return logging.Logger
7061
"""
62+
assert self.manager is not None
63+
7164
# Assert the logger doesn't already exist
7265
self.assertNotIn("test.logger", self.manager.loggerDict)
7366

7467
# Fetch a new logger
7568
log = get_logger("test.logger")
69+
assert ddtrace.internal.logger.log_filter in log.filters
7670
self.assertEqual(log.name, "test.logger")
7771
self.assertEqual(log.level, logging.NOTSET)
7872

79-
# Ensure it is a DDLogger
80-
self.assertIsInstance(log, DDLogger)
73+
# Ensure it is a logging.Logger
74+
self.assertIsInstance(log, logging.Logger)
8175
# Make sure it is stored in all the places we expect
8276
self.assertEqual(self.manager.getLogger("test.logger"), log)
8377
self.assertEqual(self.manager.loggerDict["test.logger"], log)
@@ -93,163 +87,45 @@ def test_get_logger(self):
9387
self.assertNotEqual(log, new_log)
9488

9589
# If a PlaceHolder is in place of the logger
96-
# We should return the DDLogger
90+
# We should return the logging.Logger
9791
self.assertIsInstance(self.manager.loggerDict["new.test"], logging.PlaceHolder)
9892
log = get_logger("new.test")
9993
self.assertEqual(log.name, "new.test")
100-
self.assertIsInstance(log, DDLogger)
101-
102-
def test_get_logger_children(self):
103-
"""
104-
When using `get_logger` to get a logger
105-
We appropriately assign children loggers
106-
107-
DEV: This test case is to ensure we are calling `manager._fixupChildren(logger)`
108-
"""
109-
root = get_logger("test")
110-
root.setLevel(logging.WARNING)
111-
112-
child_logger = get_logger("test.newplaceholder.long.component")
113-
self.assertEqual(child_logger.parent, root)
114-
115-
parent_logger = get_logger("test.newplaceholder")
116-
self.assertEqual(child_logger.parent, parent_logger)
117-
118-
parent_logger.setLevel(logging.INFO)
119-
# Because the child logger's level remains unset, it should inherit
120-
# the level of its closest parent, which is INFO.
121-
# If we did not properly maintain the logger tree, this would fail
122-
# because child_logger would be set to the default when it was created
123-
# which was logging.WARNING.
124-
self.assertEqual(child_logger.getEffectiveLevel(), logging.INFO)
125-
126-
# Clean up for future tests.
127-
root.setLevel(logging.NOTSET)
128-
129-
def test_get_logger_parents(self):
130-
"""
131-
When using `get_logger` to get a logger
132-
We appropriately assign parent loggers
133-
134-
DEV: This test case is to ensure we are calling `manager._fixupParents(logger)`
135-
"""
136-
# Fetch a new logger
137-
test_log = get_logger("test")
138-
self.assertEqual(test_log.parent, self.root)
139-
140-
# Fetch a new child log
141-
# Auto-associate with parent `test` logger
142-
child_log = get_logger("test.child")
143-
self.assertEqual(child_log.parent, test_log)
144-
145-
# Deep child
146-
deep_log = get_logger("test.child.logger.from.test.case")
147-
self.assertEqual(deep_log.parent, child_log)
148-
149-
def test_logger_init(self):
150-
"""
151-
When creating a new DDLogger
152-
Has the same interface as logging.Logger
153-
Configures a defaultdict for buckets
154-
Properly configures the rate limit
155-
"""
156-
# Create a logger
157-
log = DDLogger("test.logger")
158-
159-
# Ensure we set the name and use default log level
160-
self.assertEqual(log.name, "test.logger")
161-
self.assertEqual(log.level, logging.NOTSET)
162-
163-
# Assert DDLogger default properties
164-
self.assertIsInstance(log.buckets, dict)
165-
self.assertEqual(log.rate_limit, 60)
166-
167-
# Assert manager and parent
168-
# DEV: Parent is `None` because `manager._findParents()` doesn't get called
169-
# unless we use `get_logger` (this is the same behavior as `logging.getLogger` and `Logger('name')`)
170-
self.assertEqual(log.manager, self.manager)
171-
self.assertIsNone(log.parent)
172-
173-
# Override rate limit from environment variable
174-
with self.override_env(dict(DD_TRACE_LOGGING_RATE="10")):
175-
log = DDLogger("test.logger")
176-
self.assertEqual(log.rate_limit, 10)
177-
178-
# Set specific log level
179-
log = DDLogger("test.logger", level=logging.DEBUG)
180-
self.assertEqual(log.level, logging.DEBUG)
181-
182-
def test_logger_log(self):
183-
"""
184-
When calling `DDLogger` log methods
185-
We call `DDLogger.handle` with the expected log record
186-
"""
187-
log = get_logger("test.logger")
188-
189-
# -- NOTSET
190-
# By default no level is set so we only get warn, error, and critical messages
191-
self.assertEqual(log.level, logging.NOTSET)
192-
# `log.warning`, `log.error`, `log.exception`, `log.critical`, `log.fatal`
193-
self.assert_log_records(log, ["WARNING", "ERROR", "ERROR", "CRITICAL", "CRITICAL"])
194-
195-
# -- CRITICAL
196-
log.setLevel(logging.CRITICAL)
197-
# `log.critical`, `log.fatal`
198-
self.assert_log_records(log, ["CRITICAL", "CRITICAL"])
199-
200-
# -- ERROR
201-
log.setLevel(logging.ERROR)
202-
# `log.error`, `log.exception`, `log.critical`, `log.fatal`
203-
self.assert_log_records(log, ["ERROR", "ERROR", "CRITICAL", "CRITICAL"])
204-
205-
# -- WARN
206-
log.setLevel(logging.WARN)
207-
# `log.warning`, `log.error`, `log.exception`, `log.critical`, `log.fatal`
208-
self.assert_log_records(log, ["WARNING", "ERROR", "ERROR", "CRITICAL", "CRITICAL"])
209-
210-
# -- INFO
211-
log.setLevel(logging.INFO)
212-
# `log.info`, `log.warning`, `log.error`, `log.exception`, `log.critical`, `log.fatal`
213-
self.assert_log_records(log, ["INFO", "WARNING", "ERROR", "ERROR", "CRITICAL", "CRITICAL"])
214-
215-
# -- DEBUG
216-
log.setLevel(logging.DEBUG)
217-
# `log.debug`, `log.info`, `log.warning`, `log.error`, `log.exception`, `log.critical`, `log.fatal`
218-
self.assert_log_records(log, ["DEBUG", "INFO", "WARNING", "ERROR", "ERROR", "CRITICAL", "CRITICAL"])
94+
self.assertIsInstance(log, logging.Logger)
21995

220-
@mock.patch("logging.Logger.handle")
221-
def test_logger_handle_no_limit(self, base_handle):
96+
@mock.patch("logging.Logger.callHandlers")
97+
def test_logger_handle_no_limit(self, call_handlers):
22298
"""
223-
Calling `DDLogger.handle`
99+
Calling `logging.Logger.handle`
224100
When no rate limit is set
225101
Always calls the base `Logger.handle`
226102
"""
227103
# Configure an INFO logger with no rate limit
228104
log = get_logger("test.logger")
229105
log.setLevel(logging.INFO)
230-
log.rate_limit = 0
106+
ddtrace.internal.logger._rate_limit = 0
231107

232108
# Log a bunch of times very quickly (this is fast)
233109
for _ in range(1000):
234110
log.info("test")
235111

236112
# Assert that we did not perform any rate limiting
237-
self.assertEqual(base_handle.call_count, 1000)
113+
self.assertEqual(call_handlers.call_count, 1000)
238114

239115
# Our buckets are empty
240-
self.assertEqual(log.buckets, dict())
116+
self.assertEqual(ddtrace.internal.logger._buckets, dict())
241117

242-
@mock.patch("logging.Logger.handle")
243-
def test_logger_handle_debug(self, base_handle):
118+
@mock.patch("logging.Logger.callHandlers")
119+
def test_logger_handle_debug(self, call_handlers):
244120
"""
245-
Calling `DDLogger.handle`
121+
Calling `logging.Logger.handle`
246122
When effective level is DEBUG
247123
Always calls the base `Logger.handle`
248124
"""
249125
# Configure an INFO logger with no rate limit
250126
log = get_logger("test.logger")
251127
log.setLevel(logging.DEBUG)
252-
assert log.rate_limit > 0
128+
assert ddtrace.internal.logger._rate_limit > 0
253129

254130
# Log a bunch of times very quickly (this is fast)
255131
for level in ALL_LEVEL_NAMES:
@@ -259,15 +135,15 @@ def test_logger_handle_debug(self, base_handle):
259135

260136
# Assert that we did not perform any rate limiting
261137
total = 1000 * len(ALL_LEVEL_NAMES)
262-
self.assertTrue(total <= base_handle.call_count <= total + 1)
138+
self.assertTrue(total <= call_handlers.call_count <= total + 1)
263139

264140
# Our buckets are empty
265-
self.assertEqual(log.buckets, dict())
141+
self.assertEqual(ddtrace.internal.logger._buckets, dict())
266142

267-
@mock.patch("logging.Logger.handle")
268-
def test_logger_handle_bucket(self, base_handle):
143+
@mock.patch("logging.Logger.callHandlers")
144+
def test_logger_handle_bucket(self, call_handlers):
269145
"""
270-
When calling `DDLogger.handle`
146+
When calling `logging.Logger.handle`
271147
With a record
272148
We pass it to the base `Logger.handle`
273149
We create a bucket for tracking
@@ -279,30 +155,31 @@ def test_logger_handle_bucket(self, base_handle):
279155
log.handle(record)
280156

281157
# We passed to base Logger.handle
282-
base_handle.assert_called_once_with(record)
158+
call_handlers.assert_called_once_with(record)
283159

284160
# We added an bucket entry for this record
285161
key = (record.name, record.levelno, record.pathname, record.lineno)
286-
logging_bucket = log.buckets.get(key)
287-
self.assertIsInstance(logging_bucket, DDLogger.LoggingBucket)
162+
logging_bucket = ddtrace.internal.logger._buckets.get(key)
163+
self.assertIsInstance(logging_bucket, LoggingBucket)
288164

289165
# The bucket entry is correct
290-
expected_bucket = int(record.created / log.rate_limit)
166+
expected_bucket = int(record.created / ddtrace.internal.logger._rate_limit)
291167
self.assertEqual(logging_bucket.bucket, expected_bucket)
292168
self.assertEqual(logging_bucket.skipped, 0)
293169

294-
@mock.patch("logging.Logger.handle")
295-
def test_logger_handle_bucket_limited(self, base_handle):
170+
@mock.patch("logging.Logger.callHandlers")
171+
def test_logger_handle_bucket_limited(self, call_handlers):
296172
"""
297-
When calling `DDLogger.handle`
173+
When calling `logging.Logger.handle`
298174
With multiple records in a single time frame
299175
We pass only the first to the base `Logger.handle`
300176
We keep track of the number skipped
301177
"""
302178
log = get_logger("test.logger")
303179

304180
# Create log record and handle it
305-
first_record = self._make_record(log, msg="first")
181+
record = self._make_record(log, msg="first")
182+
first_record = record
306183
log.handle(first_record)
307184

308185
for _ in range(100):
@@ -312,21 +189,22 @@ def test_logger_handle_bucket_limited(self, base_handle):
312189
log.handle(record)
313190

314191
# We passed to base Logger.handle
315-
base_handle.assert_called_once_with(first_record)
192+
call_handlers.assert_called_once_with(first_record)
316193

317194
# We added an bucket entry for these records
318195
key = (record.name, record.levelno, record.pathname, record.lineno)
319-
logging_bucket = log.buckets.get(key)
196+
logging_bucket = ddtrace.internal.logger._buckets.get(key)
197+
assert logging_bucket is not None
320198

321199
# The bucket entry is correct
322-
expected_bucket = int(first_record.created / log.rate_limit)
200+
expected_bucket = int(first_record.created / ddtrace.internal.logger._rate_limit)
323201
self.assertEqual(logging_bucket.bucket, expected_bucket)
324202
self.assertEqual(logging_bucket.skipped, 100)
325203

326-
@mock.patch("logging.Logger.handle")
327-
def test_logger_handle_bucket_skipped_msg(self, base_handle):
204+
@mock.patch("logging.Logger.callHandlers")
205+
def test_logger_handle_bucket_skipped_msg(self, call_handlers):
328206
"""
329-
When calling `DDLogger.handle`
207+
When calling `logging.Logger.handle`
330208
When a bucket exists for a previous time frame
331209
We pass only the record to the base `Logger.handle`
332210
We update the record message to include the number of skipped messages
@@ -340,23 +218,23 @@ def test_logger_handle_bucket_skipped_msg(self, base_handle):
340218

341219
# Create a bucket entry for this record
342220
key = (record.name, record.levelno, record.pathname, record.lineno)
343-
bucket = int(record.created / log.rate_limit)
221+
bucket = int(record.created / ddtrace.internal.logger._rate_limit)
344222
# We want the time bucket to be for an older bucket
345-
log.buckets[key] = DDLogger.LoggingBucket(bucket=bucket - 1, skipped=20)
223+
ddtrace.internal.logger._buckets[key] = LoggingBucket(bucket=bucket - 1, skipped=20)
346224

347225
# Handle our record
348226
log.handle(record)
349227

350228
# We passed to base Logger.handle
351-
base_handle.assert_called_once_with(record)
229+
call_handlers.assert_called_once_with(record)
352230

353231
self.assertEqual(record.msg, original_msg + ", %s additional messages skipped")
354232
self.assertEqual(record.args, original_args + (20,))
355233
self.assertEqual(record.getMessage(), "hello 1, 20 additional messages skipped")
356234

357235
def test_logger_handle_bucket_key(self):
358236
"""
359-
When calling `DDLogger.handle`
237+
When calling `logging.Logger.handle`
360238
With different log messages
361239
We use different buckets to limit them
362240
"""
@@ -388,7 +266,7 @@ def get_key(record):
388266
all_records = (record1, record2, record3, record4, record5, record6)
389267
[log.handle(record) for record in all_records]
390268

391-
buckets = log.buckets
269+
buckets = ddtrace.internal.logger._buckets
392270
# We have 6 records but only end up with 5 buckets
393271
self.assertEqual(len(buckets), 5)
394272

0 commit comments

Comments
 (0)