An easy-to-use extension to Python's standard logging library which provides 3 benefits.
Many systems accept user-defined verbosity, such as a "-v" flag passed as a command-line argument. For these cases, the programmer is required to make a conversion between increasing verbosity vs decreasing severity. With this package, you can set a verbosity level on either a handler or a logger object, and it will behave as you would expect.
The following verbosity levels are supported: (listed with their corresponding log levels)
- 0: logging.OUTPUT* (only log real output)
- 1: logging.INFO (log some additional basic info)
- 2: logging.DEBUG (log some useful debug information)
- 3: logging.DETAIL* (log everything - deep dive)
* levels added within this package
There may be times when a log message is generated before a logger has actually been configured. Perhaps you would like to log the start time for your program/script, then read command line arguments and a config file to determine the log file path. With the standard logging library, you would have to store the message as a string, and log later. If your log format includes the creation time "%(asctime)s", the log would show the message created later than it actually was.
This package overrides the makeRecord method of logging.Logger, allowing users to specify the created time.
With great power comes great responsibility. Overriding a log record's creation date without good cause is a detriment to data integrity
Unless you call logging.getLogger(name) in every module, there will likely be scenarios wherein you would like to log a record without knowing for sure that a logger object is available.
Consider library function that takes a logger as an optional parameter:
def brittle_library_function(*args, logger=None, **kwargs):
"""
raises AttributeError if logger is None...bad!
"""
result = do_something(args, **kwargs)
logger.debug(result)
return result
def safe_library_function(*args, logger=None, **kwargs):
"""
Standard approach, but quickly makes code unreadable,
with many levels of indentation...better
"""
results = do_something(args, **kwargs)
if logger is not None: # works, but adds an unneccessary
logger.debug(results) # additional level of indentation
return result
def preinitlogger_library_function(*args, logger=None, **kwargs):
"""
Seamless to the programmer, accepts logger objects or
the name of a logger (as a string), and will do nothing
if logger is None...best!
"""
results = do_something(args, **kwargs)
debug(results, logger=logger)
return results
Recommended:
If your project is within a UV environment, you can add this library
as a dependency using
uv add "preinitlogger @ git+https://github.com/cmeans/preinitlogger.git"
(see usage below).
Otherwise: Clone this repo and treat as a local package.
# If cloned, these will have to be local imports
from preinitlogger import logging, PreInitMessage
from preinitlogger.config import config
logger = logging.getLogger(__name__)
...
if __name__ == '__main__':
startup_msg = PreInitMessage(level=logging.DEBUG, msg=f'{__name__} initializing.')
cli_args = parse_args(sys.argv[1:])
cli_args_msg = PreInitMessage(level=logging.DETAIL, msg=f'{cli_args = }')
config = parse_config(cli_args.get('cfg_file'))
... do_some_more_setup ...
logging.basicConfig()
logger.verbosity = cli_args.verbosity
logger.log_preinit_msgs(startup_msg, cli_args_msg)