custom-python-logger 2.0.0__tar.gz → 2.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.
- {custom_python_logger-2.0.0/custom_python_logger.egg-info → custom_python_logger-2.0.2}/PKG-INFO +1 -1
- custom_python_logger-2.0.2/custom_python_logger/consts.py +16 -0
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/custom_python_logger/logger.py +85 -67
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/custom_python_logger/usage_example.py +1 -1
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2/custom_python_logger.egg-info}/PKG-INFO +1 -1
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/custom_python_logger.egg-info/SOURCES.txt +1 -0
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/setup.py +1 -1
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/LICENSE +0 -0
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/MANIFEST.in +0 -0
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/README.md +0 -0
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/custom_python_logger/__init__.py +1 -1
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/custom_python_logger.egg-info/dependency_links.txt +0 -0
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/custom_python_logger.egg-info/requires.txt +0 -0
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/custom_python_logger.egg-info/top_level.txt +0 -0
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/pyproject.toml +0 -0
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/requirements.txt +0 -0
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/setup.cfg +0 -0
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/tests/test_logger.py +0 -0
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/tests/test_logger_pytest.py +0 -0
- {custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/tests/test_usage_example_pytest.py +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
LOG_COLORS = {
|
|
4
|
+
"DEBUG": "white",
|
|
5
|
+
"INFO": "green",
|
|
6
|
+
"WARNING": "yellow",
|
|
7
|
+
"STEP": "blue",
|
|
8
|
+
"ERROR": "red,bold",
|
|
9
|
+
"EXCEPTION": "light_red,bold",
|
|
10
|
+
"CRITICAL": "red,bg_white",
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CustomLoggerLevel(Enum):
|
|
15
|
+
EXCEPTION = 45
|
|
16
|
+
STEP = 25
|
|
@@ -2,20 +2,39 @@ import json
|
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
4
|
import time
|
|
5
|
-
from logging import Logger
|
|
5
|
+
from logging import Logger
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Any, Callable, Optional
|
|
8
8
|
|
|
9
9
|
import yaml
|
|
10
10
|
from colorlog import ColoredFormatter
|
|
11
11
|
|
|
12
|
+
from custom_python_logger.consts import LOG_COLORS, CustomLoggerLevel
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
def json_pretty_format(
|
|
16
|
+
data: Any, indent: int = 4, sort_keys: bool = True, default: Callable = None
|
|
17
|
+
) -> str:
|
|
18
|
+
return json.dumps(data, indent=indent, sort_keys=sort_keys, default=default)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def yaml_pretty_format(
|
|
22
|
+
data: Any, indent: int = 4, sort_keys: bool = False, allow_unicode=True
|
|
23
|
+
) -> str:
|
|
24
|
+
return yaml.dump(
|
|
25
|
+
data, sort_keys=sort_keys, indent=indent, allow_unicode=allow_unicode
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_project_path_by_file(markers: Optional[list[str]] = None) -> Path:
|
|
30
|
+
if not markers:
|
|
31
|
+
markers = ["pyproject.toml", "setup.py", ".git", "requirements.txt", ".gitignore", ".github", ".gitlab"]
|
|
14
32
|
path = Path(__file__).resolve()
|
|
15
33
|
for parent in path.parents:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
34
|
+
for marker in markers:
|
|
35
|
+
if (parent / marker).exists():
|
|
36
|
+
return parent
|
|
37
|
+
raise RuntimeError(f'Project root with one of the markers: "{markers}" not found.')
|
|
19
38
|
|
|
20
39
|
|
|
21
40
|
def print_before_logger(project_name: str) -> None:
|
|
@@ -30,16 +49,55 @@ def print_before_logger(project_name: str) -> None:
|
|
|
30
49
|
|
|
31
50
|
class CustomLoggerAdapter(logging.LoggerAdapter):
|
|
32
51
|
def exception(self, msg: str, *args, **kwargs):
|
|
33
|
-
|
|
34
|
-
logging.addLevelName(level_no, "EXCEPTION")
|
|
52
|
+
logging.addLevelName(CustomLoggerLevel.EXCEPTION.value, "EXCEPTION")
|
|
35
53
|
kwargs.setdefault("stacklevel", 2)
|
|
36
|
-
self.log(
|
|
54
|
+
self.log(CustomLoggerLevel.EXCEPTION.value, msg, *args, exc_info=True, **kwargs)
|
|
37
55
|
|
|
38
56
|
def step(self, msg: str, *args, **kwargs):
|
|
39
|
-
|
|
40
|
-
logging.addLevelName(level_no, "STEP")
|
|
57
|
+
logging.addLevelName(CustomLoggerLevel.STEP.value, "STEP")
|
|
41
58
|
kwargs.setdefault("stacklevel", 2)
|
|
42
|
-
self.log(
|
|
59
|
+
self.log(CustomLoggerLevel.STEP.value, msg, *args, exc_info=False, **kwargs)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def clear_existing_handlers(logger: Logger) -> None:
|
|
63
|
+
for handler in logger.handlers[:]:
|
|
64
|
+
logger.removeHandler(handler)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def add_file_handler_if_specified(
|
|
68
|
+
logger: Logger,
|
|
69
|
+
log_file: bool,
|
|
70
|
+
log_file_path: Optional[str],
|
|
71
|
+
log_format: str,
|
|
72
|
+
) -> None:
|
|
73
|
+
if log_file and log_file_path is not None:
|
|
74
|
+
log_file_formatter = logging.Formatter(log_format)
|
|
75
|
+
|
|
76
|
+
# Create directory if it doesn't exist
|
|
77
|
+
log_dir = os.path.dirname(log_file_path)
|
|
78
|
+
if log_dir and not os.path.exists(log_dir):
|
|
79
|
+
os.makedirs(log_dir)
|
|
80
|
+
|
|
81
|
+
file_handler = logging.FileHandler(log_file_path)
|
|
82
|
+
|
|
83
|
+
file_handler.setFormatter(log_file_formatter)
|
|
84
|
+
logger.addHandler(file_handler)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def add_console_handler_if_specified(
|
|
88
|
+
logger: Logger,
|
|
89
|
+
console_output: bool,
|
|
90
|
+
log_format: str
|
|
91
|
+
):
|
|
92
|
+
if console_output:
|
|
93
|
+
log_console_formatter = ColoredFormatter(
|
|
94
|
+
"%(log_color)s " + log_format,
|
|
95
|
+
log_colors=LOG_COLORS,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
console_handler = logging.StreamHandler()
|
|
99
|
+
console_handler.setFormatter(log_console_formatter)
|
|
100
|
+
logger.addHandler(console_handler)
|
|
43
101
|
|
|
44
102
|
|
|
45
103
|
def configure_logging(
|
|
@@ -67,49 +125,26 @@ def configure_logging(
|
|
|
67
125
|
root_logger = logging.getLogger()
|
|
68
126
|
root_logger.setLevel(log_level)
|
|
69
127
|
|
|
70
|
-
|
|
71
|
-
for handler in root_logger.handlers[:]:
|
|
72
|
-
root_logger.removeHandler(handler)
|
|
128
|
+
clear_existing_handlers(logger=root_logger)
|
|
73
129
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if log_dir and not os.path.exists(log_dir):
|
|
81
|
-
os.makedirs(log_dir)
|
|
82
|
-
|
|
83
|
-
file_handler = logging.FileHandler(log_file_path)
|
|
84
|
-
|
|
85
|
-
file_handler.setFormatter(log_file_formatter)
|
|
86
|
-
root_logger.addHandler(file_handler)
|
|
87
|
-
|
|
88
|
-
# Add console handler if specified
|
|
89
|
-
if console_output:
|
|
90
|
-
# log_console_formatter = logging.Formatter('%(log_color)s ' + log_format)
|
|
91
|
-
log_console_formatter = ColoredFormatter(
|
|
92
|
-
"%(log_color)s " + log_format,
|
|
93
|
-
log_colors={
|
|
94
|
-
"DEBUG": "white",
|
|
95
|
-
"INFO": "green",
|
|
96
|
-
"WARNING": "yellow",
|
|
97
|
-
"STEP": "blue",
|
|
98
|
-
"ERROR": "red,bold",
|
|
99
|
-
"EXCEPTION": "light_red,bold",
|
|
100
|
-
"CRITICAL": "red,bg_white",
|
|
101
|
-
},
|
|
102
|
-
)
|
|
130
|
+
add_file_handler_if_specified(
|
|
131
|
+
logger=root_logger,
|
|
132
|
+
log_file=log_file,
|
|
133
|
+
log_file_path=log_file_path,
|
|
134
|
+
log_format=log_format,
|
|
135
|
+
)
|
|
103
136
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
137
|
+
add_console_handler_if_specified(
|
|
138
|
+
logger=root_logger,
|
|
139
|
+
console_output=console_output,
|
|
140
|
+
log_format=log_format,
|
|
141
|
+
)
|
|
107
142
|
|
|
108
143
|
|
|
109
144
|
def build_logger(
|
|
110
145
|
project_name: str,
|
|
111
146
|
extra: Optional[dict[str, Any]] = None,
|
|
112
|
-
log_format: str = "%(asctime)s | %(levelname)-
|
|
147
|
+
log_format: str = "%(asctime)s | %(levelname)-9s | l.%(levelno)s | %(name)s | %(filename)s:%(lineno)s | %(message)s",
|
|
113
148
|
log_level: int = logging.INFO,
|
|
114
149
|
log_file: bool = False,
|
|
115
150
|
log_file_path: str = None,
|
|
@@ -128,12 +163,9 @@ def build_logger(
|
|
|
128
163
|
log_file_path: Path to log file (if None, no file logging)
|
|
129
164
|
console_output: Whether to output logs to console
|
|
130
165
|
utc: Whether to use UTC time for log timestamps
|
|
131
|
-
|
|
132
166
|
Returns:
|
|
133
167
|
Configured logger
|
|
134
168
|
"""
|
|
135
|
-
print_before_logger(project_name=project_name)
|
|
136
|
-
|
|
137
169
|
if not log_file_path:
|
|
138
170
|
log_file_path = f"{get_project_path_by_file()}/logs/{project_name}.log"
|
|
139
171
|
log_file_path = log_file_path.lower().replace(" ", "_")
|
|
@@ -147,7 +179,7 @@ def build_logger(
|
|
|
147
179
|
utc=utc,
|
|
148
180
|
)
|
|
149
181
|
|
|
150
|
-
logger = logging.getLogger()
|
|
182
|
+
logger = logging.getLogger(project_name)
|
|
151
183
|
|
|
152
184
|
if log_level is not None:
|
|
153
185
|
logger.setLevel(log_level)
|
|
@@ -155,19 +187,5 @@ def build_logger(
|
|
|
155
187
|
return CustomLoggerAdapter(logger, extra)
|
|
156
188
|
|
|
157
189
|
|
|
158
|
-
def get_logger(name: str) -> CustomLoggerAdapter:
|
|
159
|
-
return CustomLoggerAdapter(logging.getLogger(name),
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def json_pretty_format(
|
|
163
|
-
data: Any, indent: int = 4, sort_keys: bool = True, default: Callable = None
|
|
164
|
-
) -> str:
|
|
165
|
-
return json.dumps(data, indent=indent, sort_keys=sort_keys, default=default)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
def yaml_pretty_format(
|
|
169
|
-
data: Any, indent: int = 4, sort_keys: bool = False, allow_unicode=True
|
|
170
|
-
) -> str:
|
|
171
|
-
return yaml.dump(
|
|
172
|
-
data, sort_keys=sort_keys, indent=indent, allow_unicode=allow_unicode
|
|
173
|
-
)
|
|
190
|
+
def get_logger(name: str, extra: Optional[dict] = None) -> CustomLoggerAdapter:
|
|
191
|
+
return CustomLoggerAdapter(logging.getLogger(name), extra=extra)
|
{custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/custom_python_logger/usage_example.py
RENAMED
|
@@ -5,7 +5,7 @@ from custom_python_logger import build_logger, get_logger
|
|
|
5
5
|
|
|
6
6
|
class LoggerTest:
|
|
7
7
|
def __init__(self):
|
|
8
|
-
self.logger = get_logger(self.__class__.__name__)
|
|
8
|
+
self.logger = get_logger(self.__class__.__name__, extra={"class": self.__class__.__name__})
|
|
9
9
|
|
|
10
10
|
def main(self):
|
|
11
11
|
self.logger.debug("Hello World")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/custom_python_logger.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{custom_python_logger-2.0.0 → custom_python_logger-2.0.2}/tests/test_usage_example_pytest.py
RENAMED
|
File without changes
|