voussoirkit/voussoirkit/vlogging.py

152 lines
4.4 KiB
Python
Raw Normal View History

2020-11-16 06:01:47 +00:00
'''
2021-08-10 18:53:55 +00:00
vlogging
========
Hey, what's up guys, it's voussoirkit back with another awesome module for you.
This module forwards everything from logging, with the addition of levels LOUD
and SILENT, and all loggers from getLogger are given the `loud` method.
Don't forget to like, comment, and subscribe.
2020-11-16 06:01:47 +00:00
'''
2020-11-03 07:18:53 +00:00
from logging import *
_getLogger = getLogger
LOUD = 1
2021-05-08 16:00:48 +00:00
SILENT = 99999999999
2020-11-03 07:18:53 +00:00
def getLogger(name=None, main_fallback=None):
2021-05-07 02:29:02 +00:00
'''
Normally it is best practice to use getLogger(__name__), but when running
a script directly you'll see "__main__" in the output, which I think is
ugly and unexpected for anyone who doesn't know what's going on behind
the scenes. But hardcoding your logger name is not good either.
So, main_fallback is used to present your preferred name in case of main.
'''
if name == '__main__' and main_fallback is not None:
name = main_fallback
log = _getLogger(name)
2020-11-03 07:18:53 +00:00
add_loud(log)
return log
def add_loud(log):
2021-08-10 18:53:55 +00:00
'''
Add the `loud` method to the given logger.
'''
2020-11-10 00:31:19 +00:00
def loud(self, message, *args, **kwargs):
if self.isEnabledFor(LOUD):
self._log(LOUD, message, args, **kwargs)
2020-11-03 07:18:53 +00:00
addLevelName(LOUD, 'LOUD')
2020-11-10 00:31:19 +00:00
log.loud = loud.__get__(log, log.__class__)
2020-11-03 07:18:53 +00:00
def get_level_by_argv(argv):
2020-11-09 19:04:40 +00:00
'''
If any of the following arguments are present in argv, return the
corresponding log level along with a new copy of argv that has had the
argument string removed.
Since we are removing the argument, your argparser should not have options
with these same names.
--loud: LOUD
--debug: DEBUG
--quiet: ERROR
2021-05-08 16:00:48 +00:00
--silent: SILENT
2020-11-09 19:04:40 +00:00
none of the above: INFO
'''
2020-11-03 07:18:53 +00:00
argv = argv[:]
if '--loud' in argv:
level = LOUD
2020-11-03 07:18:53 +00:00
argv.remove('--loud')
elif '--debug' in argv:
level = DEBUG
2020-11-03 07:18:53 +00:00
argv.remove('--debug')
elif '--quiet' in argv:
level = ERROR
2020-11-03 07:18:53 +00:00
argv.remove('--quiet')
2020-11-09 04:12:19 +00:00
elif '--silent' in argv:
2021-05-08 16:00:48 +00:00
level = SILENT
2020-11-09 04:12:19 +00:00
argv.remove('--silent')
2020-11-03 07:18:53 +00:00
else:
level = INFO
return (level, argv)
2021-05-07 02:29:02 +00:00
def get_level_by_name(name):
'''
The logging module maintains a private variable _nameToLevel but does not
have an official public function for querying it. There is getLevelName,
but that function never fails an input, it just returns it back to you as
"Level X" for any number X, or indeed any hashable type! The return value
of getLevelName isn't accepted by setLevel since setLevel does not parse
"Level X" strings, though it does accept exact matches by registered
level names.
Consider this function your alternative, for querying levels by name and
getting an integer you can give to setLevel.
'''
if isinstance(name, int):
return name
if not isinstance(name, str):
raise TypeError(f'name should be str, not {type(name)}.')
name = name.upper()
levels = {
2021-08-24 05:41:07 +00:00
'SILENT': SILENT,
2021-05-07 02:29:02 +00:00
'CRITICAL': CRITICAL,
'FATAL': FATAL,
'ERROR': ERROR,
'WARN': WARNING,
'WARNING': WARNING,
'INFO': INFO,
'DEBUG': DEBUG,
'LOUD': LOUD,
'NOTSET': NOTSET,
}
value = levels.get(name)
if value is None:
raise ValueError(f'{name} is not a known level.')
return value
def main_decorator(main):
2021-08-10 18:53:55 +00:00
'''
Add this decorator to your application's main function to automatically
set the main log handler level by the arguments in argv. This allows you
to use --debug, --quiet, etc. on the command line without making any
changes to your argparser.
'''
def wrapped(argv):
argv = main_level_by_argv(argv)
return main(argv)
return wrapped
def main_level_by_argv(argv):
2020-11-09 19:04:40 +00:00
'''
This function puts a handler on the root logger with a level set by the
flags in argv, then returns the rest of argv which you can pass to
your argparser.
2020-11-09 19:04:40 +00:00
'''
(level, argv) = get_level_by_argv(argv)
# Previously I was using basicConfig to prepare the handlers and then
# setting root.setLevel, but the problem is that prevents any other
# handlers on the root from receiving messages at a lower level.
# It works best if the logger itself doesn't have a level and the handlers
# can choose what they want.
root = getLogger()
root.setLevel(NOTSET)
handler = StreamHandler()
handler.setFormatter(Formatter('{levelname}:{name}:{message}', style='{'))
handler.setLevel(level)
root.addHandler(handler)
return argv