81 lines
2.9 KiB
Python
81 lines
2.9 KiB
Python
import io
|
|
import logging
|
|
import os
|
|
import sys
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
import app_logging
|
|
|
|
|
|
def _reset_root_logger() -> None:
|
|
root = logging.getLogger()
|
|
for handler in list(root.handlers):
|
|
root.removeHandler(handler)
|
|
handler.close()
|
|
root.setLevel(logging.WARNING)
|
|
|
|
|
|
class ConfigureLoggingTests(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
_reset_root_logger()
|
|
self.addCleanup(_reset_root_logger)
|
|
|
|
def test_default_level_is_info(self):
|
|
with patch.dict(os.environ, {}, clear=False):
|
|
os.environ.pop("LOG_LEVEL", None)
|
|
app_logging.configure_logging()
|
|
self.assertEqual(logging.getLogger().level, logging.INFO)
|
|
|
|
def test_log_level_env_is_honored(self):
|
|
with patch.dict(os.environ, {"LOG_LEVEL": "DEBUG"}, clear=False):
|
|
app_logging.configure_logging()
|
|
self.assertEqual(logging.getLogger().level, logging.DEBUG)
|
|
|
|
def test_invalid_log_level_falls_back_to_info(self):
|
|
with patch.dict(os.environ, {"LOG_LEVEL": "TOTALLY_BOGUS"}, clear=False):
|
|
app_logging.configure_logging()
|
|
self.assertEqual(logging.getLogger().level, logging.INFO)
|
|
|
|
def test_handler_writes_to_stdout_with_expected_format(self):
|
|
# basicConfig is a no-op if root already has handlers — start clean.
|
|
with patch.dict(os.environ, {"LOG_LEVEL": "INFO"}, clear=False):
|
|
app_logging.configure_logging()
|
|
|
|
# First handler is the one we installed (basicConfig adds at most one).
|
|
handlers = logging.getLogger().handlers
|
|
self.assertTrue(handlers, "configure_logging() must install a handler")
|
|
handler = handlers[0]
|
|
self.assertIs(handler.stream, sys.stdout)
|
|
|
|
# Confirm the format string structure by emitting a record into a buffer.
|
|
formatter = handler.formatter
|
|
record = logging.LogRecord(
|
|
name="test",
|
|
level=logging.INFO,
|
|
pathname=__file__,
|
|
lineno=1,
|
|
msg="hello",
|
|
args=(),
|
|
exc_info=None,
|
|
)
|
|
rendered = formatter.format(record) if formatter else ""
|
|
# Format is "%(asctime)s - %(name)s - %(levelname)s - %(message)s".
|
|
self.assertIn(" - test - INFO - hello", rendered)
|
|
|
|
def test_configure_logging_does_not_swallow_subsequent_logs(self):
|
|
buffer = io.StringIO()
|
|
# Reset state, then replace stdout briefly so we can capture handler output.
|
|
with patch.dict(os.environ, {"LOG_LEVEL": "INFO"}, clear=False):
|
|
with patch.object(sys, "stdout", buffer):
|
|
app_logging.configure_logging()
|
|
logging.getLogger("test.module").info("payload-marker")
|
|
for handler in logging.getLogger().handlers:
|
|
handler.flush()
|
|
self.assertIn("payload-marker", buffer.getvalue())
|
|
self.assertIn("INFO", buffer.getvalue())
|
|
|
|
|
|
if __name__ == "__main__": # pragma: no cover
|
|
unittest.main()
|