Skip to content

Commit cf88c44

Browse files
authored
Merge pull request #61 from FoamyGuy/formatters
Adding Formatter and example including timestamp
2 parents ef3336d + c9ae545 commit cf88c44

File tree

2 files changed

+130
-2
lines changed

2 files changed

+130
-2
lines changed

adafruit_logging.py

+82-2
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464

6565
try:
6666
# pylint: disable=deprecated-class
67-
from typing import Optional, Hashable
67+
from typing import Optional, Hashable, Dict
6868
from typing_extensions import Protocol
6969

7070
class WriteableStream(Protocol):
@@ -148,12 +148,85 @@ def _logRecordFactory(name, level, msg, args):
148148
return LogRecord(name, level, _level_for(level), msg, time.monotonic(), args)
149149

150150

151+
class Formatter:
152+
"""
153+
Responsible for converting a LogRecord to an output string to be
154+
interpreted by a human or external system.
155+
156+
Only implements a sub-set of CPython logging.Formatter behavior,
157+
but retains all the same arguments in order to match the API.
158+
159+
The only init arguments currently supported are: fmt, defaults and
160+
style. All others are currently ignored
161+
162+
The only two styles currently supported are '%' and '{'. The default
163+
style is '{'
164+
"""
165+
166+
def __init__( # pylint: disable=too-many-arguments
167+
self,
168+
fmt: Optional[str] = None,
169+
datefmt: Optional[str] = None,
170+
style: str = "%",
171+
validate: bool = True,
172+
defaults: Dict = None,
173+
):
174+
self.fmt = fmt
175+
self.datefmt = datefmt
176+
self.style = style
177+
if self.style not in ("{", "%"):
178+
raise ValueError(
179+
"Only '%' and '{' formatting style are supported at this time."
180+
)
181+
182+
self.validate = validate
183+
self.defaults = defaults
184+
185+
def format(self, record: LogRecord) -> str:
186+
"""
187+
Format the given LogRecord into an output string
188+
"""
189+
if self.fmt is None:
190+
return record.msg
191+
192+
vals = {
193+
"name": record.name,
194+
"levelno": record.levelno,
195+
"levelname": record.levelname,
196+
"message": record.msg,
197+
"created": record.created,
198+
"args": record.args,
199+
}
200+
if "{asctime}" in self.fmt or "%(asctime)s" in self.fmt:
201+
now = time.localtime()
202+
# pylint: disable=line-too-long
203+
vals[
204+
"asctime"
205+
] = f"{now.tm_year}-{now.tm_mon:02d}-{now.tm_mday:02d} {now.tm_hour:02d}:{now.tm_min:02d}:{now.tm_sec:02d}"
206+
207+
if self.defaults:
208+
for key, val in self.defaults.items():
209+
if key not in vals:
210+
vals[key] = val
211+
212+
if self.style not in ("{", "%"):
213+
raise ValueError(
214+
"Only '%' and '{' formatting style are supported at this time."
215+
)
216+
217+
if self.style == "%":
218+
return self.fmt % vals
219+
220+
return self.fmt.format(**vals)
221+
222+
151223
class Handler:
152224
"""Base logging message handler."""
153225

154226
def __init__(self, level: int = NOTSET) -> None:
155227
"""Create Handler instance"""
156228
self.level = level
229+
self.formatter = None
157230

158231
def setLevel(self, level: int) -> None:
159232
"""
@@ -167,7 +240,8 @@ def format(self, record: LogRecord) -> str:
167240
168241
:param record: The record (message object) to be logged
169242
"""
170-
243+
if self.formatter:
244+
return self.formatter.format(record)
171245
return f"{record.created:<0.3f}: {record.levelname} - {record.msg}"
172246

173247
def emit(self, record: LogRecord) -> None:
@@ -182,6 +256,12 @@ def emit(self, record: LogRecord) -> None:
182256
def flush(self) -> None:
183257
"""Placeholder for flush function in subclasses."""
184258

259+
def setFormatter(self, formatter: Formatter) -> None:
260+
"""
261+
Set the Formatter to be used by this Handler.
262+
"""
263+
self.formatter = formatter
264+
185265

186266
# pylint: disable=too-few-public-methods
187267
class StreamHandler(Handler):

examples/logging_formatter_example.py

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# SPDX-FileCopyrightText: 2024 Tim Cocks
2+
# SPDX-License-Identifier: MIT
3+
4+
5+
"""Illustrate usage of default and custom Formatters including
6+
one with timestamps."""
7+
8+
import adafruit_logging as logging
9+
10+
# To test on CPython, un-comment below and comment out above
11+
# import logging
12+
13+
14+
logger = logging.getLogger("example")
15+
logger.setLevel(logging.INFO)
16+
print_handler = logging.StreamHandler()
17+
logger.addHandler(print_handler)
18+
19+
default_formatter = logging.Formatter()
20+
21+
print_handler.setFormatter(default_formatter)
22+
logger.info("Default formatter example")
23+
24+
25+
timestamp_formatter = logging.Formatter(fmt="%(asctime)s %(levelname)s: %(message)s")
26+
print_handler.setFormatter(timestamp_formatter)
27+
logger.info("Timestamp formatter example")
28+
29+
30+
custom_vals_formatter = logging.Formatter(
31+
fmt="%(ip)s %(levelname)s: %(message)s", defaults={"ip": "192.168.1.188"}
32+
)
33+
print_handler.setFormatter(custom_vals_formatter)
34+
logger.info("Custom formatter example")
35+
36+
37+
bracket_timestamp_formatter = logging.Formatter(
38+
fmt="{asctime} {levelname}: {message}", style="{"
39+
)
40+
print_handler.setFormatter(bracket_timestamp_formatter)
41+
logger.info("Timestamp formatter bracket style example")
42+
43+
44+
bracket_custom_vals_formatter = logging.Formatter(
45+
fmt="{ip} {levelname}: {message}", style="{", defaults={"ip": "192.168.1.188"}
46+
)
47+
print_handler.setFormatter(bracket_custom_vals_formatter)
48+
logger.info("Custom formatter bracket style example")

0 commit comments

Comments
 (0)