beans-logging-fastapi 1.1.0__py3-none-any.whl → 2.0.0__py3-none-any.whl

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.
@@ -1,4 +1,4 @@
1
- # -*- coding: utf-8 -*-
1
+ # flake8: noqa
2
2
 
3
3
  from ._filters import use_http_filter
4
4
  from ._formats import http_file_format, http_file_json_format
@@ -1,3 +1 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- __version__ = "1.1.0"
1
+ __version__ = "2.0.0"
@@ -1,42 +1,46 @@
1
- # -*- coding: utf-8 -*-
1
+ from typing import Any
2
2
 
3
- from typing import Dict, Any
4
-
5
- from pydantic import validate_arguments
3
+ from pydantic import validate_call
6
4
  from fastapi import Request
7
5
  from fastapi.concurrency import run_in_threadpool
8
6
 
9
7
  from beans_logging import logger, Logger
10
8
 
11
9
 
12
- @validate_arguments(config=dict(arbitrary_types_allowed=True))
10
+ @validate_call(config={"arbitrary_types_allowed": True})
13
11
  async def async_log_http_error(
14
12
  request: Request,
15
13
  status_code: int,
16
- msg_format: str = '<n><w>[{request_id}]</w></n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}" <n>{status_code}</n>',
17
- ):
14
+ msg_format: str = (
15
+ '<n><w>[{request_id}]</w></n> {client_host} {user_id} "<u>{method} {url_path}</u> '
16
+ 'HTTP/{http_version}" <n>{status_code}</n>'
17
+ ),
18
+ ) -> None:
18
19
  """Log HTTP error for unhandled Exception.
19
20
 
20
21
  Args:
21
22
  request (Request, required): Request instance.
22
23
  status_code (int , required): HTTP status code.
23
- msg_format (str , optional): Message format. Defaults to '<n><w>[{request_id}]</w></n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}" <n>{status_code}</n>'.
24
+ msg_format (str , optional): Message format. Defaults to
25
+ '<n><w>[{request_id}]</w></n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}"
26
+ <n>{status_code}</n>'.
24
27
  """
25
28
 
26
- _http_info: Dict[str, Any] = {"request_id": request.state.request_id}
29
+ _http_info: dict[str, Any] = {"request_id": request.state.request_id}
27
30
  if hasattr(request.state, "http_info") and isinstance(
28
31
  request.state.http_info, dict
29
32
  ):
30
- _http_info: Dict[str, Any] = request.state.http_info
33
+ _http_info: dict[str, Any] = request.state.http_info
31
34
  _http_info["status_code"] = status_code
32
35
 
33
36
  _msg = msg_format.format(**_http_info)
34
37
  _logger: Logger = logger.opt(colors=True, record=True).bind(http_info=_http_info)
35
38
  await run_in_threadpool(_logger.error, _msg)
39
+ return
36
40
 
37
41
 
38
- @validate_arguments
39
- async def async_log_trace(message: str):
42
+ @validate_call
43
+ async def async_log_trace(message: str) -> None:
40
44
  """Log trace message.
41
45
 
42
46
  Args:
@@ -44,10 +48,11 @@ async def async_log_trace(message: str):
44
48
  """
45
49
 
46
50
  await run_in_threadpool(logger.trace, message)
51
+ return
47
52
 
48
53
 
49
- @validate_arguments
50
- async def async_log_debug(message: str):
54
+ @validate_call
55
+ async def async_log_debug(message: str) -> None:
51
56
  """Log debug message.
52
57
 
53
58
  Args:
@@ -55,10 +60,11 @@ async def async_log_debug(message: str):
55
60
  """
56
61
 
57
62
  await run_in_threadpool(logger.debug, message)
63
+ return
58
64
 
59
65
 
60
- @validate_arguments
61
- async def async_log_info(message: str):
66
+ @validate_call
67
+ async def async_log_info(message: str) -> None:
62
68
  """Log info message.
63
69
 
64
70
  Args:
@@ -66,10 +72,11 @@ async def async_log_info(message: str):
66
72
  """
67
73
 
68
74
  await run_in_threadpool(logger.info, message)
75
+ return
69
76
 
70
77
 
