Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Formatter and example including timestamp #61

Merged
merged 5 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 82 additions & 2 deletions adafruit_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@

try:
# pylint: disable=deprecated-class
from typing import Optional, Hashable
from typing import Optional, Hashable, Dict
from typing_extensions import Protocol

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


class Formatter:
"""
Responsible for converting a LogRecord to an output string to be
interpreted by a human or external system.

Only implements a sub-set of CPython logging.Formatter behavior,
but retains all the same arguments in order to match the API.

The only init arguments currently supported are: fmt, defaults and
style. All others are currently ignored

The only two styles currently supported are '%' and '{'. The default
style is '{'
"""

def __init__( # pylint: disable=too-many-arguments
self,
fmt: Optional[str] = None,
datefmt: Optional[str] = None,
style: str = "%",
validate: bool = True,
defaults: Dict = None,
):
self.fmt = fmt
self.datefmt = datefmt
self.style = style
if self.style not in ("{", "%"):
raise ValueError(
"Only '%' and '{' formatting style are supported at this time."
)

self.validate = validate
self.defaults = defaults

def format(self, record: LogRecord) -> str:
"""
Format the given LogRecord into an output string
"""
if self.fmt is None:
return record.msg

vals = {
"name": record.name,
"levelno": record.levelno,
"levelname": record.levelname,
"message": record.msg,
"created": record.created,
"args": record.args,
}
if "{asctime}" in self.fmt or "%(asctime)s" in self.fmt:
now = time.localtime()
# pylint: disable=line-too-long
vals[
"asctime"
] = f"{now.tm_year}-{now.tm_mon:02d}-{now.tm_mday:02d} {now.tm_hour:02d}:{now.tm_min:02d}:{now.tm_sec:02d}"

if self.defaults:
for key, val in self.defaults.items():
if key not in vals:
vals[key] = val

if self.style not in ("{", "%"):
raise ValueError(
"Only '%' and '{' formatting style are supported at this time."
)

if self.style == "%":
return self.fmt % vals

return self.fmt.format(**vals)


class Handler:
"""Base logging message handler."""

def __init__(self, level: int = NOTSET) -> None:
"""Create Handler instance"""
self.level = level
self.formatter = None

def setLevel(self, level: int) -> None:
"""
Expand All @@ -167,7 +240,8 @@ def format(self, record: LogRecord) -> str:

:param record: The record (message object) to be logged
"""

if self.formatter:
return self.formatter.format(record)
return f"{record.created:<0.3f}: {record.levelname} - {record.msg}"

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

def setFormatter(self, formatter: Formatter) -> None:
"""
Set the Formatter to be used by this Handler.
"""
self.formatter = formatter


# pylint: disable=too-few-public-methods
class StreamHandler(Handler):
Expand Down
48 changes: 48 additions & 0 deletions examples/logging_formatter_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# SPDX-FileCopyrightText: 2024 Tim Cocks
# SPDX-License-Identifier: MIT


"""Illustrate usage of default and custom Formatters including
one with timestamps."""

import adafruit_logging as logging

# To test on CPython, un-comment below and comment out above
# import logging


logger = logging.getLogger("example")
logger.setLevel(logging.INFO)
print_handler = logging.StreamHandler()
logger.addHandler(print_handler)

default_formatter = logging.Formatter()

print_handler.setFormatter(default_formatter)
logger.info("Default formatter example")


timestamp_formatter = logging.Formatter(fmt="%(asctime)s %(levelname)s: %(message)s")
print_handler.setFormatter(timestamp_formatter)
logger.info("Timestamp formatter example")


custom_vals_formatter = logging.Formatter(
fmt="%(ip)s %(levelname)s: %(message)s", defaults={"ip": "192.168.1.188"}
)
print_handler.setFormatter(custom_vals_formatter)
logger.info("Custom formatter example")


bracket_timestamp_formatter = logging.Formatter(
fmt="{asctime} {levelname}: {message}", style="{"
)
print_handler.setFormatter(bracket_timestamp_formatter)
logger.info("Timestamp formatter bracket style example")


bracket_custom_vals_formatter = logging.Formatter(
fmt="{ip} {levelname}: {message}", style="{", defaults={"ip": "192.168.1.188"}
)
print_handler.setFormatter(bracket_custom_vals_formatter)
logger.info("Custom formatter bracket style example")