Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions gfmodules/logging/config_builder.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
from typing import Any

from gfmodules.logging.config import ConfigLogging
Expand All @@ -7,6 +8,11 @@
from gfmodules.logging.streams import LoggingStreams


def _at_least(loglevel: str, floor: int) -> str:
numeric = logging.getLevelNamesMapping().get(loglevel.upper())
return loglevel if numeric is not None and numeric >= floor else logging.getLevelName(floor)


class LogConfigBuilder:
"""Reads the registered logger root when ``build()`` runs, not before."""

Expand Down Expand Up @@ -118,6 +124,12 @@ def build(self) -> dict[str, Any]:
"level": "CRITICAL",
"propagate": False,
},
# Floored rather than silenced, and left propagating
# Would leak otherwise as inject logs every binding at DEBUG level
"inject": {
"level": _at_least(self.loglevel, logging.INFO),
"propagate": True,
},
},
"root": {"handlers": ["console"], "level": self.loglevel},
}
Expand Down
18 changes: 18 additions & 0 deletions tests/test_config_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import logging
from typing import Any

import pytest

from gfmodules.logging.config import ConfigLogging
from gfmodules.logging.config_builder import LogConfigBuilder
from gfmodules.logging.filters import AppFilter, PublicInspectFilter, SiemFilter
Expand Down Expand Up @@ -101,6 +103,7 @@
"propagate": False,
},
"uvicorn.access": {"handlers": [], "level": "CRITICAL", "propagate": False},
"inject": {"level": "INFO", "propagate": True},
},
"root": {"handlers": ["console", "syslog_debug"], "level": "INFO"},
}
Expand Down Expand Up @@ -208,6 +211,21 @@ def test_existing_loggers_are_left_enabled(self) -> None:
assert build(ConfigLogging())["disable_existing_loggers"] is False


class TestInjectIsFloored:
@pytest.mark.parametrize("loglevel", ["DEBUG", "NOTSET", "NONSENSE"])
def test_it_never_runs_below_info_however_verbose_the_application_is(self, loglevel: str) -> None:
assert build(ConfigLogging(), loglevel=loglevel)["loggers"]["inject"]["level"] == "INFO"

def test_an_application_quieter_than_the_floor_is_left_alone(self) -> None:
assert build(ConfigLogging(), loglevel="ERROR")["loggers"]["inject"]["level"] == "ERROR"

def test_what_survives_the_floor_still_reaches_the_debug_stream(self) -> None:
logger = build(ConfigLogging(syslog_path=SYSLOG))["loggers"]["inject"]

assert logger["propagate"] is True
assert "handlers" not in logger


class TestApplicationIdStamping:
def test_every_json_formatter_is_stamped(self) -> None:
formatters = build(ConfigLogging(application_id=APP_ID, syslog_path=SYSLOG))["formatters"]
Expand Down
Loading