71
- @validate_arguments
72
- async def async_log_success(message: str):
78
+ @validate_call
79
+ async def async_log_success(message: str) -> None:
73
80
  """Log success message.
74
81
 
75
82
  Args:
@@ -77,10 +84,11 @@ async def async_log_success(message: str):
77
84
  """
78
85
 
79
86
  await run_in_threadpool(logger.success, message)
87
+ return
80
88
 
81
89
 
82
- @validate_arguments
83
- async def async_log_warning(message: str):
90
+ @validate_call
91
+ async def async_log_warning(message: str) -> None:
84
92
  """Log warning message.
85
93
 
86
94
  Args:
@@ -88,10 +96,11 @@ async def async_log_warning(message: str):
88
96
  """
89
97
 
90
98
  await run_in_threadpool(logger.warning, message)
99
+ return
91
100
 
92
101
 
93
- @validate_arguments
94
- async def async_log_error(message: str):
102
+ @validate_call
103
+ async def async_log_error(message: str) -> None:
95
104
  """Log error message.
96
105
 
97
106
  Args:
@@ -99,10 +108,11 @@ async def async_log_error(message: str):
99
108
  """
100
109
 
101
110
  await run_in_threadpool(logger.error, message)
111
+ return
102
112
 
103
113
 
104
- @validate_arguments
105
- async def async_log_critical(message: str):
114
+ @validate_call
115
+ async def async_log_critical(message: str) -> None:
106
116
  """Log critical message.
107
117
 
108
118
  Args:
@@ -110,10 +120,11 @@ async def async_log_critical(message: str):
110
120
  """
111
121
 
112
122
  await run_in_threadpool(logger.critical, message)
123
+ return
113
124
 
114
125
 
115
- @validate_arguments
116
- async def async_log_level(level: str, message: str):
126
+ @validate_call
127
+ async def async_log_level(level: str, message: str) -> None:
117
128
  """Log level message.
118
129
 
119
130
  Args:
@@ -122,6 +133,7 @@ async def async_log_level(level: str, message: str):
122
133
  """
123
134
 
124
135
  await run_in_threadpool(logger.log, level, message)
136
+ return
125
137
 
126
138
 
127
139
  __all__ = [
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- from typing import Dict, Any
1
+ from typing import Any
4
2
 
5
3
  from fastapi import Request, Response
6
4
  from fastapi.concurrency import run_in_threadpool
@@ -16,16 +14,23 @@ class HttpAccessLogMiddleware(BaseHTTPMiddleware):
16
14
  BaseHTTPMiddleware: Base HTTP middleware class from starlette.
17
15
 
18
16
  Attributes:
19
- _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}"'.
20
- _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'.
21
-
22
- debug_format (str ): Http access log debug message format. Defaults to `HttpAccessLogMiddleware._DEBUG_FORMAT`.
17
+ _DEBUG_FORMAT (str ): Default http access log debug message format. Defaults to
18
+ '<n>[{request_id}]</n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}"'.
19
+ _MSG_FORMAT (str ): Default http access log message format. Defaults to
20
+ '<n><w>[{request_id}]</w></n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}"
21
+ {status_code} {content_length}B {response_time}ms'.
22
+
23
+ debug_format (str ): Http access log debug message format. Defaults to
24
+ `HttpAccessLogMiddleware._DEBUG_FORMAT`.
23
25
  msg_format (str ): Http access log message format. Defaults to `HttpAccessLogMiddleware._MSG_FORMAT`.
24
26
  use_debug_log (bool): If True, use debug log to log http access log. Defaults to True.
