Source code for ulogging

# This module is similar to the logging CPython3 module
# https://docs.python.org/3/howto/logging.html

import sys
import utime as time

_START_TIME = time.time()
LEVELS = {"DEBUG": 0, "INFO": 1, "WARNING": 2, "ERROR": 3, "CRITICAL": 4}


[docs]class Handler: """A class that gives general methods of a handler used for I/O operations."""
[docs] def __init__(self): self._level = 1 # default to INFO self._formatter = "{level} -> {message}" # default format
@property def level(self) -> int: return self._level
[docs] def set_level(self, level) -> None: """Sets the log level for filtering logs at the handler scope. Args: level: Can be DEBUG, INFO, WARNING, ERROR or CRITICAL, or a int from 0 to 4 """ if isinstance(level, str): # if level is a string, get its associated number level = LEVELS.get(level) self._level = level
[docs]class FileHandler(Handler): """A handler used to performs files operations. Attributes: file: the file path formatter: Sets a format for logs default format is "{level}: {name} (+{time}s) -> {message}" """
[docs] def __init__(self, file: str): super().__init__() self.file = file self._level = 1 # default to INFO self.formatter = "{level}: {name} (+{time}s) -> {message}"
[docs] def write(self, content: str): """Write data into the specified file, and close after. Args: content: a log line to write in a file """ with open(self.file, "a", encoding="utf-8") as logfile: logfile.write(content + "\n") logfile.flush()
[docs]class StreamHandler(Handler): """A handler used to write logs in the output console Attributes: formatter: Sets a format for logs default format is "{level}: {name} -> {message}" """
[docs] def __init__(self): super().__init__() self._level = 0 # default to DEBUG self.formatter = "{level}: {name} -> {message}"
[docs] @staticmethod def write(content: str): """Writes a formatted line to the output console (sys.stdout)""" sys.stdout.write(content + "\n")
[docs]class Logger: """A class to perform filtering log messages before sending to handlers. Conventionnal usage: logger = logging.Logger(__name__) Attributes: name: The logger's name propagate: If set to True (default), logs will be written in root's handlers. """ # A dict containing all instances of logger, we can get a logger with its name LOGGERS = {} def __new__(cls, name: str): """The real constructor, It's used to check if an instance with the same name already exists Args: name: The return an instance with a given name or create one. """ if name not in cls.LOGGERS: cls.LOGGERS[name] = super().__new__(cls) return cls.LOGGERS[name]
[docs] def __init__(self, name: str): """Initialize the logger. Args: name (str): the logger's name """ self.name = name self._level = 1 # default to INFO self._handlers = [] self.propagate = True
def __getattr__(self, name: str): """ Used to perform logging with a given level: logger.info(...) will call __getattr__(self, "info") """ if name.upper() in LEVELS: return lambda message: self.log(name.upper(), message)
[docs] def log(self, level: str, message: str) -> None: """Write a log line in a file. Tests if the log's request level is superior to the logger level, then format the string with the format of the handler and write it. Shortcuts methods are debug(), info(), error(), warning() and critical(). Args: level: Can be DEBUG, INFO, WARNING, ERROR or CRITICAL, or a int from 0 to 4. message: The message to write. """ if self._level <= LEVELS[level]: for handler in self.handlers: content = handler.formatter.format( level=level, name=self.name, time=time.time() - _START_TIME, message=message, ) if handler.level <= LEVELS[level]: handler.write(content)
@property def handlers(self) -> list: """Returns a list of handlers that are accessible by the logger. If propagate was set to True, data were sent to root's handlers. Returns: A list of all handlers. """ if self.name != "root" and self.propagate and "root" in self.LOGGERS: return self._handlers + self.LOGGERS["root"].handlers return self._handlers
[docs] def set_level(self, level) -> None: """Sets the logger's level. Args: level: Can be DEBUG, INFO, WARNING, ERROR or CRITICAL, or a int from 0 to 4 """ if isinstance(level, str): level = LEVELS.get(level) self._level = level
[docs] def add_handler(self, handler: Handler) -> None: """Add an Handler instance to the logger. Args: handler: A Handler class (FileHandler or StreamHandler) """ self._handlers.append(handler)
def __repr__(self) -> str: return "Logger(name={}, level={})".format(self.name, self._level)