Home

Awesome

micropython-ulogger

Logging on micropython is not an easy task. micropython has many unfinished interfaces, so the log content that can be recorded is very limited. I designed this ulogger library based on the situation of micropython.

Note: This article uses Google Translate as a reference. There may be many ambiguities due to inaccurate translations. Welcome to submit a pull request.

English|简体中文 LOGO

Features:

In micropython, due to the limited computing power of the single-chip microcomputer, everything needs to be processed and responded quickly, so the purpose of this module is to reduce worthless operations. Therefore, this module will be more differences with the standard library logging of CPython.

How to install?

by pypi

#repl on your board
import upip
upip.install("micropython-ulogger")

by manually

Please download the latest version of .mpy file in the release of this project, and put it in the /lib directory of the development board or the directory of your program.

How to use?

quick-start

This is the simplest example:

import ulogger
loggor = ulogger.Logger(__name__)
loggor.info("hello world")

About Handler

Now. Let's give it some condiment

import ulogger

handler_to_term = ulogger.Handler(
    level=ulogger.INFO,
    colorful=True,
    fmt="&(time)% - &(level)% - &(name)% - &(fnname)% - &(msg)%",
    clock=None,
    direction=ulogger.TO_TERM,
)
handler_to_file = ulogger.Handler(
    level=ulogger.INFO,
    fmt="&(time)% - &(level)% - &(name)% - &(fnname)% - &(msg)%",
    clock=None,
    direction=ulogger.TO_FILE,
    file_name="logging.log",
    max_file_size=1024 # max for 1k
)
logger = ulogger.Logger(
    name = __name__,
    handlers = (
        handler_to_term,
        handler_to_file
    )
)

logger.info("hello", "world")
# Support multi-parameter filling.

In ulogger, one thing is different with the logging module of CPython: In the logging module, formatter and handler are separate, but in this module, I combine them into One, this can reduce the workload of this module (you certainly don't want your development board to spend too much time to recording logs!), of course, this will reduce configurability, but we always have to pay a little price for improving performance.

The params Handler can use:

# default args
ulogger.Handler(
    level: int = INFO,
    colorful: bool = True,
    fmt: str = "&(time)% - &(level)% - &(name)% - &(msg)%",
    clock: BaseClock = None,
    direction: int = TO_TERM,
    file_name: str = "logging.log",
    max_file_size: int = 4096
)

About Clock

Perhaps you have noticed the clock parameter in Handler, we provide an interface for customizing the time output format, you only need to inherit the BaseClock class to customize your clock.

For Example:

import ntptime, machine
class RealClock(ulogger.BaseClock):
    def __init__ (self):
        self.rct = machine.RTC ()
        ntptime.settime()  # Get and set network time 
        # * Note: Get here is the international standard time 
        # * For more information about the RTC module, see: 
        # http://docs.micropython.org/en/latest/library/machine.RTC.html#machine.RTC

    def __call__(self) -> str:
        # When we need to get the time, this function will be called automatically, and its return value will be used as the timestamp text.

        # self.rtc.datetime () -> (year, month, day, weekday, hours, minutes, seconds, subseconds)
        y,m,d,_,h,mi,s,_ = self.rtc.datetime ()
        return '%d-%d-%d %d:%d:%d' % (y,m,d,h,mi,s)
    	# In micropython, formatting text with'%' is the fastest way to process it. 
        # See details at: https://blog.m-jay.cn/?p=329

If you want to customize your time-zone, you can follow this methods:

def __init__ (self):
        self.rct = machine.RTC ()
        # now = ntptime.time ()
        # tp_time = time.localtime (now)
        # self.rtc.init (tp_time)
        self.rtc.init (
            time.localtime (
                ntptime.time () + 28800  # Add eight hours (Beijing time) 
            )
        )

The principle is to add or subtract the corresponding time from the obtained network time, for example, Beijing time (+8), add 60*60*8=28800 seconds

A complete sample code:

import ulogger

# Example for esp8266 & esp32
from machine import RTC
import ntptime
class Clock(ulogger.BaseClock):
    def __init__(self):
        self.rtc = RTC()
        ntptime.host = "ntp.ntsc.ac.cn"  # Setting up a faster ntf server can reduce latency
        ntptime.settime()
        
    def __call__(self) -> str:
        y,m,d,_,h,mi,s,_ = self.rtc.datetime ()
        return '%d-%d-%d %d:%d:%d' % (y,m,d,h,mi,s)

clock = Clock()
handler_to_term = ulogger.Handler(
    level=ulogger.INFO,
    colorful=True,
    fmt="&(time)% - &(level)% - &(name)% - &(fnname)% - &(msg)%",
    clock=clock,
    direction=ulogger.TO_TERM,
)

handler_to_file = ulogger.Handler(
    level=ulogger.INFO,
    fmt="&(time)% - &(level)% - &(name)% - &(fnname)% - &(msg)%",
    clock=clock,
    direction=ulogger.TO_FILE,
    file_name="logging.log",
    max_file_size=1024 # max for 1k
)
logger = ulogger.Logger(
    name = __name__, 
    handlers = (handler_to_term, handler_to_file)
    )

logger.info("hello world")

Structure

Design Guidelines

Encapsulate it as a module

It is not scientific to configure a Clock or Handler separately in each python file, and it will take up a lot of space, so we recommend encapsulating it as a module for use, for example:

# loguitl.py
import ulogger

from machine import RTC
import ntptime
class Clock(ulogger.BaseClock):
    def __init__(self):
        self.rtc = RTC()
        ntptime.host = "ntp.ntsc.ac.cn"
        ntptime.settime()
        
    def __call__(self) -> str:
        y,m,d,_,h,mi,s,_ = self.rtc.datetime ()
        return '%d-%d-%d %d:%d:%d' % (y,m,d,h,mi,s)
clock = Clock()
    
handlers = (
    ulogger.Handler(
        level=ulogger.INFO,
        colorful=True,
        fmt="&(time)% - &(level)% - &(name)% - &(fnname)% - &(msg)%",
        clock=clock,
        direction=ulogger.TO_TERM,
    ),
    ulogger.Handler(
        level=ulogger.ERROR,
        fmt="&(time)% - &(level)% - &(name)% - &(fnname)% - &(msg)%",
        clock=clock,
        direction=ulogger.TO_FILE,
        file_name="logging.log",
        max_file_size=1024 # max for 1k
    )
)

def get_logger(name: str):
    return ulogger.Logger(name, handlers)
all = (get_logger)

Note: The above saves two Handler into a tuple, so that multiple memory allocations can be avoided when get_logger is called multiple times. If you ask me embedded devices How to design the faster code? I will tell you can save, just save

Reduce IO operations

IO operation has always been the slowest part of the computer, especially on the microcontroller. Therefore, you can:

Code hint

You can install this module on the computer where you write the code to enable the editor's code hint function:

pip install micropython-ulogger