Skip to content

Commit 3ef45eb

Browse files
authored
Fix logging_exclude_endpoints (#74)
* Fix logging exclude endpoints for FastAPI * Fix logging exclude endpoints for Litestar * Fix formatting
1 parent 8e0f97a commit 3ef45eb

File tree

4 files changed

+56
-8
lines changed

4 files changed

+56
-8
lines changed

microbootstrap/instruments/logging_instrument.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import pydantic
99
import structlog
10+
import typing_extensions
1011
from opentelemetry import trace
1112

1213
from microbootstrap.instruments.base import BaseInstrumentConfig, Instrument
@@ -128,6 +129,13 @@ class LoggingConfig(BaseInstrumentConfig):
128129
logging_exclude_endpoints: list[str] = pydantic.Field(default_factory=lambda: ["/health/", "/metrics"])
129130
logging_turn_off_middleware: bool = False
130131

132+
@pydantic.model_validator(mode="after")
133+
def remove_trailing_slashes_from_logging_exclude_endpoints(self) -> typing_extensions.Self:
134+
self.logging_exclude_endpoints = [
135+
one_endpoint.removesuffix("/") for one_endpoint in self.logging_exclude_endpoints
136+
]
137+
return self
138+
131139

132140
class LoggingInstrument(Instrument[LoggingConfig]):
133141
instrument_name = "Logging"

microbootstrap/middlewares/fastapi.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ async def dispatch(
1717
request: fastapi.Request,
1818
call_next: RequestResponseEndpoint,
1919
) -> fastapi.Response:
20+
request_path: typing.Final = request.url.path.removesuffix("/")
2021
should_log: typing.Final = not any(
21-
exclude_endpoint in str(request.url) for exclude_endpoint in exclude_endpoints
22+
exclude_endpoint == request_path for exclude_endpoint in exclude_endpoints
2223
)
2324
start_time: typing.Final = time.perf_counter_ns()
2425
try:

microbootstrap/middlewares/litestar.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,8 @@ async def __call__(
2727
start_time: typing.Final[int] = time.perf_counter_ns()
2828

2929
async def log_message_wrapper(message: litestar.types.Message) -> None:
30-
should_log: typing.Final = not any(
31-
exclude_endpoint in str(request.url) for exclude_endpoint in exclude_endpoints
32-
)
30+
request_path = request.url.path.removesuffix("/")
31+
should_log: typing.Final = not any(one_endpoint == request_path for one_endpoint in exclude_endpoints)
3332
if message["type"] == "http.response.start" and should_log:
3433
log_level: str = "info" if message["status"] < HTTP_500_INTERNAL_SERVER_ERROR else "exception"
3534
fill_log_message(log_level, request, message["status"], start_time)

tests/instruments/test_logging.py

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import logging
22
import typing
33
from io import StringIO
4+
from unittest import mock
45

56
import fastapi
67
import litestar
8+
import pytest
79
from fastapi.testclient import TestClient as FastAPITestClient
810
from litestar.testing import TestClient as LitestarTestClient
911
from opentelemetry import trace
@@ -51,7 +53,9 @@ def test_litestar_logging_bootstrap(minimal_logging_config: LoggingConfig) -> No
5153
assert len(bootsrap_result["middleware"]) == 1
5254

5355

54-
def test_litestar_logging_bootstrap_working(minimal_logging_config: LoggingConfig) -> None:
56+
def test_litestar_logging_bootstrap_working(
57+
monkeypatch: pytest.MonkeyPatch, minimal_logging_config: LoggingConfig
58+
) -> None:
5559
logging_instrument: typing.Final = LitestarLoggingInstrument(minimal_logging_config)
5660

5761
@litestar.get("/test-handler")
@@ -63,11 +67,28 @@ async def error_handler() -> str:
6367
route_handlers=[error_handler],
6468
**logging_instrument.bootstrap_before(),
6569
)
70+
monkeypatch.setattr("microbootstrap.middlewares.litestar.fill_log_message", fill_log_mock := mock.Mock())
6671

6772
with LitestarTestClient(app=litestar_application) as test_client:
6873
test_client.get("/test-handler?test-query=1")
6974
test_client.get("/test-handler")
7075

76+
assert fill_log_mock.call_count == 2 # noqa: PLR2004
77+
78+
79+
def test_litestar_logging_bootstrap_ignores_health(
80+
monkeypatch: pytest.MonkeyPatch, minimal_logging_config: LoggingConfig
81+
) -> None:
82+
logging_instrument: typing.Final = LitestarLoggingInstrument(minimal_logging_config)
83+
logging_instrument.bootstrap()
84+
litestar_application: typing.Final = litestar.Litestar(**logging_instrument.bootstrap_before())
85+
monkeypatch.setattr("microbootstrap.middlewares.litestar.fill_log_message", fill_log_mock := mock.Mock())
86+
87+
with LitestarTestClient(app=litestar_application) as test_client:
88+
test_client.get("/health")
89+
90+
assert fill_log_mock.call_count == 0
91+
7192

7293
def test_litestar_logging_bootstrap_tracer_injection(minimal_logging_config: LoggingConfig) -> None:
7394
trace.set_tracer_provider(TracerProvider())
@@ -133,18 +154,37 @@ def test_memory_logger_factory_error() -> None:
133154
assert error_message in test_stream.getvalue()
134155

135156

136-
def test_fastapi_logging_bootstrap_working(minimal_logging_config: LoggingConfig) -> None:
137-
logging_instrument: typing.Final = FastApiLoggingInstrument(minimal_logging_config)
138-
157+
def test_fastapi_logging_bootstrap_working(
158+
monkeypatch: pytest.MonkeyPatch, minimal_logging_config: LoggingConfig
159+
) -> None:
139160
fastapi_application: typing.Final = fastapi.FastAPI()
140161

141162
@fastapi_application.get("/test-handler")
142163
async def test_handler() -> str:
143164
return "Ok"
144165

166+
logging_instrument: typing.Final = FastApiLoggingInstrument(minimal_logging_config)
145167
logging_instrument.bootstrap()
146168
logging_instrument.bootstrap_after(fastapi_application)
169+
monkeypatch.setattr("microbootstrap.middlewares.fastapi.fill_log_message", fill_log_mock := mock.Mock())
147170

148171
with FastAPITestClient(app=fastapi_application) as test_client:
149172
test_client.get("/test-handler?test-query=1")
150173
test_client.get("/test-handler")
174+
175+
assert fill_log_mock.call_count == 2 # noqa: PLR2004
176+
177+
178+
def test_fastapi_logging_bootstrap_ignores_health(
179+
monkeypatch: pytest.MonkeyPatch, minimal_logging_config: LoggingConfig
180+
) -> None:
181+
fastapi_application: typing.Final = fastapi.FastAPI()
182+
logging_instrument: typing.Final = FastApiLoggingInstrument(minimal_logging_config)
183+
logging_instrument.bootstrap()
184+
logging_instrument.bootstrap_after(fastapi_application)
185+
monkeypatch.setattr("microbootstrap.middlewares.fastapi.fill_log_message", fill_log_mock := mock.Mock())
186+
187+
with FastAPITestClient(app=fastapi_application) as test_client:
188+
test_client.get("/health")
189+
190+
assert fill_log_mock.call_count == 0

0 commit comments

Comments
 (0)