beans-logging-fastapi 8.0.1__tar.gz → 8.0.2__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.
Files changed (31) hide show
  1. {beans_logging_fastapi-8.0.1/src/beans_logging_fastapi.egg-info → beans_logging_fastapi-8.0.2}/PKG-INFO +2 -2
  2. beans_logging_fastapi-8.0.2/requirements.txt +2 -0
  3. beans_logging_fastapi-8.0.2/src/beans_logging_fastapi/__version__.py +1 -0
  4. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/src/beans_logging_fastapi/formats.py +2 -2
  5. beans_logging_fastapi-8.0.2/src/beans_logging_fastapi/http_error.py +131 -0
  6. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/src/beans_logging_fastapi/middlewares.py +5 -8
  7. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/src/beans_logging_fastapi/mode.py +4 -1
  8. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2/src/beans_logging_fastapi.egg-info}/PKG-INFO +2 -2
  9. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/src/beans_logging_fastapi.egg-info/requires.txt +1 -1
  10. beans_logging_fastapi-8.0.1/requirements.txt +0 -2
  11. beans_logging_fastapi-8.0.1/src/beans_logging_fastapi/__version__.py +0 -1
  12. beans_logging_fastapi-8.0.1/src/beans_logging_fastapi/http_error.py +0 -81
  13. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/.python-version +0 -0
  14. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/LICENSE.txt +0 -0
  15. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/README.md +0 -0
  16. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/pyproject.toml +0 -0
  17. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/requirements/requirements.build.txt +0 -0
  18. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/requirements/requirements.dev.txt +0 -0
  19. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/requirements/requirements.docs.txt +0 -0
  20. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/requirements/requirements.test.txt +0 -0
  21. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/setup.cfg +0 -0
  22. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/setup.py +0 -0
  23. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/src/beans_logging_fastapi/__init__.py +0 -0
  24. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/src/beans_logging_fastapi/_async.py +0 -0
  25. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/src/beans_logging_fastapi/_core.py +0 -0
  26. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/src/beans_logging_fastapi/config.py +0 -0
  27. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/src/beans_logging_fastapi/constants.py +0 -0
  28. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/src/beans_logging_fastapi/filters.py +0 -0
  29. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/src/beans_logging_fastapi.egg-info/SOURCES.txt +0 -0
  30. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/src/beans_logging_fastapi.egg-info/dependency_links.txt +0 -0
  31. {beans_logging_fastapi-8.0.1 → beans_logging_fastapi-8.0.2}/src/beans_logging_fastapi.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: beans_logging_fastapi
3
- Version: 8.0.1
3
+ Version: 8.0.2
4
4
  Summary: This is a HTTP access log module for FastAPI based on 'beans-logging' package.
5
5
  Author-email: Batkhuu Byambajav <batkhuu10@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/bybatkhuu/module-fastapi-logging
@@ -22,7 +22,7 @@ Requires-Python: <4.0,>=3.10
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE.txt
24
24
  Requires-Dist: fastapi<1.0.0,>=0.99.1
25
- Requires-Dist: beans-logging<13.0.0,>=12.0.2
25
+ Requires-Dist: beans-logging<13.0.0,>=12.0.3
26
26
  Provides-Extra: test
27
27
  Requires-Dist: pytest<10.0.0,>=8.0.2; extra == "test"
28
28
  Requires-Dist: pytest-cov<8.0.0,>=5.0.0; extra == "test"
@@ -0,0 +1,2 @@
1
+ fastapi>=0.99.1,<1.0.0
2
+ beans-logging>=12.0.3,<13.0.0
@@ -0,0 +1 @@
1
+ __version__ = "8.0.2"
@@ -109,7 +109,7 @@ def id_std_format(record: "Record") -> str:
109
109
  _format = (
110
110
  "[<c>{time:YYYY-MM-DD HH:mm:ss.SSS Z}</c> | <level>{extra[level_short]:<5}</level> | <w>{name}:{line}</w>"
111
111
  f"{_request_id_part}{_trace_id_part}{_user_id_part}"
112
- "]: <level>{message}</level>\n"
112
+ "]: <level>{message}</level>\n{exception}"
113
113
  )
