Best practices for debug and logging output in library modules
Posted: Wed Aug 21, 2019 3:56 pm
(sorry for the formatting, BBCode seems to be disabled for my account and I can't figure out how to enable it... if you know how to fix this, please let me know!)
I'm in the middle of writing my first library module in micropython that makes use of the machine.I2C class, and as part of the bring up process for the chip, I wanted to add logging output (I2C reads/writes, etc).
For a CPython library, the logging module is included in the standard library, so you can set up debug logging within the library module like the following:
(module.py)
Then, whoever imports the module can configure what level of log messages they want to display when they use your library:
(main.py)
However, for micropython, it's not guaranteed that the logging.py module exists on the system that is importing the library module:
It looks like there has been some discussion a couple years ago around this topic:
https://github.com/micropython/micropyt ... -274002185
The uasyncio module looks like it tries to deal with this by importing logging when DEBUG is set, but I didn't come across any other examples of how this is handled in the lib:
https://github.com/micropython/micropyt ... t__.py#L10
I'm curious if some of the more experienced folks have any best practices or standard ways of writing micropython libraries that deal with this nicely. I guess one option is to take the uasyncio approach and just document that the library requires logging.py.
Another possibility is to try to use the logging module if it exists, for example:
(module.py)
This would allow the library module to just work whether or not logging.py has been included in the target system. Any thoughts on how to best approach this?
I'm in the middle of writing my first library module in micropython that makes use of the machine.I2C class, and as part of the bring up process for the chip, I wanted to add logging output (I2C reads/writes, etc).
For a CPython library, the logging module is included in the standard library, so you can set up debug logging within the library module like the following:
(module.py)
Code: Select all
import logging
# https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library
logging.getLogger('foo').addHandler(logging.NullHandler())
logger = logging.getLogger(__name__)
logger.debug('This is a test log message.')
(main.py)
Code: Select all
import logging
logging.basicConfig(level=logging.DEBUG)
Code: Select all
>>> import logging
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: no module named 'logging'
https://github.com/micropython/micropyt ... -274002185
The uasyncio module looks like it tries to deal with this by importing logging when DEBUG is set, but I didn't come across any other examples of how this is handled in the lib:
https://github.com/micropython/micropyt ... t__.py#L10
I'm curious if some of the more experienced folks have any best practices or standard ways of writing micropython libraries that deal with this nicely. I guess one option is to take the uasyncio approach and just document that the library requires logging.py.
Another possibility is to try to use the logging module if it exists, for example:
(module.py)
Code: Select all
try:
import logging
logger = logging.getLogger(__name__)
except ImportError:
pass
class MyDriver(object):
def _i2c_read_register(self, register, result):
...
if __debug__ and ('logging' in sys.modules) and (logger.isEnabledFor(logging.DEBUG)):
logger.debug("Read %s from register 0x%x" % (bytes(result), register))
...
def _i2c_write_register(self, register, data):
...
if __debug__ and ('logging' in sys.modules) and (logger.isEnabledFor(logging.DEBUG)):
logger.debug("Write %s to register 0x%x" % (data, register))
...