25
27
  """
26
28
 
27
29
  _DEBUG_FORMAT = '<n>[{request_id}]</n> {client_host} {user_id} "<u>{method} {url_path}</u> HTTP/{http_version}"'
28
- _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'
30
+ _MSG_FORMAT = (
31
+ '<n><w>[{request_id}]</w></n> {client_host} {user_id} "<u>{method} {url_path}</u> '
32
+ 'HTTP/{http_version}" {status_code} {content_length}B {response_time}ms'
33
+ )
29
34
 
30
35
  def __init__(
31
36
  self,
@@ -42,13 +47,13 @@ class HttpAccessLogMiddleware(BaseHTTPMiddleware):
42
47
  async def dispatch(self, request: Request, call_next) -> Response:
43
48
  _logger = logger.opt(colors=True, record=True)
44
49
 
45
- _http_info: Dict[str, Any] = {}
50
+ _http_info: dict[str, Any] = {}
46
51
  if hasattr(request.state, "http_info") and isinstance(
47
52
  request.state.http_info, dict
48
53
  ):
49
- _http_info: Dict[str, Any] = request.state.http_info
54
+ _http_info: dict[str, Any] = request.state.http_info
50
55
 
51
- ## Debug log:
56
+ # Debug log:
52
57
  if self.use_debug_log:
53
58
  _debug_msg = self.debug_format.format(**_http_info)
54
59
 
@@ -57,18 +62,18 @@ class HttpAccessLogMiddleware(BaseHTTPMiddleware):
57
62
  _logger.debug,
58
63
  _debug_msg,
59
64
  )
60
- ## Debug log
65
+ # Debug log
61
66
 
62
- ## Process request:
67
+ # Process request:
63
68
  response: Response = await call_next(request)
64
- ## Response processed.
69
+ # Response processed.
65
70
 
66
71
  if hasattr(request.state, "http_info") and isinstance(
67
72
  request.state.http_info, dict
68
73
  ):
69
- _http_info: Dict[str, Any] = request.state.http_info
74
+ _http_info: dict[str, Any] = request.state.http_info
70
75
 
71
- ## Http access log:
76
+ # Http access log:
72
77
  _LEVEL = "INFO"
73
78
  _msg_format = self.msg_format
74
79
  if _http_info["status_code"] < 200:
@@ -92,9 +97,11 @@ class HttpAccessLogMiddleware(BaseHTTPMiddleware):
92
97
  _msg = _msg_format.format(**_http_info)
93
98
  # _logger.bind(http_info=_http_info).log(_LEVEL, _msg)
94
99
  await run_in_threadpool(_logger.bind(http_info=_http_info).log, _LEVEL, _msg)
95
- ## Http access log
100
+ # Http access log
96
101
 
97
102
  return response
98
103
 
99
104
 
100
- __all__ = ["HttpAccessLogMiddleware"]
105
+ __all__ = [
106
+ "HttpAccessLogMiddleware",
107
+ ]
@@ -1,13 +1,16 @@
1
- # -*- coding: utf-8 -*-
1
+ from typing import TYPE_CHECKING
2
+
3
+ if TYPE_CHECKING:
4
+ from loguru import Record
2
5
 
3
6
  from beans_logging.filters import use_all_filter
4
7
 
5
8
 
6
- def use_http_filter(record: dict) -> bool:
9
+ def use_http_filter(record: "Record") -> bool:
7
10
  """Filter message only for http access log handler by checking 'http_info' key in extra.
8
11
 
9
12
  Args:
10
- record (dict): Log record as dictionary.
13
+ record (Record, required): Log record as dictionary.
11
14
 
12
15
  Returns:
13
16
  bool: True if record has 'http_info' key in extra, False otherwise.
@@ -22,4 +25,6 @@ def use_http_filter(record: dict) -> bool:
22
25
  return True
23
26
 
24
27
 
25
- __all__ = ["use_http_filter"]
28
+ __all__ = [
29
+ "use_http_filter",
30
+ ]
@@ -1,18 +1,26 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- from typing import Dict, Any
1
+ import json
2
+ from typing import Any
4
3
  from zoneinfo import ZoneInfo
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from loguru import Record
5
8
 
6
9
 
7
10
  def http_file_format(
8
- record: dict,
9
- msg_format: str = '{client_host} {request_id} {user_id} [{datetime}] "{method} {url_path} HTTP/{http_version}" {status_code} {content_length} "{h_referer}" "{h_user_agent}" {response_time}',
11
+ record: "Record",
12
+ msg_format: str = (
13
+ '{client_host} {request_id} {user_id} [{datetime}] "{method} {url_path} HTTP/{http_version}" '
14
+ '{status_code} {content_length} "{h_referer}" "{h_user_agent}" {response_time}'
15
+ ),
10
16
  tz: str = "localtime",
11
17
  ) -> str:
