Skip to content

Commit c7910bb

Browse files
committed
update logger
- add rotating log files - fix up handler levels - import json default function
1 parent 926465c commit c7910bb

File tree

1 file changed

+40
-43
lines changed

1 file changed

+40
-43
lines changed

pp/log.py

Lines changed: 40 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
usage examples to initialise a logger:
55
```python
66
# 1. initialise logger to stderr:
7-
logger = getLogger('my_logger', level=logging.DEBUG, print_stream=sys.stderr)
7+
logger = getLogger('my_logger', level=logging.DEBUG, stream=sys.stderr)
88
99
# 2. initialise logger to file:
1010
logger = getLogger('my_logger', level=logging.DEBUG, filename='my_log.log')
1111
1212
# 3. initialise logger to both stderr and file:
13-
logger = getLogger('my_logger', level=logging.DEBUG, print_stream=sys.stderr, filename='my_log.log')
13+
logger = getLogger('my_logger', level=logging.DEBUG, stream=sys.stderr, files={LogLevel.INFO: 'info.log'})
1414
```
1515
1616
usage examples to log messages:
@@ -35,8 +35,19 @@
3535

3636
from pp.pp import _json_default
3737

38+
class LogLevel:
39+
'An enum type for log levels.'
40+
CRITICAL = logging.CRITICAL
41+
ERROR = logging.ERROR
42+
WARNING = logging.WARNING
43+
INFO = logging.INFO
44+
DEBUG = logging.DEBUG
45+
NOTSET = logging.NOTSET
46+
47+
3848
LOG_ROOT_NAME = 'root'
3949

50+
4051
class LogFormatter(logging.Formatter):
4152
'Custom log formatter that formats log messages as JSON, aka "Structured Logging".'
4253

@@ -56,10 +67,7 @@ def format(self, record) -> str:
5667
{
5768
'timestamp': datetime.now().isoformat(),
5869
'msg': record.msg,
59-
'data': {
60-
**({'args': args} if args else {}),
61-
**(kwargs if kwargs else {}),
62-
}
70+
'data': {'args': args} if args else {} | kwargs or {}
6371
},
6472
default=_json_default,
6573
)
@@ -75,9 +83,10 @@ def _getLogger(name: str, level: int = logging.CRITICAL, handlers: list[logging.
7583
- the same log level is applied to all handlers.
7684
'''
7785

78-
# create the logger
86+
# create the root logger
7987
logger = logging.getLogger(LOG_ROOT_NAME)
8088
logger.setLevel(level)
89+
8190
# close/remove any existing handlers
8291
while logger.handlers:
8392
for handler in logger.handlers:
@@ -96,7 +105,6 @@ def _getLogger(name: str, level: int = logging.CRITICAL, handlers: list[logging.
96105

97106
# add the new handlers
98107
for handler in handlers:
99-
handler.setLevel(level)
100108
logger.addHandler(handler)
101109

102110
if logger.handlers:
@@ -106,44 +114,33 @@ def _getLogger(name: str, level: int = logging.CRITICAL, handlers: list[logging.
106114
return logger
107115

108116

109-
@dataclass
110-
class Logger:
111-
'''
112-
A class to create a logger with custom handlers and a custom formatter.
113-
Logger(name, level, handlers, print_stream, filename)
114-
'''
115-
name: str
116-
level: int = logging.CRITICAL
117-
print_stream: io.TextIOBase = field(default=None)
118-
filename: str = field(default=None)
119-
handlers: list[logging.Handler] = field(init=False, default_factory=list)
120-
121-
def __post_init__(self, filename: str = None):
122-
if self.print_stream:
123-
self.handlers.append(logging.StreamHandler(self.print_stream))
124-
if self.filename:
125-
self.handlers.append(logging.FileHandler(self.filename))
126-
127-
def getLogger(self) -> logging.Logger:
128-
return _getLogger(self.name, self.level, handlers=self.handlers)
129-
130-
@property
131-
def logger(self) -> logging.Logger:
132-
return self.getLogger()
133-
134-
135-
def getLogger(name: str, level: int = logging.CRITICAL, print_stream: io.TextIOBase = None, filename: str = None) -> logging.Logger:
117+
def getLogger(
118+
name: str,
119+
level: int = logging.INFO,
120+
stream: io.TextIOBase = sys.stderr,
121+
files: dict[LogLevel, str] = dict(),
122+
) -> logging.Logger:
136123
'''
137124
Creates a logger with the given name, level, and handlers.
138-
- if `print_stream` is provided, the logger will output logs to it.
139-
- if `filename` is provided, the logger will output logs to it.
140-
- if both are provided, the logger will output logs to both.
141-
- if neither are provided, the logger will not output any logs.
125+
- `name` is the name of the logger.
126+
- `stream` is the output stream for the logger (default is STDERR).
127+
- `files` is a dictionary of log levels and filenames for file handlers.
128+
- The keys are log levels (e.g., LogLevel.INFO, LogLevel.DEBUG).
129+
- The values are the filenames to log to at the corresponding level.
130+
- The file handlers will use `TimedRotatingFileHandler` to rotate logs at midnight and keep 7 backups.
131+
- `level` is the log level for the logger and all handlers (default is INFO).
142132
'''
143133
handlers = []
144-
if print_stream:
145-
handlers.append(logging.StreamHandler(print_stream))
146-
if filename:
147-
handlers.append(logging.FileHandler(filename))
134+
if stream:
135+
handler = logging.StreamHandler(stream)
136+
handler.setLevel(level)
137+
handlers.append(handler)
138+
139+
for flevel, filename in files.items():
140+
handler = logging.handlers.TimedRotatingFileHandler(
141+
filename, when='midnight', backupCount=7, encoding='utf-8',
142+
)
143+
handler.setLevel(flevel)
144+
handlers.append(handler)
148145

149146
return _getLogger(name, level, handlers)

0 commit comments

Comments
 (0)