170 lines
5.2 KiB
Python
170 lines
5.2 KiB
Python
import logging
|
|
import importlib
|
|
import sys
|
|
|
|
import pytest
|
|
|
|
from python.log_system.log_funcs import error, exception
|
|
from python.log_system.logger import (
|
|
ColoredFormatter,
|
|
LogLevel,
|
|
logger,
|
|
set_debug,
|
|
set_file_logging,
|
|
)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def reset_logger_state():
|
|
original_config = logger.config.copy()
|
|
original_module_settings = logger.config["module_settings"].copy()
|
|
original_enabled = logger.enabled
|
|
|
|
logger.reset_loggers()
|
|
logger.set_enabled(True)
|
|
logger.config["module_settings"] = {}
|
|
logger.config["log_to_file"] = False
|
|
yield
|
|
|
|
logger.reset_loggers()
|
|
logger.config.clear()
|
|
logger.config.update(original_config)
|
|
logger.config["module_settings"] = original_module_settings
|
|
logger.set_enabled(original_enabled)
|
|
|
|
|
|
def test_log_levels_honor_global_and_module_overrides():
|
|
logger.set_global_level(LogLevel.INFO)
|
|
|
|
assert not logger.is_level_enabled("test", LogLevel.DEBUG)
|
|
assert logger.is_level_enabled("test", LogLevel.INFO)
|
|
|
|
logger.set_module_level("test", LogLevel.DEBUG)
|
|
assert logger.is_level_enabled("test", LogLevel.DEBUG)
|
|
|
|
logger.set_module_level("test", LogLevel.NONE)
|
|
assert not logger.is_level_enabled("test", LogLevel.ERROR)
|
|
|
|
logger.set_enabled(False)
|
|
assert not logger.is_level_enabled("other", LogLevel.ERROR)
|
|
|
|
|
|
def test_colored_formatter_contains_timestamp_module_and_level():
|
|
record = logging.LogRecord(
|
|
name="layerforge.test",
|
|
level=logging.INFO,
|
|
pathname=__file__,
|
|
lineno=1,
|
|
msg="hello %s",
|
|
args=("world",),
|
|
exc_info=None,
|
|
)
|
|
formatter = ColoredFormatter(datefmt="%H:%M:%S", use_colors=False)
|
|
|
|
formatted = formatter.format(record)
|
|
|
|
assert "[layerforge]" in formatted
|
|
assert "[INFO]" in formatted
|
|
assert "hello world" in formatted
|
|
|
|
|
|
def test_colored_formatter_includes_exception_details_and_color():
|
|
try:
|
|
raise ValueError("boom")
|
|
except ValueError:
|
|
record = logging.LogRecord(
|
|
name="layerforge.test",
|
|
level=logging.ERROR,
|
|
pathname=__file__,
|
|
lineno=1,
|
|
msg="failed",
|
|
args=(),
|
|
exc_info=sys.exc_info(),
|
|
)
|
|
|
|
formatter = ColoredFormatter(datefmt="%H:%M:%S", use_colors=True)
|
|
formatted = formatter.format(record)
|
|
|
|
assert "\x1b[1;97;" in formatted
|
|
assert "ValueError: boom" in formatted
|
|
|
|
|
|
def test_environment_configuration_parses_valid_and_invalid_values(monkeypatch, tmp_path):
|
|
monkeypatch.setenv("AZLOGS_LOG_LEVEL", "DEBUG")
|
|
monkeypatch.setenv("AZLOGS_MODULE_LEVELS", '{"canvas": "ERROR", "ignored": "unknown"}')
|
|
monkeypatch.setenv("AZLOGS_USE_COLORS", "false")
|
|
monkeypatch.setenv("AZLOGS_LOG_TO_FILE", "true")
|
|
monkeypatch.setenv("AZLOGS_LOG_DIR", str(tmp_path / "logs"))
|
|
monkeypatch.setenv("AZLOGS_MAX_FILE_SIZE_MB", "2")
|
|
monkeypatch.setenv("AZLOGS_BACKUP_COUNT", "3")
|
|
|
|
logger._load_config_from_env()
|
|
|
|
assert logger.config["global_level"] == LogLevel.DEBUG
|
|
assert logger.config["module_settings"]["canvas"] == LogLevel.ERROR
|
|
assert logger.config["use_colors"] is False
|
|
assert logger.config["log_to_file"] is True
|
|
assert logger.config["max_file_size_mb"] == 2
|
|
assert logger.config["backup_count"] == 3
|
|
|
|
monkeypatch.setenv("AZLOGS_MODULE_LEVELS", "not-json")
|
|
monkeypatch.setenv("AZLOGS_MAX_FILE_SIZE_MB", "not-an-int")
|
|
monkeypatch.setenv("AZLOGS_BACKUP_COUNT", "not-an-int")
|
|
logger._load_config_from_env()
|
|
|
|
assert logger.config["module_settings"]["canvas"] == LogLevel.ERROR
|
|
|
|
|
|
def test_configure_disables_file_logging_when_directory_creation_fails(monkeypatch):
|
|
def raise_os_error(*args, **kwargs):
|
|
raise OSError("access denied")
|
|
|
|
logger_module = importlib.import_module("python.log_system.logger")
|
|
monkeypatch.setattr(logger_module.os, "makedirs", raise_os_error)
|
|
monkeypatch.setattr(logger_module.traceback, "print_exc", lambda: None)
|
|
|
|
logger.configure({"log_to_file": True, "log_dir": "blocked"})
|
|
|
|
assert logger.config["log_to_file"] is False
|
|
|
|
|
|
def test_file_logging_writes_to_configured_directory(tmp_path):
|
|
log_directory = tmp_path / "logs"
|
|
set_file_logging(True, str(log_directory))
|
|
logger.set_global_level(LogLevel.INFO)
|
|
|
|
logger.info("file-test", "message")
|
|
logger.info("file-test", "second message")
|
|
|
|
log_files = list(log_directory.glob("azlogs_*.log"))
|
|
assert len(log_files) == 1
|
|
assert "message" in log_files[0].read_text(encoding="utf-8")
|
|
|
|
set_file_logging(False)
|
|
assert logger.config["log_to_file"] is False
|
|
|
|
|
|
def test_error_helpers_forward_messages_and_disabled_logger_skips_output(monkeypatch):
|
|
logger.set_enabled(False)
|
|
logger.log("disabled", LogLevel.INFO, "ignored")
|
|
logger.set_enabled(True)
|
|
|
|
calls = []
|
|
monkeypatch.setattr(logger, "log", lambda *args, **kwargs: calls.append((args, kwargs)))
|
|
|
|
error("helper", "failure")
|
|
exception("helper", "with traceback")
|
|
|
|
assert calls == [
|
|
(("helper", LogLevel.ERROR, "failure"), {}),
|
|
(("helper", LogLevel.ERROR, "with traceback"), {"exc_info": True}),
|
|
]
|
|
|
|
|
|
def test_set_debug_switches_global_threshold():
|
|
set_debug(True)
|
|
assert logger.config["global_level"] == LogLevel.DEBUG
|
|
|
|
set_debug(False)
|
|
assert logger.config["global_level"] == LogLevel.INFO
|