beans-logging 6.0.2__py3-none-any.whl → 7.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.
- beans_logging/__init__.py +6 -6
- beans_logging/__version__.py +1 -1
- beans_logging/_builder.py +154 -0
- beans_logging/_constants.py +30 -0
- beans_logging/_core.py +295 -0
- beans_logging/_intercept.py +106 -0
- beans_logging/auto.py +3 -12
- beans_logging/config.py +186 -0
- beans_logging/filters.py +37 -20
- beans_logging/formats.py +20 -4
- beans_logging/{rotation.py → rotators.py} +20 -14
- beans_logging/schemas.py +129 -159
- beans_logging/sinks.py +11 -2
- {beans_logging-6.0.2.dist-info → beans_logging-7.0.0.dist-info}/METADATA +80 -61
- beans_logging-7.0.0.dist-info/RECORD +18 -0
- beans_logging/_base.py +0 -660
- beans_logging/_consts.py +0 -18
- beans_logging/_handlers.py +0 -40
- beans_logging/_utils.py +0 -99
- beans_logging-6.0.2.dist-info/RECORD +0 -17
- {beans_logging-6.0.2.dist-info → beans_logging-7.0.0.dist-info}/WHEEL +0 -0
- {beans_logging-6.0.2.dist-info → beans_logging-7.0.0.dist-info}/licenses/LICENSE.txt +0 -0
- {beans_logging-6.0.2.dist-info → beans_logging-7.0.0.dist-info}/top_level.txt +0 -0
beans_logging/__init__.py
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from ._consts import WarnEnum
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
4
3
|
from .__version__ import __version__
|
|
4
|
+
from .config import LoggerConfigPM
|
|
5
|
+
from ._core import Logger, logger, LoggerLoader
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
__all__ = [
|
|
9
|
+
"__version__",
|
|
10
|
+
"LoggerConfigPM",
|
|
8
11
|
"Logger",
|
|
9
12
|
"logger",
|
|
10
13
|
"LoggerLoader",
|
|
11
|
-
"LoggerConfigPM",
|
|
12
|
-
"WarnEnum",
|
|
13
|
-
"__version__",
|
|
14
14
|
]
|
beans_logging/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "
|
|
1
|
+
__version__ = "7.0.0"
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from pydantic import validate_call
|
|
5
|
+
|
|
6
|
+
from ._constants import LogHandlerTypeEnum, LogLevelEnum
|
|
7
|
+
from .schemas import LogHandlerPM
|
|
8
|
+
from .config import LoggerConfigPM
|
|
9
|
+
from .sinks import std_sink
|
|
10
|
+
from .formats import json_formatter
|
|
11
|
+
from .filters import (
|
|
12
|
+
use_all_filter,
|
|
13
|
+
use_std_filter,
|
|
14
|
+
use_file_filter,
|
|
15
|
+
use_file_err_filter,
|
|
16
|
+
use_file_json_filter,
|
|
17
|
+
use_file_json_err_filter,
|
|
18
|
+
)
|
|
19
|
+
from .rotators import Rotator
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@validate_call
|
|
23
|
+
def build_handler(handler: LogHandlerPM, config: LoggerConfigPM) -> dict[str, Any]:
|
|
24
|
+
"""Build handler config as dictionary for Loguru logger to add new handler.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
handler (LogHandlerPM , required): Target log handler model.
|
|
28
|
+
config (LoggerConfigPM, required): Default main config model to fill missing values.
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
ValueError: 'sink' attribute is empty, required for any log handler except std and file handlers!
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
dict[str, Any]: Loguru handler config as dictionary.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
_handler_dict = handler.model_dump(by_alias=True, exclude_none=True)
|
|
38
|
+
|
|
39
|
+
if _handler_dict.get("sink") is None:
|
|
40
|
+
if _handler_dict.get("type") == LogHandlerTypeEnum.STD:
|
|
41
|
+
_handler_dict["sink"] = std_sink
|
|
42
|
+
elif _handler_dict.get("type") == LogHandlerTypeEnum.FILE:
|
|
43
|
+
_logs_path: str = ""
|
|
44
|
+
if _handler_dict.get("serialize") or _handler_dict.get("custom_serialize"):
|
|
45
|
+
if _handler_dict.get("error"):
|
|
46
|
+
_logs_path = os.path.join(
|
|
47
|
+
config.default.file.logs_dir,
|
|
48
|
+
config.default.file.json_.err_path,
|
|
49
|
+
)
|
|
50
|
+
else:
|
|
51
|
+
_logs_path = os.path.join(
|
|
52
|
+
config.default.file.logs_dir,
|
|
53
|
+
config.default.file.json_.log_path,
|
|
54
|
+
)
|
|
55
|
+
else:
|
|
56
|
+
if _handler_dict.get("error"):
|
|
57
|
+
_logs_path = os.path.join(
|
|
58
|
+
config.default.file.logs_dir,
|
|
59
|
+
config.default.file.plain.err_path,
|
|
60
|
+
)
|
|
61
|
+
else:
|
|
62
|
+
_logs_path = os.path.join(
|
|
63
|
+
config.default.file.logs_dir,
|
|
64
|
+
config.default.file.plain.log_path,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if "{app_name}" in _logs_path:
|
|
68
|
+
_logs_path = _logs_path.format(app_name=config.app_name)
|
|
69
|
+
|
|
70
|
+
_handler_dict["sink"] = _logs_path
|
|
71
|
+
else:
|
|
72
|
+
raise ValueError(
|
|
73
|
+
"'sink' attribute is empty, required for any log handler except std and file handlers!"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if _handler_dict.get("level") is None:
|
|
77
|
+
if _handler_dict.get("error"):
|
|
78
|
+
_handler_dict["level"] = config.default.level.err
|
|
79
|
+
else:
|
|
80
|
+
_handler_dict["level"] = config.default.level.base
|
|
81
|
+
|
|
82
|
+
if (_handler_dict.get("custom_serialize") is None) and _handler_dict.get(
|
|
83
|
+
"serialize"
|
|
84
|
+
):
|
|
85
|
+
_handler_dict["custom_serialize"] = config.default.custom_serialize
|
|
86
|
+
|
|
87
|
+
if _handler_dict.get("custom_serialize"):
|
|
88
|
+
_handler_dict["serialize"] = False
|
|
89
|
+
_handler_dict["format"] = json_formatter
|
|
90
|
+
|
|
91
|
+
if (_handler_dict.get("format") is None) and (not _handler_dict.get("serialize")):
|
|
92
|
+
if _handler_dict.get("type") == LogHandlerTypeEnum.STD:
|
|
93
|
+
_handler_dict["format"] = config.default.std.format_str
|
|
94
|
+
else:
|
|
95
|
+
_handler_dict["format"] = config.default.format_str
|
|
96
|
+
|
|
97
|
+
if _handler_dict.get("filter") is None:
|
|
98
|
+
if _handler_dict.get("type") == LogHandlerTypeEnum.STD:
|
|
99
|
+
_handler_dict["filter"] = use_std_filter
|
|
100
|
+
elif _handler_dict.get("type") == LogHandlerTypeEnum.FILE:
|
|
101
|
+
if _handler_dict.get("serialize") or _handler_dict.get("custom_serialize"):
|
|
102
|
+
if _handler_dict.get("error"):
|
|
103
|
+
_handler_dict["filter"] = use_file_json_err_filter
|
|
104
|
+
else:
|
|
105
|
+
_handler_dict["filter"] = use_file_json_filter
|
|
106
|
+
else:
|
|
107
|
+
if _handler_dict.get("error"):
|
|
108
|
+
_handler_dict["filter"] = use_file_err_filter
|
|
109
|
+
else:
|
|
110
|
+
_handler_dict["filter"] = use_file_filter
|
|
111
|
+
else:
|
|
112
|
+
_handler_dict["filter"] = use_all_filter
|
|
113
|
+
|
|
114
|
+
if _handler_dict.get("backtrace") is None:
|
|
115
|
+
_handler_dict["backtrace"] = True
|
|
116
|
+
|
|
117
|
+
if (_handler_dict.get("diagnose") is None) and (
|
|
118
|
+
(_handler_dict.get("level") == LogLevelEnum.TRACE)
|
|
119
|
+
or (_handler_dict.get("level") == 5)
|
|
120
|
+
):
|
|
121
|
+
_handler_dict["diagnose"] = True
|
|
122
|
+
|
|
123
|
+
if (_handler_dict.get("colorize") is None) and (
|
|
124
|
+
_handler_dict.get("type") == LogHandlerTypeEnum.STD
|
|
125
|
+
):
|
|
126
|
+
_handler_dict["colorize"] = config.default.std.colorize
|
|
127
|
+
|
|
128
|
+
if _handler_dict.get("type") == LogHandlerTypeEnum.FILE:
|
|
129
|
+
if _handler_dict.get("enqueue") is None:
|
|
130
|
+
_handler_dict["enqueue"] = True
|
|
131
|
+
|
|
132
|
+
if _handler_dict.get("rotation") is None:
|
|
133
|
+
_handler_dict["rotation"] = Rotator(
|
|
134
|
+
rotate_size=config.default.file.rotate_size,
|
|
135
|
+
rotate_time=config.default.file.rotate_time,
|
|
136
|
+
).should_rotate
|
|
137
|
+
|
|
138
|
+
if _handler_dict.get("retention") is None:
|
|
139
|
+
_handler_dict["retention"] = config.default.file.retention
|
|
140
|
+
|
|
141
|
+
if _handler_dict.get("encoding") is None:
|
|
142
|
+
_handler_dict["encoding"] = config.default.file.encoding
|
|
143
|
+
|
|
144
|
+
_handler_dict.pop("type", None)
|
|
145
|
+
_handler_dict.pop("error", None)
|
|
146
|
+
_handler_dict.pop("custom_serialize", None)
|
|
147
|
+
_handler_dict.pop("enabled", None)
|
|
148
|
+
|
|
149
|
+
return _handler_dict
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
__all__ = [
|
|
153
|
+
"build_handler",
|
|
154
|
+
]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class LogHandlerTypeEnum(str, Enum):
|
|
5
|
+
STD = "STD"
|
|
6
|
+
FILE = "FILE"
|
|
7
|
+
SOCKET = "SOCKET"
|
|
8
|
+
HTTP = "HTTP"
|
|
9
|
+
SYSLOG = "SYSLOG"
|
|
10
|
+
QUEUE = "QUEUE"
|
|
11
|
+
MEMORY = "MEMORY"
|
|
12
|
+
NULL = "NULL"
|
|
13
|
+
CUSTOM = "CUSTOM"
|
|
14
|
+
UNKNOWN = "UNKNOWN"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class LogLevelEnum(str, Enum):
|
|
18
|
+
TRACE = "TRACE"
|
|
19
|
+
DEBUG = "DEBUG"
|
|
20
|
+
INFO = "INFO"
|
|
21
|
+
SUCCESS = "SUCCESS"
|
|
22
|
+
WARNING = "WARNING"
|
|
23
|
+
ERROR = "ERROR"
|
|
24
|
+
CRITICAL = "CRITICAL"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"LogHandlerTypeEnum",
|
|
29
|
+
"LogLevelEnum",
|
|
30
|
+
]
|
beans_logging/_core.py
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# Standard libraries
|
|
2
|
+
import os
|
|
3
|
+
import copy
|
|
4
|
+
import uuid
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
# Third-party libraries
|
|
9
|
+
import potato_util as utils
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from loguru import Logger
|
|
13
|
+
else:
|
|
14
|
+
from loguru._logger import Logger
|
|
15
|
+
from loguru import logger
|
|
16
|
+
from pydantic import validate_call
|
|
17
|
+
from potato_util import io as io_utils
|
|
18
|
+
|
|
19
|
+
# Internal modules
|
|
20
|
+
from .schemas import LogHandlerPM, LoguruHandlerPM
|
|
21
|
+
from .config import LoggerConfigPM
|
|
22
|
+
from ._builder import build_handler
|
|
23
|
+
from ._intercept import init_intercepter
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class LoggerLoader:
|
|
27
|
+
"""LoggerLoader class for setting up loguru logger.
|
|
28
|
+
|
|
29
|
+
Attributes:
|
|
30
|
+
_CONFIG_PATH (str): Default config file path. Default is '${PWD}/configs/logger.yml'.
|
|
31
|
+
|
|
32
|
+
handlers_map (dict[str, int]): Map of handler names to their IDs. Default is {'default.loguru_handler': 0}.
|
|
33
|
+
config (LoggerConfigPM): Main logger configuration model. Default is LoggerConfigPM().
|
|
34
|
+
config_path (str ): Path to logger configuration file. Default is _CONFIG_PATH.
|
|
35
|
+
|
|
36
|
+
Methods:
|
|
37
|
+
load() : Load logger handlers based on logger config.
|
|
38
|
+
_load_config_file(): Load logger config from file.
|
|
39
|
+
update_config() : Update current logger config with new config values.
|
|
40
|
+
remove_handler() : Remove handler from logger.
|
|
41
|
+
add_handler() : Add handler to logger.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
_CONFIG_PATH = os.path.join(os.getcwd(), "configs", "logger.yml")
|
|
45
|
+
|
|
46
|
+
@validate_call
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
config: LoggerConfigPM | dict[str, Any] | None = None,
|
|
50
|
+
config_path: str = _CONFIG_PATH,
|
|
51
|
+
auto_load: bool = False,
|
|
52
|
+
**kwargs,
|
|
53
|
+
) -> None:
|
|
54
|
+
|
|
55
|
+
self.handlers_map = {"default.loguru_handler": 0}
|
|
56
|
+
if not config:
|
|
57
|
+
config = LoggerConfigPM()
|
|
58
|
+
|
|
59
|
+
self.config = config
|
|
60
|
+
if kwargs:
|
|
61
|
+
self.update_config(config=kwargs)
|
|
62
|
+
|
|
63
|
+
self.config_path = config_path
|
|
64
|
+
|
|
65
|
+
if auto_load:
|
|
66
|
+
self.load()
|
|
67
|
+
|
|
68
|
+
@validate_call
|
|
69
|
+
def load(self, load_config_file: bool = True) -> "Logger":
|
|
70
|
+
"""Load logger handlers based on logger config.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
load_config_file (bool, optional): Whether to load config from file before loading handlers.
|
|
74
|
+
Default is True.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Logger: Main loguru logger instance.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
self.remove_handler()
|
|
81
|
+
if load_config_file:
|
|
82
|
+
self._load_config_file()
|
|
83
|
+
|
|
84
|
+
for _key, _handler in self.config.handlers.items():
|
|
85
|
+
self.add_handler(name=_key, handler=_handler)
|
|
86
|
+
|
|
87
|
+
init_intercepter(config=self.config)
|
|
88
|
+
return logger
|
|
89
|
+
|
|
90
|
+
def _load_config_file(self) -> None:
|
|
91
|
+
"""Load logger config from file."""
|
|
92
|
+
|
|
93
|
+
if self.config_path and os.path.isfile(self.config_path):
|
|
94
|
+
_config_data = io_utils.read_config_file(config_path=self.config_path)
|
|
95
|
+
if _config_data and ("logger" in _config_data):
|
|
96
|
+
_config_data = _config_data.get("logger", {})
|
|
97
|
+
if _config_data:
|
|
98
|
+
self.update_config(config=_config_data)
|
|
99
|
+
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
@validate_call
|
|
103
|
+
def update_config(self, config: dict[str, Any]) -> None:
|
|
104
|
+
"""Update current logger config with new config values.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
config (dict[str, Any], required): New config values to update current logger config.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
_config_dict = self.config.model_dump()
|
|
111
|
+
_merged_dict = utils.deep_merge(_config_dict, config)
|
|
112
|
+
try:
|
|
113
|
+
self.config = LoggerConfigPM(**_merged_dict)
|
|
114
|
+
except Exception:
|
|
115
|
+
logger.critical(
|
|
116
|
+
"Failed to load `config` argument into <class 'LoggerConfigPM'>."
|
|
117
|
+
)
|
|
118
|
+
raise
|
|
119
|
+
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
@validate_call
|
|
123
|
+
def remove_handler(self, handler: str | int | None = None) -> None:
|
|
124
|
+
"""Remove handler from logger.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
handler (str | int | None, optional): Handler name or ID to remove from logger.
|
|
128
|
+
Default is None, which removes all handlers.
|
|
129
|
+
|
|
130
|
+
Raises:
|
|
131
|
+
ValueError: If handler name or ID is not found in handlers map.
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
if handler:
|
|
135
|
+
if isinstance(handler, str):
|
|
136
|
+
if handler in self.handlers_map:
|
|
137
|
+
_handler_id = self.handlers_map.get(handler)
|
|
138
|
+
logger.remove(_handler_id)
|
|
139
|
+
self.handlers_map.pop(handler)
|
|
140
|
+
else:
|
|
141
|
+
raise ValueError(
|
|
142
|
+
f"Not found handler name '{handler}' in handlers map!"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
elif isinstance(handler, int):
|
|
146
|
+
if handler in self.handlers_map.values():
|
|
147
|
+
logger.remove(handler)
|
|
148
|
+
for _handler_name, _handler_id in list(self.handlers_map.items()):
|
|
149
|
+
if handler == _handler_id:
|
|
150
|
+
self.handlers_map.pop(_handler_name)
|
|
151
|
+
break
|
|
152
|
+
else:
|
|
153
|
+
raise ValueError(
|
|
154
|
+
f"Not found handler ID '{handler}' in handlers map!"
|
|
155
|
+
)
|
|
156
|
+
else:
|
|
157
|
+
logger.remove()
|
|
158
|
+
self.handlers_map.clear()
|
|
159
|
+
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
@validate_call
|
|
163
|
+
def add_handler(
|
|
164
|
+
self,
|
|
165
|
+
handler: LogHandlerPM | LoguruHandlerPM | dict[str, Any],
|
|
166
|
+
name: str | None = None,
|
|
167
|
+
) -> int | None:
|
|
168
|
+
"""Add handler to logger.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
handler (LogHandlerPM | LoguruHandlerPM | dict[str, Any], required): Handler model or dictionary to add to
|
|
172
|
+
logger.
|
|
173
|
+
name (str | None , optional): Handler name. Default is None.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
int | None: Handler ID if added successfully, otherwise None.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
_handler_id: int | None = None
|
|
180
|
+
try:
|
|
181
|
+
if isinstance(handler, dict):
|
|
182
|
+
handler = LogHandlerPM(**handler)
|
|
183
|
+
elif isinstance(handler, LoguruHandlerPM):
|
|
184
|
+
handler = LogHandlerPM(
|
|
185
|
+
**handler.model_dump(exclude_unset=True, exclude_none=True)
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
if handler.enabled:
|
|
189
|
+
_handler_dict = build_handler(handler=handler, config=self.config)
|
|
190
|
+
_sink = _handler_dict.get("sink")
|
|
191
|
+
if isinstance(_sink, (str, Path)):
|
|
192
|
+
_logs_dir = os.path.dirname(_sink)
|
|
193
|
+
if _logs_dir:
|
|
194
|
+
io_utils.create_dir(create_dir=_logs_dir)
|
|
195
|
+
|
|
196
|
+
_handler_id = logger.add(**_handler_dict)
|
|
197
|
+
if not name:
|
|
198
|
+
name = f"log_handler.{uuid.uuid4().hex}"
|
|
199
|
+
|
|
200
|
+
self.handlers_map[name] = _handler_id
|
|
201
|
+
|
|
202
|
+
except Exception:
|
|
203
|
+
logger.critical("Failed to add custom log handler to logger!")
|
|
204
|
+
raise
|
|
205
|
+
|
|
206
|
+
return _handler_id
|
|
207
|
+
|
|
208
|
+
# ATTRIBUTES
|
|
209
|
+
# handlers_map
|
|
210
|
+
@property
|
|
211
|
+
def handlers_map(self) -> dict[str, int]:
|
|
212
|
+
try:
|
|
213
|
+
return self.__handlers_map
|
|
214
|
+
except AttributeError:
|
|
215
|
+
raise AttributeError("`handlers_map` attribute is not set!")
|
|
216
|
+
|
|
217
|
+
@handlers_map.setter
|
|
218
|
+
def handlers_map(self, handlers_map: dict[str, int]) -> None:
|
|
219
|
+
if not isinstance(handlers_map, dict):
|
|
220
|
+
raise TypeError(
|
|
221
|
+
f"`handlers_map` attribute type {type(handlers_map)} is invalid, must be <dict>!."
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
self.__handlers_map = copy.deepcopy(handlers_map)
|
|
225
|
+
return
|
|
226
|
+
|
|
227
|
+
# handlers_map
|
|
228
|
+
|
|
229
|
+
# config
|
|
230
|
+
@property
|
|
231
|
+
def config(self) -> LoggerConfigPM:
|
|
232
|
+
try:
|
|
233
|
+
return self.__config
|
|
234
|
+
except AttributeError:
|
|
235
|
+
self.__config = LoggerConfigPM()
|
|
236
|
+
|
|
237
|
+
return self.__config
|
|
238
|
+
|
|
239
|
+
@config.setter
|
|
240
|
+
def config(self, config: LoggerConfigPM | dict[str, Any]) -> None:
|
|
241
|
+
if (not isinstance(config, LoggerConfigPM)) and (not isinstance(config, dict)):
|
|
242
|
+
raise TypeError(
|
|
243
|
+
f"`config` attribute type {type(config)} is invalid, must be a <class 'LoggerConfigPM'> or <dict>!"
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
if isinstance(config, dict):
|
|
247
|
+
config = LoggerConfigPM(**config)
|
|
248
|
+
elif isinstance(config, LoggerConfigPM):
|
|
249
|
+
config = config.model_copy(deep=True)
|
|
250
|
+
|
|
251
|
+
self.__config = config
|
|
252
|
+
return
|
|
253
|
+
|
|
254
|
+
# config
|
|
255
|
+
|
|
256
|
+
# config_path
|
|
257
|
+
@property
|
|
258
|
+
def config_path(self) -> str:
|
|
259
|
+
try:
|
|
260
|
+
return self.__config_path
|
|
261
|
+
except AttributeError:
|
|
262
|
+
self.__config_path = LoggerLoader._CONFIG_PATH
|
|
263
|
+
|
|
264
|
+
return self.__config_path
|
|
265
|
+
|
|
266
|
+
@config_path.setter
|
|
267
|
+
def config_path(self, config_path: str) -> None:
|
|
268
|
+
if not isinstance(config_path, str):
|
|
269
|
+
raise TypeError(
|
|
270
|
+
f"`config_path` attribute type {type(config_path)} is invalid, must be a <str>!"
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
config_path = config_path.strip()
|
|
274
|
+
if config_path == "":
|
|
275
|
+
raise ValueError("`config_path` attribute value is empty!")
|
|
276
|
+
|
|
277
|
+
if (
|
|
278
|
+
(not config_path.lower().endswith((".yml", ".yaml")))
|
|
279
|
+
and (not config_path.lower().endswith(".json"))
|
|
280
|
+
and (not config_path.lower().endswith(".toml"))
|
|
281
|
+
):
|
|
282
|
+
raise ValueError(
|
|
283
|
+
f"`config_path` attribute value '{config_path}' is invalid, "
|
|
284
|
+
f"file must be '.yml', '.yaml', '.json' or '.toml' format!"
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
self.__config_path = config_path
|
|
288
|
+
|
|
289
|
+
# config_path
|
|
290
|
+
# ATTRIBUTES
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
__all__ = [
|
|
294
|
+
"LoggerLoader",
|
|
295
|
+
]
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import logging
|
|
3
|
+
from logging import LogRecord, Handler
|
|
4
|
+
|
|
5
|
+
from loguru import logger
|
|
6
|
+
from pydantic import validate_call
|
|
7
|
+
|
|
8
|
+
from .config import LoggerConfigPM
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InterceptHandler(Handler):
|
|
12
|
+
"""A handler class that intercepts logs from standard logging and redirects them to loguru logger.
|
|
13
|
+
|
|
14
|
+
Inherits:
|
|
15
|
+
Handler: Handler class from standard logging.
|
|
16
|
+
|
|
17
|
+
Overrides:
|
|
18
|
+
emit(): Handle intercepted log record.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def emit(self, record: LogRecord) -> None:
|
|
22
|
+
"""Handle intercepted log record.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
record (LogRecord, required): Log needs to be handled.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
# Get corresponding Loguru level if it exists.
|
|
29
|
+
try:
|
|
30
|
+
_level: str | int = logger.level(record.levelname).name
|
|
31
|
+
except ValueError:
|
|
32
|
+
_level = record.levelno
|
|
33
|
+
|
|
34
|
+
# Find caller from where originated the logged message.
|
|
35
|
+
_frame, _depth = inspect.currentframe(), 0
|
|
36
|
+
while _frame and (_depth == 0 or _frame.f_code.co_filename == logging.__file__):
|
|
37
|
+
_frame = _frame.f_back
|
|
38
|
+
_depth += 1
|
|
39
|
+
|
|
40
|
+
logger.opt(depth=_depth, exception=record.exc_info).log(
|
|
41
|
+
_level, record.getMessage()
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@validate_call
|
|
48
|
+
def init_intercepter(config: LoggerConfigPM) -> None:
|
|
49
|
+
"""Initialize log interceptor based on provided config.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
config (LoggerConfigPM, required): Main logger config model to use intercepter settings.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
_intercept_handler = InterceptHandler()
|
|
56
|
+
|
|
57
|
+
# Intercepting all logs from standard (root logger) logging:
|
|
58
|
+
logging.basicConfig(handlers=[_intercept_handler], level=0, force=True)
|
|
59
|
+
|
|
60
|
+
_intercepted_modules = set()
|
|
61
|
+
_muted_modules = set()
|
|
62
|
+
|
|
63
|
+
if config.intercept.enabled:
|
|
64
|
+
for _module_name in list(logging.root.manager.loggerDict.keys()):
|
|
65
|
+
if config.intercept.only_base:
|
|
66
|
+
_module_name = _module_name.split(".")[0]
|
|
67
|
+
|
|
68
|
+
if (_module_name not in _intercepted_modules) and (
|
|
69
|
+
_module_name not in config.intercept.ignore_modules
|
|
70
|
+
):
|
|
71
|
+
_logger = logging.getLogger(_module_name)
|
|
72
|
+
_logger.handlers = [_intercept_handler]
|
|
73
|
+
_logger.propagate = False
|
|
74
|
+
_intercepted_modules.add(_module_name)
|
|
75
|
+
|
|
76
|
+
for _include_module_name in config.intercept.include_modules:
|
|
77
|
+
_logger = logging.getLogger(_include_module_name)
|
|
78
|
+
_logger.handlers = [_intercept_handler]
|
|
79
|
+
# _logger.propagate = False
|
|
80
|
+
|
|
81
|
+
if _include_module_name not in _intercepted_modules:
|
|
82
|
+
_intercepted_modules.add(_include_module_name)
|
|
83
|
+
|
|
84
|
+
for _mute_module_name in config.intercept.mute_modules:
|
|
85
|
+
_logger = logging.getLogger(_mute_module_name)
|
|
86
|
+
_logger.handlers = []
|
|
87
|
+
_logger.propagate = False
|
|
88
|
+
_logger.disabled = True
|
|
89
|
+
|
|
90
|
+
if _mute_module_name in _intercepted_modules:
|
|
91
|
+
_intercepted_modules.remove(_mute_module_name)
|
|
92
|
+
|
|
93
|
+
if _mute_module_name not in _muted_modules:
|
|
94
|
+
_muted_modules.add(_mute_module_name)
|
|
95
|
+
|
|
96
|
+
logger.trace(
|
|
97
|
+
f"Intercepted modules: {list(_intercepted_modules)}; Muted modules: {list(_muted_modules)};"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
__all__ = [
|
|
104
|
+
"InterceptHandler",
|
|
105
|
+
"init_intercepter",
|
|
106
|
+
]
|
beans_logging/auto.py
CHANGED
|
@@ -1,24 +1,15 @@
|
|
|
1
1
|
# flake8: noqa
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
3
|
from . import *
|
|
6
4
|
|
|
7
|
-
logger_loader: LoggerLoader
|
|
8
|
-
_DISABLE_DEFAULT_LOGGER = (
|
|
9
|
-
str(os.getenv("BEANS_LOGGING_DISABLE_DEFAULT")).strip().lower()
|
|
10
|
-
)
|
|
11
|
-
if (_DISABLE_DEFAULT_LOGGER != "true") and (_DISABLE_DEFAULT_LOGGER != "1"):
|
|
12
|
-
logger_loader: LoggerLoader = LoggerLoader()
|
|
13
|
-
logger: Logger = logger_loader.load()
|
|
5
|
+
logger_loader: LoggerLoader = LoggerLoader(auto_load=True)
|
|
14
6
|
|
|
15
7
|
|
|
16
8
|
__all__ = [
|
|
9
|
+
"__version__",
|
|
10
|
+
"LoggerConfigPM",
|
|
17
11
|
"Logger",
|
|
18
12
|
"logger",
|
|
19
13
|
"LoggerLoader",
|
|
20
14
|
"logger_loader",
|
|
21
|
-
"LoggerConfigPM",
|
|
22
|
-
"WarnEnum",
|
|
23
|
-
"__version__",
|
|
24
15
|
]
|