beans-logging 5.0.0__tar.gz → 6.0.0__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.
- {beans_logging-5.0.0 → beans_logging-6.0.0}/PKG-INFO +9 -152
- {beans_logging-5.0.0 → beans_logging-6.0.0}/README.md +5 -150
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging/__version__.py +1 -1
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging.egg-info/PKG-INFO +9 -152
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging.egg-info/SOURCES.txt +0 -5
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging.egg-info/requires.txt +3 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/setup.py +1 -0
- beans_logging-5.0.0/beans_logging/fastapi/__init__.py +0 -6
- beans_logging-5.0.0/beans_logging/fastapi/_filters.py +0 -22
- beans_logging-5.0.0/beans_logging/fastapi/_formats.py +0 -62
- beans_logging-5.0.0/beans_logging/fastapi/_handlers.py +0 -79
- beans_logging-5.0.0/beans_logging/fastapi/_middlewares.py +0 -262
- {beans_logging-5.0.0 → beans_logging-6.0.0}/LICENSE.txt +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging/__init__.py +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging/_base.py +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging/_consts.py +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging/_handlers.py +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging/_utils.py +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging/auto.py +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging/filters.py +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging/formats.py +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging/rotation.py +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging/schemas.py +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging/sinks.py +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging.egg-info/dependency_links.txt +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/beans_logging.egg-info/top_level.txt +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/setup.cfg +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/tests/__init__.py +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/tests/conftest.py +0 -0
- {beans_logging-5.0.0 → beans_logging-6.0.0}/tests/test_beans_logging.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: beans_logging
|
|
3
|
-
Version:
|
|
3
|
+
Version: 6.0.0
|
|
4
4
|
Summary: 'beans_logging' is a python package for simple logger and easily managing logging modules. It is a Loguru based custom logging package for python projects.
|
|
5
5
|
Home-page: https://github.com/bybatkhuu/module.python-logging
|
|
6
|
-
Download-URL: https://github.com/bybatkhuu/module.python-logging/archive/
|
|
6
|
+
Download-URL: https://github.com/bybatkhuu/module.python-logging/archive/v6.0.0.tar.gz
|
|
7
7
|
Author: Batkhuu Byambajav
|
|
8
8
|
Author-email: batkhuu10@gmail.com
|
|
9
9
|
License: MIT
|
|
@@ -22,6 +22,8 @@ License-File: LICENSE.txt
|
|
|
22
22
|
Requires-Dist: PyYAML<7.0,>=6.0
|
|
23
23
|
Requires-Dist: pydantic!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.10.0
|
|
24
24
|
Requires-Dist: loguru<1.0.0,>=0.7.2
|
|
25
|
+
Provides-Extra: fastapi
|
|
26
|
+
Requires-Dist: beans-logging-fastapi<2.0.0,>=1.0.0; extra == "fastapi"
|
|
25
27
|
|
|
26
28
|
# beans_logging
|
|
27
29
|
|
|
@@ -47,7 +49,6 @@ It is a `Loguru` based custom logging package for python projects.
|
|
|
47
49
|
- Custom logging **formats**
|
|
48
50
|
- **Multiprocess** compatibility (Linux, macOS - 'fork')
|
|
49
51
|
- Add custom **handlers**
|
|
50
|
-
- **FastAPI** HTTP access logging **middleware**
|
|
51
52
|
- **Base** logging module
|
|
52
53
|
- Support **Pydantic-v1** and **Pydantic-v2**
|
|
53
54
|
|
|
@@ -236,157 +237,12 @@ Traceback (most recent call last):
|
|
|
236
237
|
ZeroDivisionError: division by zero
|
|
237
238
|
```
|
|
238
239
|
|
|
239
|
-
### **
|
|
240
|
+
### **FastAPI**
|
|
240
241
|
|
|
241
|
-
|
|
242
|
+
Checkout `beans_logging_fastapi` package: <https://github.com/bybatkhuu/module.fastapi-logging>
|
|
242
243
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
app_name: "fastapi-app"
|
|
246
|
-
level: "TRACE"
|
|
247
|
-
use_diagnose: false
|
|
248
|
-
stream:
|
|
249
|
-
use_color: true
|
|
250
|
-
use_icon: false
|
|
251
|
-
format_str: "[<c>{time:YYYY-MM-DD HH:mm:ss.SSS Z}</c> | <level>{level_short:<5}</level> | <w>{name}:{line}</w>]: <level>{message}</level>"
|
|
252
|
-
std_handler:
|
|
253
|
-
enabled: true
|
|
254
|
-
file:
|
|
255
|
-
logs_dir: "./logs"
|
|
256
|
-
rotate_size: 10000000 # 10MB
|
|
257
|
-
rotate_time: "00:00:00"
|
|
258
|
-
backup_count: 90
|
|
259
|
-
log_handlers:
|
|
260
|
-
enabled: true
|
|
261
|
-
format_str: "[{time:YYYY-MM-DD HH:mm:ss.SSS Z} | {level_short:<5} | {name}:{line}]: {message}"
|
|
262
|
-
log_path: "{app_name}.std.all.log"
|
|
263
|
-
err_path: "{app_name}.std.err.log"
|
|
264
|
-
json_handlers:
|
|
265
|
-
enabled: true
|
|
266
|
-
use_custom: false
|
|
267
|
-
log_path: "json/{app_name}.json.all.log"
|
|
268
|
-
err_path: "json/{app_name}.json.err.log"
|
|
269
|
-
intercept:
|
|
270
|
-
auto_load:
|
|
271
|
-
enabled: true
|
|
272
|
-
only_base: false
|
|
273
|
-
ignore_modules: []
|
|
274
|
-
include_modules: []
|
|
275
|
-
mute_modules: ["uvicorn.access", "uvicorn.error"]
|
|
276
|
-
extra:
|
|
277
|
-
http_std_debug_format: '<n>[{request_id}]</n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}"'
|
|
278
|
-
http_std_msg_format: '<n><w>[{request_id}]</w></n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}" {status_code} {content_length}B {response_time}ms'
|
|
279
|
-
http_file_enabled: true
|
|
280
|
-
http_log_path: "http/{app_name}.http.access.log"
|
|
281
|
-
http_err_path: "http/{app_name}.http.err.log"
|
|
282
|
-
http_json_enabled: true
|
|
283
|
-
http_json_path: "json.http/{app_name}.json.http.access.log"
|
|
284
|
-
http_json_err_path: "json.http/{app_name}.json.http.err.log"
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
[**`.env`**](https://github.com/bybatkhuu/module.python-logging/blob/main/examples/advanced/.env):
|
|
288
|
-
|
|
289
|
-
```sh
|
|
290
|
-
ENV=development
|
|
291
|
-
DEBUG=true
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
[**`logger.py`**](https://github.com/bybatkhuu/module.python-logging/blob/main/examples/advanced/logger.py):
|
|
295
|
-
|
|
296
|
-
```python
|
|
297
|
-
from beans_logging import Logger, LoggerLoader
|
|
298
|
-
from beans_logging.fastapi import add_http_file_handler, add_http_file_json_handler
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
logger_loader = LoggerLoader()
|
|
302
|
-
logger: Logger = logger_loader.load()
|
|
303
|
-
|
|
304
|
-
if logger_loader.config.extra.http_file_enabled:
|
|
305
|
-
add_http_file_handler(
|
|
306
|
-
logger_loader=logger_loader,
|
|
307
|
-
log_path=logger_loader.config.extra.http_log_path,
|
|
308
|
-
err_path=logger_loader.config.extra.http_err_path,
|
|
309
|
-
)
|
|
310
|
-
|
|
311
|
-
if logger_loader.config.extra.http_json_enabled:
|
|
312
|
-
add_http_file_json_handler(
|
|
313
|
-
logger_loader=logger_loader,
|
|
314
|
-
log_path=logger_loader.config.extra.http_json_path,
|
|
315
|
-
err_path=logger_loader.config.extra.http_json_err_path,
|
|
316
|
-
)
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
[**`main.py`**](https://github.com/bybatkhuu/module.python-logging/blob/main/examples/advanced/main.py):
|
|
320
|
-
|
|
321
|
-
```python
|
|
322
|
-
from typing import Union
|
|
323
|
-
from contextlib import asynccontextmanager
|
|
324
|
-
|
|
325
|
-
from dotenv import load_dotenv
|
|
326
|
-
from fastapi import FastAPI, HTTPException
|
|
327
|
-
from fastapi.responses import RedirectResponse
|
|
328
|
-
|
|
329
|
-
load_dotenv()
|
|
330
|
-
|
|
331
|
-
from beans_logging.fastapi import HttpAccessLogMiddleware
|
|
332
|
-
|
|
333
|
-
from logger import logger, logger_loader
|
|
334
|
-
from __version__ import __version__
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
@asynccontextmanager
|
|
338
|
-
async def lifespan(app: FastAPI):
|
|
339
|
-
logger.info("Preparing to startup...")
|
|
340
|
-
logger.success("Finished preparation to startup.")
|
|
341
|
-
logger.info(f"API version: {__version__}")
|
|
342
|
-
|
|
343
|
-
yield
|
|
344
|
-
logger.info("Praparing to shutdown...")
|
|
345
|
-
logger.success("Finished preparation to shutdown.")
|
|
346
|
-
|
|
347
|
-
app = FastAPI(lifespan=lifespan, version=__version__)
|
|
348
|
-
app.add_middleware(
|
|
349
|
-
HttpAccessLogMiddleware,
|
|
350
|
-
has_proxy_headers=True,
|
|
351
|
-
debug_format=logger_loader.config.extra.http_std_debug_format,
|
|
352
|
-
msg_format=logger_loader.config.extra.http_std_msg_format,
|
|
353
|
-
)
|
|
354
|
-
|
|
355
|
-
@app.get("/")
|
|
356
|
-
def root():
|
|
357
|
-
return {"Hello": "World"}
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
Run the [**`examples/advanced`**](https://github.com/bybatkhuu/module.python-logging/tree/main/examples/advanced):
|
|
361
|
-
|
|
362
|
-
```sh
|
|
363
|
-
cd ./examples/advanced
|
|
364
|
-
# Install python dependencies for examples:
|
|
365
|
-
pip install -r ./requirements.txt
|
|
366
|
-
|
|
367
|
-
uvicorn main:app --host=0.0.0.0 --port=8000
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
**Output**:
|
|
371
|
-
|
|
372
|
-
```txt
|
|
373
|
-
[2023-09-01 00:00:00.000 +09:00 | TRACE | beans_logging._base:576]: Intercepted modules: ['watchfiles.watcher', 'dotenv', 'asyncio', 'dotenv.main', 'watchfiles.main', 'concurrent.futures', 'uvicorn', 'fastapi', 'concurrent', 'watchfiles']; Muted modules: ['uvicorn.access', 'uvicorn.error'];
|
|
374
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.server:76]: Started server process [17146]
|
|
375
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.lifespan.on:46]: Waiting for application startup.
|
|
376
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | main:21]: Preparing to startup...
|
|
377
|
-
[2023-09-01 00:00:00.000 +09:00 | OK | main:22]: Finished preparation to startup.
|
|
378
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | main:23]: API version: 0.0.1-000000
|
|
379
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.lifespan.on:60]: Application startup complete.
|
|
380
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.server:218]: Uvicorn running on http://0.0.0.0:9000 (Press CTRL+C to quit)
|
|
381
|
-
[2023-09-01 00:00:00.000 +09:00 | DEBUG | anyio._backends._asyncio:833]: [f635ebbc3f2348db9dcff681be1bd52a] 127.0.0.1 - "GET / HTTP/1.1"
|
|
382
|
-
[2023-09-01 00:00:00.000 +09:00 | OK | anyio._backends._asyncio:833]: [f635ebbc3f2348db9dcff681be1bd52a] 127.0.0.1 - "GET / HTTP/1.1" 200 17B 0.7ms
|
|
383
|
-
^C[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.server:264]: Shutting down
|
|
384
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.lifespan.on:65]: Waiting for application shutdown.
|
|
385
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | main:26]: Praparing to shutdown...
|
|
386
|
-
[2023-09-01 00:00:00.000 +09:00 | OK | main:27]: Finished preparation to shutdown.
|
|
387
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.lifespan.on:76]: Application shutdown complete.
|
|
388
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.server:86]: Finished server process [17146]
|
|
389
|
-
```
|
|
244
|
+
- FastAPI HTTP access logging middleware
|
|
245
|
+
- Install with pip: `pip install -U beans-logging[fastapi]` or `pip install -U beans-logging-fastapi`
|
|
390
246
|
|
|
391
247
|
---
|
|
392
248
|
|
|
@@ -467,3 +323,4 @@ logger:
|
|
|
467
323
|
- <https://github.com/Delgan/loguru>
|
|
468
324
|
- <https://loguru.readthedocs.io/en/stable/api/logger.html>
|
|
469
325
|
- <https://loguru.readthedocs.io/en/stable/resources/recipes.html>
|
|
326
|
+
- <https://github.com/bybatkhuu/module.fastapi-logging>
|
|
@@ -22,7 +22,6 @@ It is a `Loguru` based custom logging package for python projects.
|
|
|
22
22
|
- Custom logging **formats**
|
|
23
23
|
- **Multiprocess** compatibility (Linux, macOS - 'fork')
|
|
24
24
|
- Add custom **handlers**
|
|
25
|
-
- **FastAPI** HTTP access logging **middleware**
|
|
26
25
|
- **Base** logging module
|
|
27
26
|
- Support **Pydantic-v1** and **Pydantic-v2**
|
|
28
27
|
|
|
@@ -211,157 +210,12 @@ Traceback (most recent call last):
|
|
|
211
210
|
ZeroDivisionError: division by zero
|
|
212
211
|
```
|
|
213
212
|
|
|
214
|
-
### **
|
|
213
|
+
### **FastAPI**
|
|
215
214
|
|
|
216
|
-
|
|
215
|
+
Checkout `beans_logging_fastapi` package: <https://github.com/bybatkhuu/module.fastapi-logging>
|
|
217
216
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
app_name: "fastapi-app"
|
|
221
|
-
level: "TRACE"
|
|
222
|
-
use_diagnose: false
|
|
223
|
-
stream:
|
|
224
|
-
use_color: true
|
|
225
|
-
use_icon: false
|
|
226
|
-
format_str: "[<c>{time:YYYY-MM-DD HH:mm:ss.SSS Z}</c> | <level>{level_short:<5}</level> | <w>{name}:{line}</w>]: <level>{message}</level>"
|
|
227
|
-
std_handler:
|
|
228
|
-
enabled: true
|
|
229
|
-
file:
|
|
230
|
-
logs_dir: "./logs"
|
|
231
|
-
rotate_size: 10000000 # 10MB
|
|
232
|
-
rotate_time: "00:00:00"
|
|
233
|
-
backup_count: 90
|
|
234
|
-
log_handlers:
|
|
235
|
-
enabled: true
|
|
236
|
-
format_str: "[{time:YYYY-MM-DD HH:mm:ss.SSS Z} | {level_short:<5} | {name}:{line}]: {message}"
|
|
237
|
-
log_path: "{app_name}.std.all.log"
|
|
238
|
-
err_path: "{app_name}.std.err.log"
|
|
239
|
-
json_handlers:
|
|
240
|
-
enabled: true
|
|
241
|
-
use_custom: false
|
|
242
|
-
log_path: "json/{app_name}.json.all.log"
|
|
243
|
-
err_path: "json/{app_name}.json.err.log"
|
|
244
|
-
intercept:
|
|
245
|
-
auto_load:
|
|
246
|
-
enabled: true
|
|
247
|
-
only_base: false
|
|
248
|
-
ignore_modules: []
|
|
249
|
-
include_modules: []
|
|
250
|
-
mute_modules: ["uvicorn.access", "uvicorn.error"]
|
|
251
|
-
extra:
|
|
252
|
-
http_std_debug_format: '<n>[{request_id}]</n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}"'
|
|
253
|
-
http_std_msg_format: '<n><w>[{request_id}]</w></n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}" {status_code} {content_length}B {response_time}ms'
|
|
254
|
-
http_file_enabled: true
|
|
255
|
-
http_log_path: "http/{app_name}.http.access.log"
|
|
256
|
-
http_err_path: "http/{app_name}.http.err.log"
|
|
257
|
-
http_json_enabled: true
|
|
258
|
-
http_json_path: "json.http/{app_name}.json.http.access.log"
|
|
259
|
-
http_json_err_path: "json.http/{app_name}.json.http.err.log"
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
[**`.env`**](https://github.com/bybatkhuu/module.python-logging/blob/main/examples/advanced/.env):
|
|
263
|
-
|
|
264
|
-
```sh
|
|
265
|
-
ENV=development
|
|
266
|
-
DEBUG=true
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
[**`logger.py`**](https://github.com/bybatkhuu/module.python-logging/blob/main/examples/advanced/logger.py):
|
|
270
|
-
|
|
271
|
-
```python
|
|
272
|
-
from beans_logging import Logger, LoggerLoader
|
|
273
|
-
from beans_logging.fastapi import add_http_file_handler, add_http_file_json_handler
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
logger_loader = LoggerLoader()
|
|
277
|
-
logger: Logger = logger_loader.load()
|
|
278
|
-
|
|
279
|
-
if logger_loader.config.extra.http_file_enabled:
|
|
280
|
-
add_http_file_handler(
|
|
281
|
-
logger_loader=logger_loader,
|
|
282
|
-
log_path=logger_loader.config.extra.http_log_path,
|
|
283
|
-
err_path=logger_loader.config.extra.http_err_path,
|
|
284
|
-
)
|
|
285
|
-
|
|
286
|
-
if logger_loader.config.extra.http_json_enabled:
|
|
287
|
-
add_http_file_json_handler(
|
|
288
|
-
logger_loader=logger_loader,
|
|
289
|
-
log_path=logger_loader.config.extra.http_json_path,
|
|
290
|
-
err_path=logger_loader.config.extra.http_json_err_path,
|
|
291
|
-
)
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
[**`main.py`**](https://github.com/bybatkhuu/module.python-logging/blob/main/examples/advanced/main.py):
|
|
295
|
-
|
|
296
|
-
```python
|
|
297
|
-
from typing import Union
|
|
298
|
-
from contextlib import asynccontextmanager
|
|
299
|
-
|
|
300
|
-
from dotenv import load_dotenv
|
|
301
|
-
from fastapi import FastAPI, HTTPException
|
|
302
|
-
from fastapi.responses import RedirectResponse
|
|
303
|
-
|
|
304
|
-
load_dotenv()
|
|
305
|
-
|
|
306
|
-
from beans_logging.fastapi import HttpAccessLogMiddleware
|
|
307
|
-
|
|
308
|
-
from logger import logger, logger_loader
|
|
309
|
-
from __version__ import __version__
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
@asynccontextmanager
|
|
313
|
-
async def lifespan(app: FastAPI):
|
|
314
|
-
logger.info("Preparing to startup...")
|
|
315
|
-
logger.success("Finished preparation to startup.")
|
|
316
|
-
logger.info(f"API version: {__version__}")
|
|
317
|
-
|
|
318
|
-
yield
|
|
319
|
-
logger.info("Praparing to shutdown...")
|
|
320
|
-
logger.success("Finished preparation to shutdown.")
|
|
321
|
-
|
|
322
|
-
app = FastAPI(lifespan=lifespan, version=__version__)
|
|
323
|
-
app.add_middleware(
|
|
324
|
-
HttpAccessLogMiddleware,
|
|
325
|
-
has_proxy_headers=True,
|
|
326
|
-
debug_format=logger_loader.config.extra.http_std_debug_format,
|
|
327
|
-
msg_format=logger_loader.config.extra.http_std_msg_format,
|
|
328
|
-
)
|
|
329
|
-
|
|
330
|
-
@app.get("/")
|
|
331
|
-
def root():
|
|
332
|
-
return {"Hello": "World"}
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
Run the [**`examples/advanced`**](https://github.com/bybatkhuu/module.python-logging/tree/main/examples/advanced):
|
|
336
|
-
|
|
337
|
-
```sh
|
|
338
|
-
cd ./examples/advanced
|
|
339
|
-
# Install python dependencies for examples:
|
|
340
|
-
pip install -r ./requirements.txt
|
|
341
|
-
|
|
342
|
-
uvicorn main:app --host=0.0.0.0 --port=8000
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
**Output**:
|
|
346
|
-
|
|
347
|
-
```txt
|
|
348
|
-
[2023-09-01 00:00:00.000 +09:00 | TRACE | beans_logging._base:576]: Intercepted modules: ['watchfiles.watcher', 'dotenv', 'asyncio', 'dotenv.main', 'watchfiles.main', 'concurrent.futures', 'uvicorn', 'fastapi', 'concurrent', 'watchfiles']; Muted modules: ['uvicorn.access', 'uvicorn.error'];
|
|
349
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.server:76]: Started server process [17146]
|
|
350
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.lifespan.on:46]: Waiting for application startup.
|
|
351
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | main:21]: Preparing to startup...
|
|
352
|
-
[2023-09-01 00:00:00.000 +09:00 | OK | main:22]: Finished preparation to startup.
|
|
353
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | main:23]: API version: 0.0.1-000000
|
|
354
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.lifespan.on:60]: Application startup complete.
|
|
355
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.server:218]: Uvicorn running on http://0.0.0.0:9000 (Press CTRL+C to quit)
|
|
356
|
-
[2023-09-01 00:00:00.000 +09:00 | DEBUG | anyio._backends._asyncio:833]: [f635ebbc3f2348db9dcff681be1bd52a] 127.0.0.1 - "GET / HTTP/1.1"
|
|
357
|
-
[2023-09-01 00:00:00.000 +09:00 | OK | anyio._backends._asyncio:833]: [f635ebbc3f2348db9dcff681be1bd52a] 127.0.0.1 - "GET / HTTP/1.1" 200 17B 0.7ms
|
|
358
|
-
^C[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.server:264]: Shutting down
|
|
359
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.lifespan.on:65]: Waiting for application shutdown.
|
|
360
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | main:26]: Praparing to shutdown...
|
|
361
|
-
[2023-09-01 00:00:00.000 +09:00 | OK | main:27]: Finished preparation to shutdown.
|
|
362
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.lifespan.on:76]: Application shutdown complete.
|
|
363
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.server:86]: Finished server process [17146]
|
|
364
|
-
```
|
|
217
|
+
- FastAPI HTTP access logging middleware
|
|
218
|
+
- Install with pip: `pip install -U beans-logging[fastapi]` or `pip install -U beans-logging-fastapi`
|
|
365
219
|
|
|
366
220
|
---
|
|
367
221
|
|
|
@@ -442,3 +296,4 @@ logger:
|
|
|
442
296
|
- <https://github.com/Delgan/loguru>
|
|
443
297
|
- <https://loguru.readthedocs.io/en/stable/api/logger.html>
|
|
444
298
|
- <https://loguru.readthedocs.io/en/stable/resources/recipes.html>
|
|
299
|
+
- <https://github.com/bybatkhuu/module.fastapi-logging>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: beans-logging
|
|
3
|
-
Version:
|
|
3
|
+
Version: 6.0.0
|
|
4
4
|
Summary: 'beans_logging' is a python package for simple logger and easily managing logging modules. It is a Loguru based custom logging package for python projects.
|
|
5
5
|
Home-page: https://github.com/bybatkhuu/module.python-logging
|
|
6
|
-
Download-URL: https://github.com/bybatkhuu/module.python-logging/archive/
|
|
6
|
+
Download-URL: https://github.com/bybatkhuu/module.python-logging/archive/v6.0.0.tar.gz
|
|
7
7
|
Author: Batkhuu Byambajav
|
|
8
8
|
Author-email: batkhuu10@gmail.com
|
|
9
9
|
License: MIT
|
|
@@ -22,6 +22,8 @@ License-File: LICENSE.txt
|
|
|
22
22
|
Requires-Dist: PyYAML<7.0,>=6.0
|
|
23
23
|
Requires-Dist: pydantic!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.10.0
|
|
24
24
|
Requires-Dist: loguru<1.0.0,>=0.7.2
|
|
25
|
+
Provides-Extra: fastapi
|
|
26
|
+
Requires-Dist: beans-logging-fastapi<2.0.0,>=1.0.0; extra == "fastapi"
|
|
25
27
|
|
|
26
28
|
# beans_logging
|
|
27
29
|
|
|
@@ -47,7 +49,6 @@ It is a `Loguru` based custom logging package for python projects.
|
|
|
47
49
|
- Custom logging **formats**
|
|
48
50
|
- **Multiprocess** compatibility (Linux, macOS - 'fork')
|
|
49
51
|
- Add custom **handlers**
|
|
50
|
-
- **FastAPI** HTTP access logging **middleware**
|
|
51
52
|
- **Base** logging module
|
|
52
53
|
- Support **Pydantic-v1** and **Pydantic-v2**
|
|
53
54
|
|
|
@@ -236,157 +237,12 @@ Traceback (most recent call last):
|
|
|
236
237
|
ZeroDivisionError: division by zero
|
|
237
238
|
```
|
|
238
239
|
|
|
239
|
-
### **
|
|
240
|
+
### **FastAPI**
|
|
240
241
|
|
|
241
|
-
|
|
242
|
+
Checkout `beans_logging_fastapi` package: <https://github.com/bybatkhuu/module.fastapi-logging>
|
|
242
243
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
app_name: "fastapi-app"
|
|
246
|
-
level: "TRACE"
|
|
247
|
-
use_diagnose: false
|
|
248
|
-
stream:
|
|
249
|
-
use_color: true
|
|
250
|
-
use_icon: false
|
|
251
|
-
format_str: "[<c>{time:YYYY-MM-DD HH:mm:ss.SSS Z}</c> | <level>{level_short:<5}</level> | <w>{name}:{line}</w>]: <level>{message}</level>"
|
|
252
|
-
std_handler:
|
|
253
|
-
enabled: true
|
|
254
|
-
file:
|
|
255
|
-
logs_dir: "./logs"
|
|
256
|
-
rotate_size: 10000000 # 10MB
|
|
257
|
-
rotate_time: "00:00:00"
|
|
258
|
-
backup_count: 90
|
|
259
|
-
log_handlers:
|
|
260
|
-
enabled: true
|
|
261
|
-
format_str: "[{time:YYYY-MM-DD HH:mm:ss.SSS Z} | {level_short:<5} | {name}:{line}]: {message}"
|
|
262
|
-
log_path: "{app_name}.std.all.log"
|
|
263
|
-
err_path: "{app_name}.std.err.log"
|
|
264
|
-
json_handlers:
|
|
265
|
-
enabled: true
|
|
266
|
-
use_custom: false
|
|
267
|
-
log_path: "json/{app_name}.json.all.log"
|
|
268
|
-
err_path: "json/{app_name}.json.err.log"
|
|
269
|
-
intercept:
|
|
270
|
-
auto_load:
|
|
271
|
-
enabled: true
|
|
272
|
-
only_base: false
|
|
273
|
-
ignore_modules: []
|
|
274
|
-
include_modules: []
|
|
275
|
-
mute_modules: ["uvicorn.access", "uvicorn.error"]
|
|
276
|
-
extra:
|
|
277
|
-
http_std_debug_format: '<n>[{request_id}]</n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}"'
|
|
278
|
-
http_std_msg_format: '<n><w>[{request_id}]</w></n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}" {status_code} {content_length}B {response_time}ms'
|
|
279
|
-
http_file_enabled: true
|
|
280
|
-
http_log_path: "http/{app_name}.http.access.log"
|
|
281
|
-
http_err_path: "http/{app_name}.http.err.log"
|
|
282
|
-
http_json_enabled: true
|
|
283
|
-
http_json_path: "json.http/{app_name}.json.http.access.log"
|
|
284
|
-
http_json_err_path: "json.http/{app_name}.json.http.err.log"
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
[**`.env`**](https://github.com/bybatkhuu/module.python-logging/blob/main/examples/advanced/.env):
|
|
288
|
-
|
|
289
|
-
```sh
|
|
290
|
-
ENV=development
|
|
291
|
-
DEBUG=true
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
[**`logger.py`**](https://github.com/bybatkhuu/module.python-logging/blob/main/examples/advanced/logger.py):
|
|
295
|
-
|
|
296
|
-
```python
|
|
297
|
-
from beans_logging import Logger, LoggerLoader
|
|
298
|
-
from beans_logging.fastapi import add_http_file_handler, add_http_file_json_handler
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
logger_loader = LoggerLoader()
|
|
302
|
-
logger: Logger = logger_loader.load()
|
|
303
|
-
|
|
304
|
-
if logger_loader.config.extra.http_file_enabled:
|
|
305
|
-
add_http_file_handler(
|
|
306
|
-
logger_loader=logger_loader,
|
|
307
|
-
log_path=logger_loader.config.extra.http_log_path,
|
|
308
|
-
err_path=logger_loader.config.extra.http_err_path,
|
|
309
|
-
)
|
|
310
|
-
|
|
311
|
-
if logger_loader.config.extra.http_json_enabled:
|
|
312
|
-
add_http_file_json_handler(
|
|
313
|
-
logger_loader=logger_loader,
|
|
314
|
-
log_path=logger_loader.config.extra.http_json_path,
|
|
315
|
-
err_path=logger_loader.config.extra.http_json_err_path,
|
|
316
|
-
)
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
[**`main.py`**](https://github.com/bybatkhuu/module.python-logging/blob/main/examples/advanced/main.py):
|
|
320
|
-
|
|
321
|
-
```python
|
|
322
|
-
from typing import Union
|
|
323
|
-
from contextlib import asynccontextmanager
|
|
324
|
-
|
|
325
|
-
from dotenv import load_dotenv
|
|
326
|
-
from fastapi import FastAPI, HTTPException
|
|
327
|
-
from fastapi.responses import RedirectResponse
|
|
328
|
-
|
|
329
|
-
load_dotenv()
|
|
330
|
-
|
|
331
|
-
from beans_logging.fastapi import HttpAccessLogMiddleware
|
|
332
|
-
|
|
333
|
-
from logger import logger, logger_loader
|
|
334
|
-
from __version__ import __version__
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
@asynccontextmanager
|
|
338
|
-
async def lifespan(app: FastAPI):
|
|
339
|
-
logger.info("Preparing to startup...")
|
|
340
|
-
logger.success("Finished preparation to startup.")
|
|
341
|
-
logger.info(f"API version: {__version__}")
|
|
342
|
-
|
|
343
|
-
yield
|
|
344
|
-
logger.info("Praparing to shutdown...")
|
|
345
|
-
logger.success("Finished preparation to shutdown.")
|
|
346
|
-
|
|
347
|
-
app = FastAPI(lifespan=lifespan, version=__version__)
|
|
348
|
-
app.add_middleware(
|
|
349
|
-
HttpAccessLogMiddleware,
|
|
350
|
-
has_proxy_headers=True,
|
|
351
|
-
debug_format=logger_loader.config.extra.http_std_debug_format,
|
|
352
|
-
msg_format=logger_loader.config.extra.http_std_msg_format,
|
|
353
|
-
)
|
|
354
|
-
|
|
355
|
-
@app.get("/")
|
|
356
|
-
def root():
|
|
357
|
-
return {"Hello": "World"}
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
Run the [**`examples/advanced`**](https://github.com/bybatkhuu/module.python-logging/tree/main/examples/advanced):
|
|
361
|
-
|
|
362
|
-
```sh
|
|
363
|
-
cd ./examples/advanced
|
|
364
|
-
# Install python dependencies for examples:
|
|
365
|
-
pip install -r ./requirements.txt
|
|
366
|
-
|
|
367
|
-
uvicorn main:app --host=0.0.0.0 --port=8000
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
**Output**:
|
|
371
|
-
|
|
372
|
-
```txt
|
|
373
|
-
[2023-09-01 00:00:00.000 +09:00 | TRACE | beans_logging._base:576]: Intercepted modules: ['watchfiles.watcher', 'dotenv', 'asyncio', 'dotenv.main', 'watchfiles.main', 'concurrent.futures', 'uvicorn', 'fastapi', 'concurrent', 'watchfiles']; Muted modules: ['uvicorn.access', 'uvicorn.error'];
|
|
374
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.server:76]: Started server process [17146]
|
|
375
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.lifespan.on:46]: Waiting for application startup.
|
|
376
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | main:21]: Preparing to startup...
|
|
377
|
-
[2023-09-01 00:00:00.000 +09:00 | OK | main:22]: Finished preparation to startup.
|
|
378
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | main:23]: API version: 0.0.1-000000
|
|
379
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.lifespan.on:60]: Application startup complete.
|
|
380
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.server:218]: Uvicorn running on http://0.0.0.0:9000 (Press CTRL+C to quit)
|
|
381
|
-
[2023-09-01 00:00:00.000 +09:00 | DEBUG | anyio._backends._asyncio:833]: [f635ebbc3f2348db9dcff681be1bd52a] 127.0.0.1 - "GET / HTTP/1.1"
|
|
382
|
-
[2023-09-01 00:00:00.000 +09:00 | OK | anyio._backends._asyncio:833]: [f635ebbc3f2348db9dcff681be1bd52a] 127.0.0.1 - "GET / HTTP/1.1" 200 17B 0.7ms
|
|
383
|
-
^C[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.server:264]: Shutting down
|
|
384
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.lifespan.on:65]: Waiting for application shutdown.
|
|
385
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | main:26]: Praparing to shutdown...
|
|
386
|
-
[2023-09-01 00:00:00.000 +09:00 | OK | main:27]: Finished preparation to shutdown.
|
|
387
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.lifespan.on:76]: Application shutdown complete.
|
|
388
|
-
[2023-09-01 00:00:00.000 +09:00 | INFO | uvicorn.server:86]: Finished server process [17146]
|
|
389
|
-
```
|
|
244
|
+
- FastAPI HTTP access logging middleware
|
|
245
|
+
- Install with pip: `pip install -U beans-logging[fastapi]` or `pip install -U beans-logging-fastapi`
|
|
390
246
|
|
|
391
247
|
---
|
|
392
248
|
|
|
@@ -467,3 +323,4 @@ logger:
|
|
|
467
323
|
- <https://github.com/Delgan/loguru>
|
|
468
324
|
- <https://loguru.readthedocs.io/en/stable/api/logger.html>
|
|
469
325
|
- <https://loguru.readthedocs.io/en/stable/resources/recipes.html>
|
|
326
|
+
- <https://github.com/bybatkhuu/module.fastapi-logging>
|
|
@@ -19,11 +19,6 @@ beans_logging.egg-info/SOURCES.txt
|
|
|
19
19
|
beans_logging.egg-info/dependency_links.txt
|
|
20
20
|
beans_logging.egg-info/requires.txt
|
|
21
21
|
beans_logging.egg-info/top_level.txt
|
|
22
|
-
beans_logging/fastapi/__init__.py
|
|
23
|
-
beans_logging/fastapi/_filters.py
|
|
24
|
-
beans_logging/fastapi/_formats.py
|
|
25
|
-
beans_logging/fastapi/_handlers.py
|
|
26
|
-
beans_logging/fastapi/_middlewares.py
|
|
27
22
|
tests/__init__.py
|
|
28
23
|
tests/conftest.py
|
|
29
24
|
tests/test_beans_logging.py
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
from beans_logging.filters import use_all_filter
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def use_http_filter(record: dict) -> bool:
|
|
7
|
-
"""Filter message only for http access log handler by checking 'http_info' key in extra.
|
|
8
|
-
|
|
9
|
-
Args:
|
|
10
|
-
record (dict): Log record as dictionary.
|
|
11
|
-
|
|
12
|
-
Returns:
|
|
13
|
-
bool: True if record has 'http_info' key in extra, False otherwise.
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
if not use_all_filter(record):
|
|
17
|
-
return False
|
|
18
|
-
|
|
19
|
-
if "http_info" not in record["extra"]:
|
|
20
|
-
return False
|
|
21
|
-
|
|
22
|
-
return True
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def http_file_format(record: dict) -> str:
|
|
5
|
-
"""Http access log file format.
|
|
6
|
-
|
|
7
|
-
Args:
|
|
8
|
-
record (dict): Log record as dictionary.
|
|
9
|
-
|
|
10
|
-
Returns:
|
|
11
|
-
str: Format for http access log record.
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
_MSG_FORMAT = '{client_host} {request_id} {user_id} [{datetime}] "{method} {url_path} HTTP/{http_version}" {status_code} {content_length} "{h_referer}" "{h_user_agent}" {response_time}'
|
|
15
|
-
|
|
16
|
-
if "http_info" not in record["extra"]:
|
|
17
|
-
return ""
|
|
18
|
-
|
|
19
|
-
if "http_message" not in record:
|
|
20
|
-
_http_info = record["extra"]["http_info"]
|
|
21
|
-
if "datetime" not in _http_info:
|
|
22
|
-
_http_info["datetime"] = record["time"].isoformat()
|
|
23
|
-
|
|
24
|
-
if "content_length" not in _http_info:
|
|
25
|
-
_http_info["content_length"] = 0
|
|
26
|
-
|
|
27
|
-
if "h_referer" not in _http_info:
|
|
28
|
-
_http_info["h_referer"] = "-"
|
|
29
|
-
|
|
30
|
-
if "h_user_agent" not in _http_info:
|
|
31
|
-
_http_info["h_user_agent"] = "-"
|
|
32
|
-
|
|
33
|
-
if "response_time" not in _http_info:
|
|
34
|
-
_http_info["response_time"] = 0
|
|
35
|
-
|
|
36
|
-
record["extra"]["http_info"] = _http_info
|
|
37
|
-
|
|
38
|
-
_msg = _MSG_FORMAT.format(**_http_info)
|
|
39
|
-
record["http_message"] = _msg
|
|
40
|
-
|
|
41
|
-
return "{http_message}\n"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def http_file_json_format(record: dict) -> str:
|
|
45
|
-
"""Http access json log file format.
|
|
46
|
-
|
|
47
|
-
Args:
|
|
48
|
-
record (dict): Log record as dictionary.
|
|
49
|
-
|
|
50
|
-
Returns:
|
|
51
|
-
str: Format for http access json log record.
|
|
52
|
-
"""
|
|
53
|
-
|
|
54
|
-
if "http_info" not in record["extra"]:
|
|
55
|
-
return ""
|
|
56
|
-
|
|
57
|
-
_http_info = record["extra"]["http_info"]
|
|
58
|
-
if "datetime" not in _http_info:
|
|
59
|
-
_http_info["datetime"] = record["time"].isoformat()
|
|
60
|
-
record["extra"]["http_info"] = _http_info
|
|
61
|
-
|
|
62
|
-
return "{extra[http_info]}\n"
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
from typing import Union, Callable
|
|
4
|
-
|
|
5
|
-
import pydantic
|
|
6
|
-
|
|
7
|
-
if "2.0.0" <= pydantic.__version__:
|
|
8
|
-
from pydantic import validate_call
|
|
9
|
-
else:
|
|
10
|
-
from pydantic import validate_arguments as validate_call
|
|
11
|
-
|
|
12
|
-
from beans_logging import LoggerLoader
|
|
13
|
-
|
|
14
|
-
from ._filters import use_http_filter
|
|
15
|
-
from ._formats import http_file_format, http_file_json_format
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@validate_call(config=dict(arbitrary_types_allowed=True))
|
|
19
|
-
def add_http_file_handler(
|
|
20
|
-
logger_loader: LoggerLoader,
|
|
21
|
-
log_path: str = "http/{app_name}.http.access.log",
|
|
22
|
-
err_path: str = "http/{app_name}.http.err.log",
|
|
23
|
-
formatter: Union[Callable, str] = http_file_format,
|
|
24
|
-
):
|
|
25
|
-
"""Add http access log file and error file handler.
|
|
26
|
-
|
|
27
|
-
Args:
|
|
28
|
-
logger_loader (LoggerLoader, required): LoggerLoader instance.
|
|
29
|
-
log_path (str, optional): Log file path. Defaults to "http/{app_name}.http.access.log".
|
|
30
|
-
err_path (str, optional): Error log file path. Defaults to "http/{app_name}.http.err.log".
|
|
31
|
-
formatter (Union[Callable, str], optional): Log formatter. Defaults to `http_file_format` function.
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
-
logger_loader.add_custom_handler(
|
|
35
|
-
handler_name="FILE.HTTP",
|
|
36
|
-
sink=log_path,
|
|
37
|
-
filter=use_http_filter,
|
|
38
|
-
format=formatter,
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
logger_loader.add_custom_handler(
|
|
42
|
-
handler_name="FILE.HTTP_ERR",
|
|
43
|
-
sink=err_path,
|
|
44
|
-
level="WARNING",
|
|
45
|
-
filter=use_http_filter,
|
|
46
|
-
format=formatter,
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
@validate_call(config=dict(arbitrary_types_allowed=True))
|
|
51
|
-
def add_http_file_json_handler(
|
|
52
|
-
logger_loader: LoggerLoader,
|
|
53
|
-
log_path: str = "json.http/{app_name}.json.http.access.log",
|
|
54
|
-
err_path: str = "json.http/{app_name}.json.http.err.log",
|
|
55
|
-
formatter: Union[Callable, str] = http_file_json_format,
|
|
56
|
-
):
|
|
57
|
-
"""Add http access json log file and json error file handler.
|
|
58
|
-
|
|
59
|
-
Args:
|
|
60
|
-
logger_loader (LoggerLoader, required): LoggerLoader instance.
|
|
61
|
-
log_path (str, optional): Json log file path. Defaults to "http.json/{app_name}.json.http.access.log".
|
|
62
|
-
err_path (str, optional): Json error log file path. Defaults to "http.json/{app_name}.json.http.err.log".
|
|
63
|
-
formatter (Union[Callable, str], optional): Log formatter. Defaults to `http_file_json_format` function.
|
|
64
|
-
"""
|
|
65
|
-
|
|
66
|
-
logger_loader.add_custom_handler(
|
|
67
|
-
handler_name="FILE.JSON.HTTP",
|
|
68
|
-
sink=log_path,
|
|
69
|
-
filter=use_http_filter,
|
|
70
|
-
format=formatter,
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
logger_loader.add_custom_handler(
|
|
74
|
-
handler_name="FILE.JSON.HTTP_ERR",
|
|
75
|
-
sink=err_path,
|
|
76
|
-
level="WARNING",
|
|
77
|
-
filter=use_http_filter,
|
|
78
|
-
format=formatter,
|
|
79
|
-
)
|
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import time
|
|
5
|
-
from uuid import uuid4
|
|
6
|
-
|
|
7
|
-
from fastapi import Request, Response
|
|
8
|
-
from fastapi.concurrency import run_in_threadpool
|
|
9
|
-
from starlette.middleware.base import BaseHTTPMiddleware
|
|
10
|
-
|
|
11
|
-
from beans_logging import logger
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class HttpAccessLogMiddleware(BaseHTTPMiddleware):
|
|
15
|
-
"""Http access log middleware for FastAPI.
|
|
16
|
-
|
|
17
|
-
Inherits:
|
|
18
|
-
BaseHTTPMiddleware: Base HTTP middleware class from starlette.
|
|
19
|
-
|
|
20
|
-
Attributes:
|
|
21
|
-
_DEBUG_FORMAT (str ): Default http access log debug message format. Defaults to '<n>[{request_id}]</n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}"'.
|
|
22
|
-
_MSG_FORMAT (str ): Default http access log message format. Defaults to '<n><w>[{request_id}]</w></n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}" {status_code} {content_length}B {response_time}ms'.
|
|
23
|
-
|
|
24
|
-
has_proxy_headers (bool): If True, use proxy headers to get http request info. Defaults to False.
|
|
25
|
-
has_cf_headers (bool): If True, use cloudflare headers to get http request info. Defaults to False.
|
|
26
|
-
debug_format (str ): Http access log debug message format. Defaults to `HttpAccessLogMiddleware._DEBUG_FORMAT`.
|
|
27
|
-
msg_format (str ): Http access log message format. Defaults to `HttpAccessLogMiddleware._MSG_FORMAT`.
|
|
28
|
-
use_debug_log (bool): If True, use debug log to log http access log. Defaults to True.
|
|
29
|
-
"""
|
|
30
|
-
|
|
31
|
-
_DEBUG_FORMAT = '<n>[{request_id}]</n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}"'
|
|
32
|
-
_MSG_FORMAT = '<n><w>[{request_id}]</w></n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}" {status_code} {content_length}B {response_time}ms'
|
|
33
|
-
|
|
34
|
-
def __init__(
|
|
35
|
-
self,
|
|
36
|
-
app,
|
|
37
|
-
has_proxy_headers: bool = False,
|
|
38
|
-
has_cf_headers: bool = False,
|
|
39
|
-
debug_format: str = _DEBUG_FORMAT,
|
|
40
|
-
msg_format: str = _MSG_FORMAT,
|
|
41
|
-
use_debug_log: bool = True,
|
|
42
|
-
):
|
|
43
|
-
super().__init__(app)
|
|
44
|
-
self.has_proxy_headers = has_proxy_headers
|
|
45
|
-
self.has_cf_headers = has_cf_headers
|
|
46
|
-
self.debug_format = debug_format
|
|
47
|
-
self.msg_format = msg_format
|
|
48
|
-
self.use_debug_log = use_debug_log
|
|
49
|
-
|
|
50
|
-
async def dispatch(self, request: Request, call_next) -> Response:
|
|
51
|
-
_logger = logger.opt(colors=True, record=True)
|
|
52
|
-
|
|
53
|
-
_http_info = {}
|
|
54
|
-
_http_info["request_id"] = uuid4().hex
|
|
55
|
-
if "X-Request-ID" in request.headers:
|
|
56
|
-
_http_info["request_id"] = request.headers.get("X-Request-ID")
|
|
57
|
-
elif "X-Correlation-ID" in request.headers:
|
|
58
|
-
_http_info["request_id"] = request.headers.get("X-Correlation-ID")
|
|
59
|
-
|
|
60
|
-
## Set request_id to request state:
|
|
61
|
-
request.state.request_id = _http_info["request_id"]
|
|
62
|
-
|
|
63
|
-
_http_info["client_host"] = request.client.host
|
|
64
|
-
_http_info["request_proto"] = request.url.scheme
|
|
65
|
-
_http_info["request_host"] = (
|
|
66
|
-
request.url.hostname if request.url.hostname else ""
|
|
67
|
-
)
|
|
68
|
-
if (request.url.port != 80) and (request.url.port != 443):
|
|
69
|
-
_http_info[
|
|
70
|
-
"request_host"
|
|
71
|
-
] = f"{_http_info['request_host']}:{request.url.port}"
|
|
72
|
-
|
|
73
|
-
_http_info["request_port"] = request.url.port
|
|
74
|
-
_http_info["http_version"] = request.scope["http_version"]
|
|
75
|
-
|
|
76
|
-
if self.has_proxy_headers:
|
|
77
|
-
if "X-Real-IP" in request.headers:
|
|
78
|
-
_http_info["client_host"] = request.headers.get("X-Real-IP")
|
|
79
|
-
elif "X-Forwarded-For" in request.headers:
|
|
80
|
-
_http_info["client_host"] = request.headers.get(
|
|
81
|
-
"X-Forwarded-For"
|
|
82
|
-
).split(",")[0]
|
|
83
|
-
_http_info["h_x_forwarded_for"] = request.headers.get("X-Forwarded-For")
|
|
84
|
-
|
|
85
|
-
if "X-Forwarded-Proto" in request.headers:
|
|
86
|
-
_http_info["request_proto"] = request.headers.get("X-Forwarded-Proto")
|
|
87
|
-
|
|
88
|
-
if "X-Forwarded-Host" in request.headers:
|
|
89
|
-
_http_info["request_host"] = request.headers.get("X-Forwarded-Host")
|
|
90
|
-
elif "Host" in request.headers:
|
|
91
|
-
_http_info["request_host"] = request.headers.get("Host")
|
|
92
|
-
|
|
93
|
-
if "X-Forwarded-Port" in request.headers:
|
|
94
|
-
try:
|
|
95
|
-
_http_info["request_port"] = int(
|
|
96
|
-
request.headers.get("X-Forwarded-Port")
|
|
97
|
-
)
|
|
98
|
-
except ValueError:
|
|
99
|
-
logger.warning(
|
|
100
|
-
f"`X-Forwarded-Port` header value '{request.headers.get('X-Forwarded-Port')}' is invalid, should be parseable to <int>!"
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
if "Via" in request.headers:
|
|
104
|
-
_http_info["h_via"] = request.headers.get("Via")
|
|
105
|
-
|
|
106
|
-
if self.has_cf_headers:
|
|
107
|
-
if "CF-Connecting-IP" in request.headers:
|
|
108
|
-
_http_info["client_host"] = request.headers.get("CF-Connecting-IP")
|
|
109
|
-
_http_info["h_cf_connecting_ip"] = request.headers.get(
|
|
110
|
-
"CF-Connecting-IP"
|
|
111
|
-
)
|
|
112
|
-
elif "True-Client-IP" in request.headers:
|
|
113
|
-
_http_info["client_host"] = request.headers.get("True-Client-IP")
|
|
114
|
-
_http_info["h_true_client_ip"] = request.headers.get(
|
|
115
|
-
"True-Client-IP"
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
if "CF-IPCountry" in request.headers:
|
|
119
|
-
_http_info["client_country"] = request.headers.get("CF-IPCountry")
|
|
120
|
-
_http_info["h_cf_ipcountry"] = request.headers.get("CF-IPCountry")
|
|
121
|
-
|
|
122
|
-
if "CF-RAY" in request.headers:
|
|
123
|
-
_http_info["h_cf_ray"] = request.headers.get("CF-RAY")
|
|
124
|
-
|
|
125
|
-
if "cf-ipcontinent" in request.headers:
|
|
126
|
-
_http_info["h_cf_ipcontinent"] = request.headers.get(
|
|
127
|
-
"cf-ipcontinent"
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
if "cf-ipcity" in request.headers:
|
|
131
|
-
_http_info["h_cf_ipcity"] = request.headers.get("cf-ipcity")
|
|
132
|
-
|
|
133
|
-
if "cf-iplongitude" in request.headers:
|
|
134
|
-
_http_info["h_cf_iplongitude"] = request.headers.get(
|
|
135
|
-
"cf-iplongitude"
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
if "cf-iplatitude" in request.headers:
|
|
139
|
-
_http_info["h_cf_iplatitude"] = request.headers.get("cf-iplatitude")
|
|
140
|
-
|
|
141
|
-
if "cf-postal-code" in request.headers:
|
|
142
|
-
_http_info["h_cf_postal_code"] = request.headers.get(
|
|
143
|
-
"cf-postal-code"
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
if "cf-timezone" in request.headers:
|
|
147
|
-
_http_info["h_cf_timezone"] = request.headers.get("cf-timezone")
|
|
148
|
-
|
|
149
|
-
_http_info["method"] = request.method
|
|
150
|
-
_http_info["url_path"] = request.url.path
|
|
151
|
-
if "{" in _http_info["url_path"]:
|
|
152
|
-
_http_info["url_path"] = _http_info["url_path"].replace("{", "{{")
|
|
153
|
-
|
|
154
|
-
if "}" in _http_info["url_path"]:
|
|
155
|
-
_http_info["url_path"] = _http_info["url_path"].replace("}", "}}")
|
|
156
|
-
|
|
157
|
-
if request.url.query:
|
|
158
|
-
_http_info["url_path"] = f"{request.url.path}?{request.url.query}"
|
|
159
|
-
|
|
160
|
-
_http_info["url_query_params"] = request.query_params._dict
|
|
161
|
-
_http_info["url_path_params"] = request.path_params
|
|
162
|
-
|
|
163
|
-
_http_info["h_referer"] = request.headers.get("Referer", "-")
|
|
164
|
-
_http_info["h_user_agent"] = request.headers.get("User-Agent", "-")
|
|
165
|
-
_http_info["h_accept"] = request.headers.get("Accept", "")
|
|
166
|
-
_http_info["h_content_type"] = request.headers.get("Content-Type", "")
|
|
167
|
-
|
|
168
|
-
if "Origin" in request.headers:
|
|
169
|
-
_http_info["h_origin"] = request.headers.get("Origin")
|
|
170
|
-
|
|
171
|
-
_http_info["user_id"] = "-"
|
|
172
|
-
if hasattr(request.state, "user_id"):
|
|
173
|
-
_http_info["user_id"] = str(request.state.user_id)
|
|
174
|
-
|
|
175
|
-
## Debug log:
|
|
176
|
-
if self.use_debug_log:
|
|
177
|
-
_debug_msg = self.debug_format.format(**_http_info)
|
|
178
|
-
|
|
179
|
-
# _logger.debug(_debug_msg)
|
|
180
|
-
await run_in_threadpool(
|
|
181
|
-
_logger.debug,
|
|
182
|
-
_debug_msg,
|
|
183
|
-
)
|
|
184
|
-
## Debug log
|
|
185
|
-
|
|
186
|
-
## Set http info to request state:
|
|
187
|
-
request.state.http_info = _http_info
|
|
188
|
-
|
|
189
|
-
_start_time = time.time()
|
|
190
|
-
## Process request:
|
|
191
|
-
response = await call_next(request)
|
|
192
|
-
## Response processed.
|
|
193
|
-
_http_info["response_time"] = round((time.time() - _start_time) * 1000, 1)
|
|
194
|
-
|
|
195
|
-
if "X-Process-Time" in response.headers:
|
|
196
|
-
try:
|
|
197
|
-
_http_info["response_time"] = float(
|
|
198
|
-
response.headers.get("X-Process-Time")
|
|
199
|
-
)
|
|
200
|
-
except ValueError:
|
|
201
|
-
logger.warning(
|
|
202
|
-
f"`X-Process-Time` header value '{response.headers.get('X-Process-Time')}' is invalid, should be parseable to <float>!"
|
|
203
|
-
)
|
|
204
|
-
else:
|
|
205
|
-
response.headers["X-Process-Time"] = str(_http_info["response_time"])
|
|
206
|
-
|
|
207
|
-
if "X-Request-ID" not in response.headers:
|
|
208
|
-
response.headers["X-Request-ID"] = _http_info["request_id"]
|
|
209
|
-
|
|
210
|
-
if hasattr(request.state, "user_id"):
|
|
211
|
-
_http_info["user_id"] = str(request.state.user_id)
|
|
212
|
-
|
|
213
|
-
_http_info["status_code"] = response.status_code
|
|
214
|
-
_http_info["content_length"] = 0
|
|
215
|
-
if "Content-Length" in response.headers:
|
|
216
|
-
try:
|
|
217
|
-
_http_info["content_length"] = int(
|
|
218
|
-
response.headers.get("Content-Length")
|
|
219
|
-
)
|
|
220
|
-
except ValueError:
|
|
221
|
-
logger.warning(
|
|
222
|
-
f"`Content-Length` header value '{response.headers.get('Content-Length')}' is invalid, should be parseable to <int>!"
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
try:
|
|
226
|
-
json.dumps(_http_info)
|
|
227
|
-
except TypeError:
|
|
228
|
-
logger.warning(
|
|
229
|
-
"Can not serialize `http_info` to json string in HttpAccessLogMiddleware!"
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
## Http access log:
|
|
233
|
-
_LEVEL = "INFO"
|
|
234
|
-
_msg_format = self.msg_format
|
|
235
|
-
if _http_info["status_code"] < 200:
|
|
236
|
-
_LEVEL = "DEBUG"
|
|
237
|
-
_msg_format = f'<d>{_msg_format.replace("{status_code}", "<n><b><k>{status_code}</k></b></n>")}</d>'
|
|
238
|
-
elif (200 <= _http_info["status_code"]) and (_http_info["status_code"] < 300):
|
|
239
|
-
_LEVEL = "SUCCESS"
|
|
240
|
-
_msg_format = f'<w>{_msg_format.replace("{status_code}", "<lvl>{status_code}</lvl>")}</w>'
|
|
241
|
-
elif (300 <= _http_info["status_code"]) and (_http_info["status_code"] < 400):
|
|
242
|
-
_LEVEL = "INFO"
|
|
243
|
-
_msg_format = f'<d>{_msg_format.replace("{status_code}", "<n><b><c>{status_code}</c></b></n>")}</d>'
|
|
244
|
-
elif (400 <= _http_info["status_code"]) and (_http_info["status_code"] < 500):
|
|
245
|
-
_LEVEL = "WARNING"
|
|
246
|
-
_msg_format = _msg_format.replace("{status_code}", "<r>{status_code}</r>")
|
|
247
|
-
elif 500 <= _http_info["status_code"]:
|
|
248
|
-
_LEVEL = "ERROR"
|
|
249
|
-
_msg_format = (
|
|
250
|
-
f'{_msg_format.replace("{status_code}", "<n>{status_code}</n>")}'
|
|
251
|
-
)
|
|
252
|
-
|
|
253
|
-
_msg = _msg_format.format(**_http_info)
|
|
254
|
-
# _logger.bind(http_info=_http_info).log(_LEVEL, _msg)
|
|
255
|
-
await run_in_threadpool(
|
|
256
|
-
_logger.bind(http_info=_http_info).log,
|
|
257
|
-
_LEVEL,
|
|
258
|
-
_msg,
|
|
259
|
-
)
|
|
260
|
-
## Http access log
|
|
261
|
-
|
|
262
|
-
return response
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|