114
114
  return _format
115
115
 
@@ -134,7 +134,7 @@ def id_file_format(record: "Record") -> str:
134
134
  _format = (
135
135
  "[{time:YYYY-MM-DD HH:mm:ss.SSS Z} | {extra[level_short]:<5} | {name}:{line}"
136
136
  f"{_request_id_part}{_trace_id_part}{_user_id_part}"
137
- "]: {message}\n"
137
+ "]: {message}\n{exception}"
138
138
  )
139
139
  return _format
140
140
 
@@ -0,0 +1,131 @@
1
+ from typing import Any
2
+
3
+ from pydantic import validate_call
4
+ from fastapi import Request
5
+ from fastapi.concurrency import run_in_threadpool
6
+
7
+ from beans_logging import logger, Logger
8
+
9
+
10
+ @validate_call(config={"arbitrary_types_allowed": True})
11
+ async def async_log_http_error(
12
+ request: Request,
13
+ status_code: int,
14
+ exc: Exception | None = None,
15
+ sub_format: str = (
16
+ '{client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}" <n>{status_code}</n>'
17
+ ),
18
+ ) -> None:
19
+ """Async Log HTTP error for unhandled Exception.
20
+
21
+ Args:
22
+ request (Request , required): Request instance.
23
+ status_code (int , required): HTTP status code.
24
+ exc (Exception, optional): Exception instance. Defaults to None.
25
+ sub_format (str , optional): Message format. Defaults to
26
+ '{client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}" <n>{status_code}</n>'.
27
+ """
28
+
29
+ _http_info: dict[str, Any] = {}
30
+ if hasattr(request.state, "http_info") and isinstance(
31
+ request.state.http_info, dict
32
+ ):
33
+ _http_info = request.state.http_info
34
+
35
+ _http_info.setdefault("request_id", "")
36
+ if hasattr(request.state, "request_id") and isinstance(
37
+ request.state.request_id, str
38
+ ):
39
+ _http_info["request_id"] = request.state.request_id
40
+
41
+ if "url_path" not in _http_info:
42
+ _url_path = request.url.path
43
+ if request.url.query:
44
+ _url_path = f"{_url_path}?{request.url.query}"
45
+ _url_path = _url_path.replace("{", "{{").replace("}", "}}").replace("<", "\\<")
46
+ _http_info["url_path"] = _url_path
47
+
48
+ _http_info.setdefault("client_host", request.client.host if request.client else "")
49
+ _http_info.setdefault("user_id", "-")
50
+ _http_info.setdefault("method", request.method)
51
+ _http_info.setdefault("http_version", request.scope.get("http_version", "1.1"))
52
+ _http_info["status_code"] = status_code
53
+
54
+ _msg = sub_format.format(**_http_info)
55
+ _logger: Logger = logger.opt(colors=True, record=True).bind(
56
+ http_info=_http_info,
57
+ request_id=_http_info.get("request_id"),
58
+ disable_std_handler=True,
59
+ )
60
+
61
+ if exc:
62
+ await run_in_threadpool(_logger.opt(exception=exc).error, _msg)
63
+ else:
64
+ await run_in_threadpool(_logger.error, _msg)
65
+
66
+ return
67
+
68
+
69
+ @validate_call(config={"arbitrary_types_allowed": True})
70
+ def log_http_error(
71
+ request: Request,
72
+ status_code: int,
73
+ exc: Exception | None = None,
74
+ sub_format: str = (
75
+ '{client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}" <n>{status_code}</n>'
76
+ ),
77
+ ) -> None:
78
+ """Log HTTP error for unhandled Exception.
79
+
80
+ Args:
81
+ request (Request , required): Request instance.
82
+ status_code (int , required): HTTP status code.
83
+ exc (Exception, optional): Exception instance. Defaults to None.
84
+ sub_format (str , optional): Message format. Defaults to
85
+ '{client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}" <n>{status_code}</n>'.
86
+ """
87
+
88
+ _http_info: dict[str, Any] = {}
89
+ if hasattr(request.state, "http_info") and isinstance(
90
+ request.state.http_info, dict
91
+ ):
92
+ _http_info = request.state.http_info
93
+
94
+ _http_info.setdefault("request_id", "")
95
+ if hasattr(request.state, "request_id") and isinstance(
96
+ request.state.request_id, str
97
+ ):
98
+ _http_info["request_id"] = request.state.request_id
99
+
100
+ if "url_path" not in _http_info:
101
+ _url_path = request.url.path
102
+ if request.url.query:
103
+ _url_path = f"{_url_path}?{request.url.query}"
104
+ _url_path = _url_path.replace("{", "{{").replace("}", "}}").replace("<", "\\<")
105
+ _http_info["url_path"] = _url_path
106
+
107
+ _http_info.setdefault("client_host", request.client.host if request.client else "")
108
+ _http_info.setdefault("user_id", "-")
109
+ _http_info.setdefault("method", request.method)
110
+ _http_info.setdefault("http_version", request.scope.get("http_version", "1.1"))
111
+ _http_info["status_code"] = status_code
112
+
113
+ _msg = sub_format.format(**_http_info)
114
+ _logger: Logger = logger.opt(colors=True, record=True, depth=3).bind(
115
+ http_info=_http_info,
116
+ request_id=_http_info.get("request_id"),
117
+ disable_std_handler=True,
118
+ )
119
+
120
+ if exc:
121
+ _logger.opt(exception=exc).error(_msg)
122
+ else:
123
+ _logger.error(_msg)
124
+
125
+ return
126
+
127
+
128
+ __all__ = [
129
+ "async_log_http_error",
130
+ "log_http_error",
131
+ ]
@@ -137,15 +137,12 @@ class RequestHTTPInfoMiddleware(BaseHTTPMiddleware):
137
137
  _http_info["h_cf_timezone"] = request.headers.get("cf-timezone")
