|
18 | 18 | _clear_request_id, |
19 | 19 | _propagate_request_id, |
20 | 20 | _restore_request_id, |
| 21 | + _task_log_context, |
21 | 22 | ) |
22 | 23 |
|
23 | 24 | from .conftest import ( |
|
29 | 30 |
|
30 | 31 | @pytest.fixture(autouse=True) |
31 | 32 | def _clean_request_context(): |
32 | | - """Reset request context before and after each test.""" |
| 33 | + """Reset request context and Loguru log context before and after each test.""" |
33 | 34 | reset_request_context() |
34 | 35 | yield |
| 36 | + # Clean up any leaked Loguru contextualize from Celery signal tests |
| 37 | + ctx = _task_log_context.get() |
| 38 | + if ctx is not None: |
| 39 | + ctx.__exit__(None, None, None) |
| 40 | + _task_log_context.set(None) |
35 | 41 | reset_request_context() |
36 | 42 |
|
37 | 43 |
|
@@ -193,7 +199,7 @@ async def test_rejects_oversized_request_id(self, mock_asgi_app): |
193 | 199 | assert UUID_PATTERN.match(request_id_headers[0]) |
194 | 200 |
|
195 | 201 | async def test_request_id_in_log_output(self, mock_asgi_app, loguru_caplog): |
196 | | - """Request ID appears in log records via the patcher.""" |
| 202 | + """Request ID appears in log records via logger.contextualize().""" |
197 | 203 | app, _ = mock_asgi_app() |
198 | 204 | middleware = LogRequestMiddleware(app) |
199 | 205 |
|
@@ -268,24 +274,32 @@ async def test_concurrent_requests_get_different_ids(self, mock_asgi_app): |
268 | 274 | assert id1 != id2 |
269 | 275 |
|
270 | 276 |
|
271 | | -class TestLoggerPatcher: |
272 | | - """Tests for the Loguru patcher that injects request_id.""" |
| 277 | +class TestLoggerContextualize: |
| 278 | + """Tests for request_id injection via logger.contextualize().""" |
273 | 279 |
|
274 | | - def test_patcher_injects_request_id(self, loguru_caplog): |
275 | | - """When request_id is set, it appears in log records.""" |
276 | | - set_request_id("patcher-test-abc") |
277 | | - logger.info("test message") |
| 280 | + def test_contextualize_injects_request_id(self, loguru_caplog): |
| 281 | + """When logger.contextualize is active, request_id appears in log records.""" |
| 282 | + with logger.contextualize(request_id="ctx-test-abc"): |
| 283 | + logger.info("test message") |
278 | 284 |
|
279 | 285 | record = loguru_caplog.records[-1] |
280 | | - assert record.extra.get("request_id") == "patcher-test-abc" |
| 286 | + assert record.extra.get("request_id") == "ctx-test-abc" |
281 | 287 |
|
282 | | - def test_patcher_omits_request_id_when_none(self, loguru_caplog): |
283 | | - """When no request_id is set, it's not added to log records.""" |
| 288 | + def test_no_request_id_outside_context(self, loguru_caplog): |
| 289 | + """When no contextualize is active, request_id is not in log records.""" |
284 | 290 | logger.info("test message without context") |
285 | 291 |
|
286 | 292 | record = loguru_caplog.records[-1] |
287 | 293 | assert "request_id" not in record.extra |
288 | 294 |
|
| 295 | + def test_set_request_id_alone_does_not_inject_into_logs(self, loguru_caplog): |
| 296 | + """set_request_id without contextualize does not inject into log records.""" |
| 297 | + set_request_id("only-context-var") |
| 298 | + logger.info("test message") |
| 299 | + |
| 300 | + record = loguru_caplog.records[-1] |
| 301 | + assert "request_id" not in record.extra |
| 302 | + |
289 | 303 |
|
290 | 304 | class TestCelerySignals: |
291 | 305 | """Tests for Celery request_id propagation signals.""" |
@@ -325,10 +339,42 @@ def test_restore_request_id_skips_when_absent(self): |
325 | 339 |
|
326 | 340 | assert get_request_id() is None |
327 | 341 |
|
| 342 | + def test_restore_injects_request_id_into_logs(self, loguru_caplog): |
| 343 | + """task_prerun sets up logger.contextualize so logs get request_id.""" |
| 344 | + mock_task = type( |
| 345 | + "MockTask", |
| 346 | + (), |
| 347 | + {"request": type("MockRequest", (), {"request_id": "celery-log-789"})()}, |
| 348 | + )() |
| 349 | + |
| 350 | + _restore_request_id(task=mock_task) |
| 351 | + logger.info("task log message") |
| 352 | + |
| 353 | + record = loguru_caplog.records[-1] |
| 354 | + assert record.extra.get("request_id") == "celery-log-789" |
| 355 | + |
| 356 | + # Cleanup |
| 357 | + _clear_request_id() |
| 358 | + |
328 | 359 | def test_clear_request_id_after_task(self): |
329 | 360 | """task_postrun clears only the request_id, not other context.""" |
330 | 361 | set_request_context(request_id="should-be-cleared", user_id="keep-me") |
331 | 362 | _clear_request_id() |
332 | 363 |
|
333 | 364 | assert get_request_id() is None |
334 | 365 | assert get_user_id() == "keep-me" |
| 366 | + |
| 367 | + def test_clear_removes_log_context(self, loguru_caplog): |
| 368 | + """task_postrun cleans up logger.contextualize so logs no longer get request_id.""" |
| 369 | + mock_task = type( |
| 370 | + "MockTask", |
| 371 | + (), |
| 372 | + {"request": type("MockRequest", (), {"request_id": "celery-clear-test"})()}, |
| 373 | + )() |
| 374 | + |
| 375 | + _restore_request_id(task=mock_task) |
| 376 | + _clear_request_id() |
| 377 | + |
| 378 | + logger.info("after task") |
| 379 | + record = loguru_caplog.records[-1] |
| 380 | + assert "request_id" not in record.extra |
0 commit comments