12
18
  """Http access log file format.
13
19
 
14
20
  Args:
15
- record (dict): Log record as dictionary.
21
+ record (Record, required): Log record as dictionary.
22
+ msg_format (str , optional): Log message format.
23
+ tz (str , optional): Timezone for datetime field. Defaults to 'localtime'.
16
24
 
17
25
  Returns:
18
26
  str: Format for http access log record.
@@ -21,43 +29,44 @@ def http_file_format(
21
29
  if "http_info" not in record["extra"]:
22
30
  return ""
23
31
 
24
- if "http_message" not in record:
25
- _http_info: Dict[str, Any] = record["extra"]["http_info"]
26
- if "datetime" not in _http_info:
27
- _dt = record["time"]
28
- if tz != "localtime":
29
- if not _dt.tzinfo:
30
- _dt = _dt.replace(tzinfo=ZoneInfo("UTC"))
32
+ if "http_message" in record["extra"]:
33
+ del record["extra"]["http_message"]
31
34
 
32
- _dt = _dt.astimezone(ZoneInfo(tz))
35
+ _http_info: dict[str, Any] = record["extra"]["http_info"]
36
+ if "datetime" not in _http_info:
37
+ _dt = record["time"]
38
+ if tz != "localtime":
39
+ if not _dt.tzinfo:
40
+ _dt = _dt.replace(tzinfo=ZoneInfo("UTC"))
33
41
 
34
- _http_info["datetime"] = _dt.isoformat(timespec="milliseconds")
42
+ _dt = _dt.astimezone(ZoneInfo(tz))
35
43
 
36
- if "content_length" not in _http_info:
37
- _http_info["content_length"] = 0
44
+ _http_info["datetime"] = _dt.isoformat(timespec="milliseconds")
38
45
 
39
- if "h_referer" not in _http_info:
40
- _http_info["h_referer"] = "-"
46
+ if "content_length" not in _http_info:
47
+ _http_info["content_length"] = 0
41
48
 
42
- if "h_user_agent" not in _http_info:
43
- _http_info["h_user_agent"] = "-"
49
+ if "h_referer" not in _http_info:
50
+ _http_info["h_referer"] = "-"
44
51
 
45
- if "response_time" not in _http_info:
46
- _http_info["response_time"] = 0
52
+ if "h_user_agent" not in _http_info:
53
+ _http_info["h_user_agent"] = "-"
47
54
 
48
- record["extra"]["http_info"] = _http_info
55
+ if "response_time" not in _http_info:
56
+ _http_info["response_time"] = 0
49
57
 
50
- _msg = msg_format.format(**_http_info)
51
- record["http_message"] = _msg
58
+ record["extra"]["http_info"] = _http_info
59
+ _msg = msg_format.format(**_http_info)
52
60
 
53
- return "{http_message}\n"
61
+ record["extra"]["http_message"] = _msg
62
+ return "{extra[http_message]}\n"
54
63
 
55
64
 
56
- def http_file_json_format(record: dict) -> str:
65
+ def http_file_json_format(record: "Record") -> str:
57
66
  """Http access json log file format.
58
67
 
59
68
  Args:
60
- record (dict): Log record as dictionary.
69
+ record (Record, required): Log record as dictionary.
61
70
 
62
71
  Returns:
63
72
  str: Format for http access json log record.
@@ -66,12 +75,21 @@ def http_file_json_format(record: dict) -> str:
66
75
  if "http_info" not in record["extra"]:
67
76
  return ""
68
77
 
69
- _http_info: Dict[str, Any] = record["extra"]["http_info"]
70
- if "datetime" not in _http_info:
71
- _http_info["datetime"] = record["time"].isoformat(timespec="milliseconds")
72
- record["extra"]["http_info"] = _http_info
78
+ if "datetime" not in record["extra"]["http_info"]:
79
+ record["extra"]["http_info"]["datetime"] = record["time"].isoformat(
80
+ timespec="milliseconds"
81
+ )
82
+
83
+ if "http_serialized" in record["extra"]:
84
+ del record["extra"]["http_serialized"]
85
+
86
+ _http_info = record["extra"]["http_info"]
87
+ record["extra"]["http_serialized"] = json.dumps(_http_info)
73
88
 
74
- return "{extra[http_info]}\n"
89
+ return "{extra[http_serialized]}\n"
75
90
 
76
91
 