138
138
 
139
139
  _http_info["method"] = request.method
140
- _http_info["url_path"] = request.url.path
141
- if "{" in _http_info["url_path"]:
142
- _http_info["url_path"] = _http_info["url_path"].replace("{", "{{")
143
- if "}" in _http_info["url_path"]:
144
- _http_info["url_path"] = _http_info["url_path"].replace("}", "}}")
145
- if "<" in _http_info["url_path"]:
146
- _http_info["url_path"] = _http_info["url_path"].replace("<", "\\<")
140
+
141
+ _url_path = request.url.path
147
142
  if request.url.query:
148
- _http_info["url_path"] = f"{request.url.path}?{request.url.query}"
143
+ _url_path = f"{_url_path}?{request.url.query}"
144
+ _url_path = _url_path.replace("{", "{{").replace("}", "}}").replace("<", "\\<")
145
+ _http_info["url_path"] = _url_path
149
146
 
150
147
  _http_info["url_query_params"] = request.query_params._dict
151
148
  _http_info["url_path_params"] = request.path_params
@@ -1,3 +1,4 @@
1
+ import sys
1
2
  from typing import TYPE_CHECKING
2
3
 
3
4
  from pydantic import validate_call
@@ -42,9 +43,11 @@ async def async_log_at(
42
43
 
43
44
  if warn_mode == WarnEnum.ALWAYS:
44
45
  if level == LogLevelEnum.EXCEPTION:
45
- await run_in_threadpool(logger.exception, message)
46
+ _exc_info = sys.exc_info()
47
+ await run_in_threadpool(logger.opt(exception=_exc_info).error, message)
46
48
  else:
47
49
  await run_in_threadpool(logger.log, level.name, message)
50
+
48
51
  elif warn_mode == WarnEnum.DEBUG:
49
52
  await run_in_threadpool(logger.debug, message)
50
53
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: beans_logging_fastapi
3
- Version: 8.0.1
3
+ Version: 8.0.2
4
4
  Summary: This is a HTTP access log module for FastAPI based on 'beans-logging' package.
5
5
  Author-email: Batkhuu Byambajav <batkhuu10@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/bybatkhuu/module-fastapi-logging
@@ -22,7 +22,7 @@ Requires-Python: <4.0,>=3.10
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE.txt
24
24
  Requires-Dist: fastapi<1.0.0,>=0.99.1
25
- Requires-Dist: beans-logging<13.0.0,>=12.0.2
25
+ Requires-Dist: beans-logging<13.0.0,>=12.0.3
26
26
  Provides-Extra: test
27
27
  Requires-Dist: pytest<10.0.0,>=8.0.2; extra == "test"
28
28
  Requires-Dist: pytest-cov<8.0.0,>=5.0.0; extra == "test"
@@ -1,5 +1,5 @@
1
1
  fastapi<1.0.0,>=0.99.1
2
- beans-logging<13.0.0,>=12.0.2
2
+ beans-logging<13.0.0,>=12.0.3
3
3
 
4
4
  [build]
5
5
  setuptools<83.0.0,>=70.3.0
@@ -1,2 +0,0 @@
1
- fastapi>=0.99.1,<1.0.0
2
- beans-logging>=12.0.2,<13.0.0
@@ -1 +0,0 @@
1
- __version__ = "8.0.1"
@@ -1,81 +0,0 @@
1
- from typing import Any
2
-
3
- from pydantic import validate_call
4
- from fastapi import Request
5
- from fastapi.concurrency import run_in_threadpool
6
-
7
- from beans_logging import logger, Logger
8
-
9
-
10
- @validate_call(config={"arbitrary_types_allowed": True})
11
- async def async_log_http_error(
12
- request: Request,
13
- status_code: int,
14
- sub_format: str = (
15
- '{client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}" <n>{status_code}</n>'
16
- ),
17
- ) -> None:
18
- """Async Log HTTP error for unhandled Exception.
19
-
20
- Args:
21
- request (Request, required): Request instance.
22
- status_code (int , required): HTTP status code.
23
- sub_format (str , optional): Message format. Defaults to
24
- '{client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}" <n>{status_code}</n>'.
25
- """
26
-
27
- _http_info: dict[str, Any] = {"request_id": request.state.request_id}
28
- if hasattr(request.state, "http_info") and isinstance(
29
- request.state.http_info, dict
30
- ):
31
- _http_info: dict[str, Any] = request.state.http_info
32
- _http_info["status_code"] = status_code
33
-
34
- _msg = sub_format.format(**_http_info)
35
- _logger: Logger = logger.opt(colors=True, record=True).bind(
36
- http_info=_http_info,
37
- request_id=_http_info.get("request_id", ""),
38
- disable_std_handler=True,
39
- )
40
- await run_in_threadpool(_logger.error, _msg)
41
- return
42
-
43
-
44
- @validate_call(config={"arbitrary_types_allowed": True})
45
- def log_http_error(
46
- request: Request,
47
- status_code: int,
48
- sub_format: str = (
49
- '{client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}" <n>{status_code}</n>'
50
- ),
51
- ) -> None:
52
- """Log HTTP error for unhandled Exception.
53
-
54
- Args:
55
- request (Request, required): Request instance.
56
- status_code (int , required): HTTP status code.
57
- sub_format (str , optional): Message format. Defaults to
58
- '{client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}" <n>{status_code}</n>'.
59
- """
60
-
61
- _http_info: dict[str, Any] = {"request_id": request.state.request_id}
62
- if hasattr(request.state, "http_info") and isinstance(
63
- request.state.http_info, dict
64
- ):
65
- _http_info: dict[str, Any] = request.state.http_info
66
- _http_info["status_code"] = status_code
67
-
68
- _msg = sub_format.format(**_http_info)
69
- _logger: Logger = logger.opt(colors=True, record=True, depth=3).bind(
70
- http_info=_http_info,
71
- request_id=_http_info.get("request_id", ""),
72
- disable_std_handler=True,
73
- )
74
- _logger.error(_msg)
75
- return
76
-
77
-
78
- __all__ = [
79
- "async_log_http_error",
80
- "log_http_error",
81
- ]