voltwire-fastapi-logging 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- voltwire_fastapi_logging-0.0.1/.gitignore +221 -0
- voltwire_fastapi_logging-0.0.1/PKG-INFO +40 -0
- voltwire_fastapi_logging-0.0.1/README.md +28 -0
- voltwire_fastapi_logging-0.0.1/pyproject.toml +23 -0
- voltwire_fastapi_logging-0.0.1/src/voltwire/fastapi/logging/__init__.py +35 -0
- voltwire_fastapi_logging-0.0.1/src/voltwire/fastapi/logging/_constants.py +11 -0
- voltwire_fastapi_logging-0.0.1/src/voltwire/fastapi/logging/base.py +81 -0
- voltwire_fastapi_logging-0.0.1/src/voltwire/fastapi/logging/middleware.py +55 -0
- voltwire_fastapi_logging-0.0.1/tests/test_sample.py +6 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
.idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
219
|
+
|
|
220
|
+
# Local tool state
|
|
221
|
+
.omc/
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: voltwire-fastapi-logging
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Logging utilities for FastAPI apps
|
|
5
|
+
Author-email: Hermann Steidel <hsteidel.software@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: <4.0,>=3.13
|
|
8
|
+
Requires-Dist: loguru>=0.7.0
|
|
9
|
+
Requires-Dist: pydantic>=2.0.0
|
|
10
|
+
Requires-Dist: starlette>=0.27.0
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
<img src="https://raw.githubusercontent.com/hsteidel/voltwire/main/assets/icons/fastapi-logging.svg" alt="" width="56" height="56" align="left">
|
|
14
|
+
|
|
15
|
+
# voltwire-fastapi-logging
|
|
16
|
+
|
|
17
|
+
Logging utilities for FastAPI apps: request-context-enriching middleware.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install voltwire-fastapi-logging
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quickstart
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from voltwire.fastapi.logging import setup_logging, DefaultLogSettings
|
|
29
|
+
|
|
30
|
+
setup_logging(DefaultLogSettings(enable_json=False, level="INFO"))
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Add `LoggingContextMiddleware` to enrich every log entry within a request with a correlation
|
|
34
|
+
ID, method, and path:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from voltwire.fastapi.logging import LoggingContextMiddleware, DefaultLoggingMiddlewareSettings
|
|
38
|
+
|
|
39
|
+
app.add_middleware(LoggingContextMiddleware, settings=DefaultLoggingMiddlewareSettings())
|
|
40
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<img src="https://raw.githubusercontent.com/hsteidel/voltwire/main/assets/icons/fastapi-logging.svg" alt="" width="56" height="56" align="left">
|
|
2
|
+
|
|
3
|
+
# voltwire-fastapi-logging
|
|
4
|
+
|
|
5
|
+
Logging utilities for FastAPI apps: request-context-enriching middleware.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install voltwire-fastapi-logging
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from voltwire.fastapi.logging import setup_logging, DefaultLogSettings
|
|
17
|
+
|
|
18
|
+
setup_logging(DefaultLogSettings(enable_json=False, level="INFO"))
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Add `LoggingContextMiddleware` to enrich every log entry within a request with a correlation
|
|
22
|
+
ID, method, and path:
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from voltwire.fastapi.logging import LoggingContextMiddleware, DefaultLoggingMiddlewareSettings
|
|
26
|
+
|
|
27
|
+
app.add_middleware(LoggingContextMiddleware, settings=DefaultLoggingMiddlewareSettings())
|
|
28
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "voltwire-fastapi-logging"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
description = "Logging utilities for FastAPI apps"
|
|
5
|
+
authors = [{name = "Hermann Steidel", email = "hsteidel.software@gmail.com"}]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
requires-python = ">=3.13,<4.0"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"pydantic>=2.0.0",
|
|
11
|
+
"loguru>=0.7.0",
|
|
12
|
+
"starlette>=0.27.0",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[dependency-groups]
|
|
16
|
+
dev = ["pytest>=8.0"]
|
|
17
|
+
|
|
18
|
+
[build-system]
|
|
19
|
+
requires = ["hatchling"]
|
|
20
|
+
build-backend = "hatchling.build"
|
|
21
|
+
|
|
22
|
+
[tool.hatch.build.targets.wheel]
|
|
23
|
+
packages = ["src/voltwire"]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
__version__ = "0.0.0"
|
|
2
|
+
|
|
3
|
+
from typing import Any, Protocol
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
from voltwire.fastapi.logging._constants import log_context
|
|
8
|
+
from voltwire.fastapi.logging.middleware import ContextEnricher
|
|
9
|
+
|
|
10
|
+
__all__ = ["ContextEnricher", "log_context", "integration_logger", "log_banner", "IntegrationLogContext", "AppRuntimeSettings"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class IntegrationLogContext(BaseModel):
|
|
14
|
+
integration: str
|
|
15
|
+
message: str
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AppRuntimeSettings(Protocol):
|
|
19
|
+
app_name: str
|
|
20
|
+
version: str
|
|
21
|
+
environment: str
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def log_banner(settings: AppRuntimeSettings):
|
|
25
|
+
print(
|
|
26
|
+
f"Application: '{settings.app_name}' Version: '{settings.version}' Environment: '{settings.environment}'",
|
|
27
|
+
flush=True,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def integration_logger(context: IntegrationLogContext, **extra: Any):
|
|
32
|
+
from loguru import logger
|
|
33
|
+
|
|
34
|
+
context_dict = {**context.model_dump(), **extra}
|
|
35
|
+
return logger.contextualize(**context_dict)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from loguru import logger
|
|
4
|
+
|
|
5
|
+
APP_LOG_FORMAT = (
|
|
6
|
+
"<level>{level}</level>: <green>{time:YYYY-MM-DDTHH:mm:s:SSS!UTC}</green> "
|
|
7
|
+
"| {process}:{thread} | {name}:{line} - {message} | {extra}"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
def log_context(**extra: Any):
|
|
11
|
+
return logger.contextualize(**extra)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Protocol
|
|
5
|
+
|
|
6
|
+
from loguru import logger
|
|
7
|
+
|
|
8
|
+
from voltwire.fastapi.logging._constants import APP_LOG_FORMAT
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LogSettings(Protocol):
|
|
12
|
+
level: str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class DefaultLogSettings:
|
|
17
|
+
level: str = "INFO"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Loggers known to attach their own handlers directly (bypassing propagation
|
|
21
|
+
# to root) rather than relying on the standard logging tree.
|
|
22
|
+
_DIRECTLY_HANDLED_LOGGERS = (
|
|
23
|
+
"uvicorn",
|
|
24
|
+
"uvicorn.access",
|
|
25
|
+
"uvicorn.error",
|
|
26
|
+
"gunicorn",
|
|
27
|
+
"gunicorn.access",
|
|
28
|
+
"gunicorn.error",
|
|
29
|
+
"sqlalchemy",
|
|
30
|
+
"sqlalchemy.engine",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class InterceptHandler(logging.Handler):
|
|
35
|
+
"""Forwards stdlib `logging` records into loguru.
|
|
36
|
+
|
|
37
|
+
Installed on the root logger so any code using plain
|
|
38
|
+
`logging.getLogger(__name__)` (this codebase, other voltwire
|
|
39
|
+
packages, or third-party dependencies) is routed through loguru
|
|
40
|
+
without those callers needing to know loguru exists.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def emit(self, record: logging.LogRecord) -> None:
|
|
44
|
+
try:
|
|
45
|
+
level = logger.level(record.levelname).name
|
|
46
|
+
except ValueError:
|
|
47
|
+
level = record.levelno
|
|
48
|
+
|
|
49
|
+
frame, depth = logging.currentframe(), 0
|
|
50
|
+
while frame and (depth == 0 or frame.f_code.co_filename == logging.__file__):
|
|
51
|
+
frame = frame.f_back
|
|
52
|
+
depth += 1
|
|
53
|
+
|
|
54
|
+
logger.opt(depth=depth, exception=record.exc_info).log(
|
|
55
|
+
level, record.getMessage()
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def setup_logging(settings: LogSettings):
|
|
60
|
+
|
|
61
|
+
# ensure all python and loguru loggers are clean
|
|
62
|
+
logger.remove()
|
|
63
|
+
root = logging.getLogger()
|
|
64
|
+
for handler in root.handlers[:]:
|
|
65
|
+
root.removeHandler(handler)
|
|
66
|
+
|
|
67
|
+
logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True)
|
|
68
|
+
|
|
69
|
+
# some libraries attach handlers directly to their own named logger
|
|
70
|
+
# instead of propagating to root - strip those so they route through
|
|
71
|
+
# the intercept handler too.
|
|
72
|
+
for name in _DIRECTLY_HANDLED_LOGGERS:
|
|
73
|
+
named_logger = logging.getLogger(name)
|
|
74
|
+
named_logger.handlers = []
|
|
75
|
+
named_logger.propagate = True
|
|
76
|
+
|
|
77
|
+
logger.add(
|
|
78
|
+
sys.stdout,
|
|
79
|
+
format=APP_LOG_FORMAT,
|
|
80
|
+
level=settings.level,
|
|
81
|
+
)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Protocol, Callable, Awaitable, Any
|
|
3
|
+
|
|
4
|
+
from loguru import logger
|
|
5
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
6
|
+
from starlette.requests import Request
|
|
7
|
+
|
|
8
|
+
from voltwire.fastapi.logging._constants import log_context
|
|
9
|
+
|
|
10
|
+
ContextEnricher = Callable[[Request, dict[str, Any]], Awaitable[None]]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LoggingMiddlewareSettings(Protocol):
|
|
14
|
+
context_enrichers: list[ContextEnricher]
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class DefaultLoggingMiddlewareSettings:
|
|
18
|
+
context_enrichers: list[ContextEnricher] = field(default_factory=list)
|
|
19
|
+
|
|
20
|
+
class LoggingContextMiddleware(BaseHTTPMiddleware):
|
|
21
|
+
"""
|
|
22
|
+
This middleware enriches every log entry with request context.
|
|
23
|
+
- Adds method and path to all logs within the request scope
|
|
24
|
+
- Executes custom context enrichers to add additional context
|
|
25
|
+
|
|
26
|
+
Custom context enrichers can be registered via settings:
|
|
27
|
+
async def custom_enricher(request: Request, context: dict[str, Any]) -> None:
|
|
28
|
+
context["custom_field"] = "custom_value"
|
|
29
|
+
|
|
30
|
+
settings = DefaultLoggingMiddlewareSettings(
|
|
31
|
+
context_enrichers=[custom_enricher]
|
|
32
|
+
)
|
|
33
|
+
app.add_middleware(LoggingContextMiddleware, settings=settings)
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, app, settings: LoggingMiddlewareSettings):
|
|
37
|
+
super().__init__(app)
|
|
38
|
+
self.settings = settings
|
|
39
|
+
|
|
40
|
+
async def dispatch(self, request: Request, call_next):
|
|
41
|
+
request_context = {"method": request.method, "path": request.url.path}
|
|
42
|
+
|
|
43
|
+
# Execute custom context enrichers
|
|
44
|
+
for enricher in self.settings.context_enrichers:
|
|
45
|
+
try:
|
|
46
|
+
await enricher(request, request_context)
|
|
47
|
+
except Exception as e:
|
|
48
|
+
logger.warning(f"Context enricher {enricher.__name__} failed: {str(e)}")
|
|
49
|
+
|
|
50
|
+
# Process the request within loguru context
|
|
51
|
+
with log_context(**request_context):
|
|
52
|
+
logger.trace(f"Request started: {request.method} {request.url.path}")
|
|
53
|
+
response = await call_next(request)
|
|
54
|
+
logger.trace(f"Request completed: {request.method} {request.url.path} - {response.status_code}")
|
|
55
|
+
return response
|