Fully typed, simple, intuitive, and pragmatic logger configurator for standard Python logging.
logician is a lightweight logger-configurator that simplifies configuring Python's built-in logging module. It
supports logger setup using environment variables, CLI flags (-v, -q), and sensible defaultsβall fully typed,
tested, and documented.
-
π§ Minimal boilerplate for structured logging
-
π Environment-variable driven configuration (e.g.
LGCN_ALL_LOG=DEBUG) -
βοΈ Verbosity-aware:
-v,-vv,-q, etc. -
ποΈ Different formats for different log levels
-
π Works seamlessly with standard loggers. More loggers are planned to be supported in the future.
-
π§ͺ Fully type annotated and well-tested via doctests
-
π Extensive docstrings with live examples
-
ποΈ Per-module logging control: associate specific environment variables with individual modules to fineβtune log levels.
For example:
my_pkg.my_module -> MY_MOD_ENV my.pkg.their_module -> TH_MOD_ENV their_pkg.kl.mod -> KLO_ENVUsers can then set environment variables to control log levels per module:
MY_MOD_ENV=DEBUG KLO_ENV=TRACE python -m my_app
This enables users to have fine-grained control over logging, and they can pinβpoint issues just by enabling/disabling/verbosifying logging on a particular module of interest.
-
π Logger-configurator Observability Check what logger-configurator facilities are provided by the programs that use
logician.For example:
$ lgcn my-logician-using-command my-another-command cat
# details about logger configurators of each command: {'my-logician-using-command': {'ro': {'env_list': ['IN_ENV', 'IN.ENV'], 'level_list': [None, None], 'level': 'Level 14', 'propagate': False}, 'ro.or': {'level': 'Level 14', 'propagate': False}, 'r': {'env_list': ['ENV'], 'level_list': [None], 'level': 'Level 32', 'propagate': False}, 'r.c': {'env_list': ['ENV.C', 'ENV_C', 'ENV'], 'level_list': [None, None, None], 'level': 'Level 32', 'propagate': False}, 'c.r': {'level': 'Level 12', 'verbosity': None, 'quietness': None, 'propagate': False}}, 'my-another-command cat': {'ro': {'env_list': ['IN_ENV', 'IN.ENV'], 'level_list': [None, None], 'level': 'Level 14', 'propagate': False}, 'ro.or': {'level': 'Level 14', 'propagate': False}}, 'cat': {}} # no logger-configurator details for the `cat` command as it does not use logician.
pip install logicianfrom logician import get_direct_all_level_logger
import logging
_base_logger = logging.getLogger(__name__)
logger = get_direct_all_level_logger(_base_logger)
"""
The logician configured logger.
"""
logger.trace("π Trace from logician.")
logger.debug("π Debug from logician.")
logger.cmd("πΊ My command's output (maybe captured stderr)", cmd_name="MY_CMD")
logger.info("βΉοΈ Info from logician.")
logger.success("β
Success from logician.") # majorly user/CLI facing log-level
logger.notice("π Notice from logician.") # majorly user/CLI facing log-level
logger.warning("β οΈ Warning from logician.")
logger.error("β Error!")
logger.exception("π₯ Exception!")
logger.critical("π¨ Critical!")
logger.fatal("π Fatal!")This sets up the root logger and all loggers derived from it with a sensible default formatter and log level.
Total eleven (πDude, this many!π¦Ύ) log levels are provided out-of-the-box with room for more!.
These are listed in increasing order of criticality:
- TRACE
- DEBUG
- COMMAND
- INFO
- SUCCESS - usually user/CLI facing log-level
- NOTICE - usually user/CLI facing log-level
- WARNING
- ERROR
- EXCEPTION
- (exception level is just a detailed error level.)
- CRITICAL
- FATAL
- (Yes, CRITICAL and FATAL are two different log levels.)
Modules can be configured to respond to certain env-vars which only correspond to their own logging, without affecting other loggers or logger-configurators.
logician can read and set log levels from environment variables like:
MY_MODULE_LOG=DEBUG # module configured to respond to MY_MODULE_LOG env-var now logs on debug level
MY_SOME_MODULE_LOG=WARNING # other modules not affected by the configured module's logger
OTHER_MODULE_LOG=TRACEThese automatically control the logger levels without code changes.
You can also use the lower-level API directly, for e.g. the APGEN env-var is configured for the ap-generator app:
from logician.stdlog.configurator import StdLoggerConfigurator
from logician.configurators.env import EnvListLC
import logging
base_logger = logging.getLogger('ap-generator') # python std logger
logger = EnvListLC(["APGEN"], StdLoggerConfigurator(level=logging.INFO)).configure(base_logger)One can have multiple env-vars set on the configurator with decreasing order of priority, for e.g.:
import logging
from logician.stdlog.configurator import StdLoggerConfigurator
from logician.configurators.env import EnvListLC
base_logger = logging.getLogger('gp-generator')
logger = EnvListLC(["GPGEN", "GPGENLP"], StdLoggerConfigurator()).configure(base_logger)
"""
``GPGENLP`` has lower priority that ``GPGEN`` to the logger configurator.
if such is the setting
GPGEN=WARNING
GPGENLP=INFO
then WARNING level will be picked-up for logging.
"""See EnvListLC in logician.configurators.env to learn more.
It can get quite detailed, verbose (and may I say, messy) to remember so many env-vars. Thus, logician also provides
env-var LGCN_ALL_LOG with the lowest priority which can set the log levels of all loggers:
# set logging level to INFO for all loggers in my-app
export LGCN_ALL_LOG=INFO
# run my app to see the magic!
python -m my-appSince LGCN_ALL_LOG is the lowest priority env-var hence, other higher priority env-vars can take over the respective
configurations for their own module's logging if needed:
export LGCN_ALL_LOG=INFO
# all modules of my-app perform logging based on the LGCN_ALL_LOG env-vars's value (INFO) but the module configured to
# respond to the ENV_VAR_FOR_MOD_OF_MY_INTRST env-var will only log on TRACE level. This will not affect logging of any
# other module.
ENV_VAR_FOR_MOD_OF_MY_INTRST=TRACE my-app # command to run my-app (here run as a CLI script)Use -v, -vv, -q, --quiet flags from your CLI parser to dynamically set log levels:
from argparse import ArgumentParser
from logician.stdlog.configurator import StdLoggerConfigurator, VQSepLoggerConfigurator
import logging
parser = ArgumentParser()
parser.add_argument("-v", "--verbose", action="count", default=0)
parser.add_argument("-q", "--quiet", action="count", default=0)
args = parser.parse_args()
lc = VQSepLoggerConfigurator(StdLoggerConfigurator(), verbosity=args.verbose, quietness=args.quiet)
logger = logging.getLogger(__name__)
logger = lc.configure(logger)This configures the logger to reflect the verbosity or quietness of the CLI input.
Many-a-times it is the case that more refined (lower) log-levels need to output more (detailed) information. Hence,
logician maps more-detailed log-formats to lower log-levels. Different log levels can be mapped to different log
formats automatically which takes effects throughout all log levels.
β οΈ These format mappings currently assume use with Python's standardloggingmodule. In the future, support may expand to other logging libraries or frameworks.
The default setup looks like this:
WARN and up -> '%(levelname)s: %(message)s'
INFO -> '%(name)s: %(levelname)s: %(message)s'
DEBUG -> '%(name)s: %(levelname)s: [%(filename)s - %(funcName)10s() ]: %(message)s'
TRACE and lower -> '%(asctime)s: %(name)s: %(levelname)s: [%(filename)s:%(lineno)d - %(funcName)10s() ]: %(message)s'You can override these or pass in your own formatting configuration.
-
Have verbosity (v, vv, vvv) and quietness (q, qq, qqq) supported by the logger.
# run my_tool with -qq cli args my_tool -qq>>> import logging >>> from logician.stdlog.configurator import VQSepLoggerConfigurator, StdLoggerConfigurator >>> class Args: ... "get args from CLI or argparse or click or whatever" ... pass >>> args = Args() >>> args.verbose = "v" >>> args.quiet = None >>> lc = VQSepLoggerConfigurator(StdLoggerConfigurator(), verbosity=args.verbose, quietness=args.quiet) >>> _logger = logging.getLogger("my_tool") >>> logger = lc.configure(_logger)
-
Support env-vars to configure logging levels (INFO, DEBUG, SUCCESS, ... etc)
# run my-another-tool with env-var ENV1 supplying log level as ENV1=DEBUG ENV1=DEBUG my-another-tool>>> import logging >>> from logician.configurators.env import LgcnEnvListLC >>> from logician.stdlog.configurator import StdLoggerConfigurator # get a logger configurator supporting ENV1 and ANO_ENV # now ENV1=DEBUG can be set to enable DEBUG level for the logger >>> lc = LgcnEnvListLC(["ENV1", "ANO_ENV"], StdLoggerConfigurator()) >>> _logger = logging.getLogger("my-another-tool") >>> logger = lc.configure(_logger)
-
Support env-vars to configure verbosity levels (v, vv, vvv, q, qq, qqq)
# run my-another-tool with env-var ENV1 supplying log level as ENV1=q (sepcifying ERROR log leve;) ENV1=q my-another-tool>>> import logging >>> from logician.configurators.env import LgcnEnvListLC >>> from logician.stdlog.configurator import StdLoggerConfigurator, VQCommLoggerConfigurator # get a logger configurator supporting ENV1 and ANO_ENV # now ENV1=vv can be set to enable DEBUG level for the logger >>> lc = LgcnEnvListLC(["ENV1", "ANO_ENV"], VQCommLoggerConfigurator(None, StdLoggerConfigurator())) >>> _logger = logging.getLogger("my-another-tool") >>> logger = lc.configure(_logger)
- β 100% typed (compatible with MyPy)
- β Doctests validated examples
- β
Deep pytests in
tests/
Apache License 2.0. See LICENSE for full text.
Contributions welcome!
git clone https://github.com/Vaastav-Technologies/py-logician.git
cd py-logicianactivate a venv, then run
pip install -e .[dev,test] # install logician in local venv
pytest --cov # run pytest with coverage
mypy -p logician # check for static type safety
ruff check # check for lints
ruff format # check for formatsPlease write tests and add doctests for public facing APIs.
- π¦ PyPI: https://pypi.org/project/logician
- π GitHub: https://github.com/Vaastav-Technologies/py-logician