77
- __all__ = ["http_file_format", "http_file_json_format"]
92
+ __all__ = [
93
+ "http_file_format",
94
+ "http_file_json_format",
95
+ ]
@@ -1,13 +1,6 @@
1
- # -*- coding: utf-8 -*-
1
+ from collections.abc import Callable
2
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
3
+ from pydantic import validate_call
11
4
 
12
5
  from beans_logging import LoggerLoader
13
6
 
@@ -15,13 +8,13 @@ from ._filters import use_http_filter
15
8
  from ._formats import http_file_format, http_file_json_format
16
9
 
17
10
 
18
- @validate_call(config=dict(arbitrary_types_allowed=True))
11
+ @validate_call(config={"arbitrary_types_allowed": True})
19
12
  def add_http_file_handler(
20
13
  logger_loader: LoggerLoader,
21
14
  log_path: str = "http/{app_name}.http.access.log",
22
15
  err_path: str = "http/{app_name}.http.err.log",
23
- formatter: Union[Callable, str] = http_file_format,
24
- ):
16
+ formatter: Callable | str = http_file_format,
17
+ ) -> None:
25
18
  """Add http access log file and error file handler.
26
19
 
27
20
  Args:
@@ -31,52 +24,73 @@ def add_http_file_handler(
31
24
  formatter (Union[Callable, str], optional): Log formatter. Defaults to `http_file_format` function.
32
25
  """
33
26
 
34
- logger_loader.add_custom_handler(
35
- handler_name="FILE.HTTP",
36
- sink=log_path,
37
- filter=use_http_filter,
38
- format=formatter,
27
+ logger_loader.add_handler(
28
+ name="default.http.access.file_handler",
29
+ handler={
30
+ "type": "FILE",
31
+ "sink": log_path,
32
+ "filter": use_http_filter,
33
+ "format": formatter,
34
+ },
39
35
  )
40
36
 
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,
37
+ logger_loader.add_handler(
38
+ name="default.http.err.file_handler",
39
+ handler={
40
+ "type": "FILE",
41
+ "sink": err_path,
42
+ "filter": use_http_filter,
43
+ "format": formatter,
44
+ "error": True,
45
+ },
47
46
  )
48
47
 
48
+ return
49
49
 
50
- @validate_call(config=dict(arbitrary_types_allowed=True))
50
+
51
+ @validate_call(config={"arbitrary_types_allowed": True})
51
52
  def add_http_file_json_handler(
52
53
  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
- ):
54
+ log_path: str = "http.json/{app_name}.http.json.access.log",
55
+ err_path: str = "http.json/{app_name}.http.json.err.log",
56
+ formatter: Callable | str = http_file_json_format,
57
+ ) -> None:
57
58
  """Add http access json log file and json error file handler.
58
59
 
59
60
  Args:
60
61
  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".
62
+ log_path (str, optional): Json log file path. Defaults to
63
+ "http.json/{app_name}.http.json.access.log".
64
+ err_path (str, optional): Json error log file path. Defaults to
65
+ "http.json/{app_name}.http.json.err.log".
63
66
  formatter (Union[Callable, str], optional): Log formatter. Defaults to `http_file_json_format` function.
64
67
  """
65
68
 
66
- logger_loader.add_custom_handler(
67
- handler_name="FILE.JSON.HTTP",
68
- sink=log_path,
69
- filter=use_http_filter,
70
- format=formatter,
69
+ logger_loader.add_handler(
70
+ name="default.http.access.json_handler",
71
+ handler={
72
+ "type": "FILE",
73
+ "sink": log_path,
74
+ "filter": use_http_filter,
75
+ "format": formatter,
76
+ },
71
77
  )
72
78
 
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
+ logger_loader.add_handler(
80
+ name="default.http.err.json_handler",
81
+ handler={
82
+ "type": "FILE",
83
+ "sink": err_path,
84
+ "filter": use_http_filter,
85
+ "format": formatter,
86
+ "error": True,
87
+ },
79
88
  )
80
89
 
90
+ return
91
+
81
92
 
82
- __all__ = ["add_http_file_handler", "add_http_file_json_handler"]
93
+ __all__ = [
94
+ "add_http_file_handler",
95
+ "add_http_file_json_handler",
96
+ ]