-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlogger.py
More file actions
149 lines (131 loc) · 4.49 KB
/
logger.py
File metadata and controls
149 lines (131 loc) · 4.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
from __future__ import annotations
import sys
from logging import Logger
from pathlib import Path
from typing import Optional, Union
from loguru import logger as _logger
PathLike = Union[str, Path]
def create_logger(
log_dir: Optional[PathLike] = "logs",
level: str = "INFO",
dev_mode: bool = False,
rotation: str = "10 MB",
retention: str = "14 days",
compression: str = "zip",
enqueue: bool = True,
diagnose: bool = False,
) -> Logger:
"""configure and return a production-grade loguru logger.
provides structured logging, rotation, multiprocessing safety, and clean console output suitable for pipelines.
Args:
log_dir (Optional[PathLike], optional): directory where logs are stored. Defaults to "logs".
level (str, optional): base log level. Only applies to stdout. Defaults to "INFO".
dev_mode (bool, optional): enable trace/debug in console. Defaults to False.
rotation (str, optional): file rotation policy. Defaults to "10 MB".
retention (str, optional): log retention policy. Defaults to "14 days".
compression (str, optional): compression for rotated logs. Defaults to "zip".
enqueue (bool, optional): queue logs for multiprocessing safety. Defaults to True.
diagnose (bool, optional): include variable values in tracebacks. Defaults to False.
Returns:
logger: configured logger proxy
"""
_logger.remove()
# level styling for console readability
level_styles = {
"TRACE": {"color": "<white><dim>"},
"DEBUG": {"color": "<white>"},
"INFO": {"color": "<blue>"},
"SUCCESS": {"color": "<green><bold>"},
"WARNING": {"color": "<yellow>"},
"ERROR": {"color": "<red><bold>"},
"CRITICAL": {"color": "<bg red><fg black><bold>"},
}
for name, style in level_styles.items():
_logger.level(name, **style)
# console log format
console_fmt = (
"<white><dim>{time:%I:%M %p %z}</dim></white> | "
"<white><dim>{module:<10}</dim></white> | "
"<level>{level:<9}</level> | <level>{message}</level>"
)
# file log format
file_fmt = (
"{time:YYYY-MM-DD HH:mm:ss.SSS Z} | {level:<9} | {module:<12} | {message}"
)
if log_dir:
Path(log_dir).mkdir(parents=True, exist_ok=True)
# stdout: operational output
# in case dev mode is enabled
# also log debug and trace
stdout_levels = {"INFO", "SUCCESS"}
if dev_mode:
stdout_levels |= {"DEBUG", "TRACE"}
_logger.add(
sys.stdout,
filter=lambda r: r["level"].name in stdout_levels,
format=console_fmt,
level=level,
colorize=True,
enqueue=enqueue,
backtrace=False,
diagnose=diagnose,
)
# stderr: problems & diagnostics
_logger.add(
sys.stderr,
level="WARNING",
format=console_fmt,
colorize=True,
enqueue=enqueue,
backtrace=True,
diagnose=diagnose,
)
if log_dir:
# warning+ structured logs (alerts, monitoring)
_logger.add(
Path(log_dir) / "warning_plus.jsonl",
level="WARNING",
serialize=True,
rotation=rotation,
retention=retention,
compression=compression,
enqueue=enqueue,
)
# info+ structured logs (analytics & ops)
_logger.add(
Path(log_dir) / "info_plus.jsonl",
level="INFO",
serialize=True,
rotation=rotation,
retention=retention,
compression=compression,
enqueue=enqueue,
)
# trace/debug developer logs
_logger.add(
Path(log_dir) / "trace_debug.log",
level="TRACE",
filter=lambda r: r["level"].name in {"TRACE", "DEBUG"},
format=file_fmt,
rotation=rotation,
retention=retention,
compression=compression,
enqueue=enqueue,
)
# full audit trail
_logger.add(
Path(log_dir) / "all.log",
level="TRACE",
format=file_fmt,
rotation=rotation,
retention=retention,
compression=compression,
enqueue=enqueue,
)
# ensure caller module is reported correctly
class LoggerProxy:
def __init__(self, base):
self._base = base
def __getattr__(self, name):
return getattr(self._base.opt(depth=1), name)
return LoggerProxy(_logger)