2020-11-16 06:01:47 +00:00
|
|
|
'''
|
|
|
|
This module forwards everything from logging, with the addition of a level
|
|
|
|
LOUD=1 and all loggers are given the `loud` method.
|
|
|
|
'''
|
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
|
|
|
|
2021-01-29 00:51:06 +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.
|
|
|
|
'''
|
2021-01-29 00:51:06 +00:00
|
|
|
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):
|
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
|
|
|
|
2020-11-09 04:12:05 +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:
|
2020-11-09 04:12:05 +00:00
|
|
|
level = LOUD
|
2020-11-03 07:18:53 +00:00
|
|
|
argv.remove('--loud')
|
|
|
|
elif '--debug' in argv:
|
2020-11-09 04:12:05 +00:00
|
|
|
level = DEBUG
|
2020-11-03 07:18:53 +00:00
|
|
|
argv.remove('--debug')
|
|
|
|
elif '--quiet' in argv:
|
2020-11-09 04:12:05 +00:00
|
|
|
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:
|
2020-11-09 04:12:05 +00:00
|
|
|
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 = {
|
|
|
|
'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
|
|
|
|
|
2020-11-09 04:12:05 +00:00
|
|
|
def set_level_by_argv(log, argv):
|
2020-11-09 19:04:40 +00:00
|
|
|
'''
|
|
|
|
This function is helpful for single-file scripts where you instantiate the
|
|
|
|
logger in the global scope, and use this function to set its level
|
|
|
|
according to the "--debug" flags in argv, then pass the rest of argv to
|
|
|
|
your argparser.
|
|
|
|
'''
|
2020-11-09 04:12:05 +00:00
|
|
|
basicConfig()
|
|
|
|
|
|
|
|
(level, argv) = get_level_by_argv(argv)
|
|
|
|
log.setLevel(level)
|
2020-11-03 07:18:53 +00:00
|
|
|
|
|
|
|
return argv
|