📦 EqualifyEverything / equalify-reflow

📄 test_logging_config.py · 94 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94"""Tests for JSON logging configuration."""

import json
import logging
import uuid

import pytest
from src.utils.logging_config import JsonFormatter, configure_logging

pytestmark = pytest.mark.unit


def _record(**extra) -> logging.LogRecord:
    """Build a LogRecord with optional extra fields, the way logging does."""
    record = logging.LogRecord(
        name="src.test",
        level=logging.INFO,
        pathname=__file__,
        lineno=1,
        msg="hello %s",
        args=("world",),
        exc_info=None,
    )
    for key, value in extra.items():
        setattr(record, key, value)
    return record


def test_formats_valid_json_with_core_fields():
    out = json.loads(JsonFormatter().format(_record()))
    assert out["level"] == "INFO"
    assert out["logger"] == "src.test"
    assert out["message"] == "hello world"  # %-args rendered
    assert "timestamp" in out


def test_includes_extra_fields():
    out = json.loads(JsonFormatter().format(_record(
        path="/api/v1/documents",
        status_code=200,
        user_sub="zach",
    )))
    assert out["path"] == "/api/v1/documents"
    assert out["status_code"] == 200
    assert out["user_sub"] == "zach"


def test_does_not_emit_standard_record_internals():
    out = json.loads(JsonFormatter().format(_record()))
    # Internal LogRecord attributes must not leak into the payload.
    for noise in ("args", "msg", "levelno", "pathname", "thread"):
        assert noise not in out


def test_non_serialisable_extra_degrades_gracefully():
    job_id = uuid.uuid4()
    out = json.loads(JsonFormatter().format(_record(job_id=job_id)))
    # default=str keeps the log call from crashing on a UUID.
    assert out["job_id"] == str(job_id)


def test_includes_exception_info():
    try:
        raise ValueError("boom")
    except ValueError:
        import sys
        record = _record()
        record.exc_info = sys.exc_info()
        out = json.loads(JsonFormatter().format(record))
    assert "exc_info" in out
    assert "ValueError: boom" in out["exc_info"]


def test_configure_logging_json_installs_json_formatter():
    try:
        configure_logging(level="INFO", json_format=True)
        root = logging.getLogger()
        assert len(root.handlers) == 1
        assert isinstance(root.handlers[0].formatter, JsonFormatter)
        assert root.level == logging.INFO
    finally:
        logging.getLogger().handlers.clear()


def test_configure_logging_text_installs_plain_formatter():
    try:
        configure_logging(level="DEBUG", json_format=False)
        root = logging.getLogger()
        assert len(root.handlers) == 1
        assert not isinstance(root.handlers[0].formatter, JsonFormatter)
        assert root.level == logging.DEBUG
    finally:
        logging.getLogger().handlers.clear()