misc: add save_json and load_json to DotDict, logging: complete rework
This commit is contained in:
parent
da85c268bb
commit
86a7737e6e
|
@ -1,198 +1,112 @@
|
||||||
'''Simple logging module'''
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from os import environ as env
|
from IzzyLib.misc import DotDict
|
||||||
|
from os import getppid, environ as env
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
stdout = sys.stdout
|
class Levels(Enum):
|
||||||
|
CRITICAL = 60,
|
||||||
|
ERROR = 50
|
||||||
|
WARNING = 40
|
||||||
|
INFO = 30
|
||||||
|
VERBOSE = 20
|
||||||
|
DEBUG = 10
|
||||||
|
MERP = 0
|
||||||
|
|
||||||
|
|
||||||
class Log():
|
class Log:
|
||||||
def __init__(self, config=dict()):
|
__slots__ = [
|
||||||
'''setup the logger'''
|
'name', 'level', 'date', 'format',
|
||||||
if not isinstance(config, dict):
|
'critical', 'error', 'warning',
|
||||||
raise TypeError(f'config is not a dict')
|
'info', 'verbose', 'debug', 'merp'
|
||||||
|
]
|
||||||
|
|
||||||
self.levels = {
|
def __init__(self, name, **config):
|
||||||
'CRIT': 60,
|
self.name = name
|
||||||
'ERROR': 50,
|
self.level = Levels.INFO
|
||||||
'WARN': 40,
|
self.date = True
|
||||||
'INFO': 30,
|
self.format = '%Y-%m-%d %H:%M:%S'
|
||||||
'VERB': 20,
|
self.update_config(**config)
|
||||||
'DEBUG': 10,
|
|
||||||
'MERP': 0
|
|
||||||
}
|
|
||||||
|
|
||||||
self.long_levels = {
|
for level in Levels:
|
||||||
'CRITICAL': 'CRIT',
|
self._set_log_function(level)
|
||||||
'ERROR': 'ERROR',
|
|
||||||
'WARNING': 'WARN',
|
|
||||||
'INFO': 'INFO',
|
|
||||||
'VERBOSE': 'VERB',
|
|
||||||
'DEBUG': 'DEBUG',
|
|
||||||
'MERP': 'MERP'
|
|
||||||
}
|
|
||||||
|
|
||||||
self.config = {'windows': sys.executable.endswith('pythonw.exe')}
|
|
||||||
self.setConfig(self._parseConfig(config))
|
|
||||||
|
|
||||||
|
|
||||||
def _lvlCheck(self, level):
|
def _set_log_function(self, level):
|
||||||
'''make sure the minimum logging level is an int'''
|
setattr(self, level.name.lower(), lambda *args: self.log(level, *args))
|
||||||
|
|
||||||
|
|
||||||
|
def print(self, *args):
|
||||||
|
sys.stdout.write(' '.join([str(arg) for arg in args]))
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def parse_level(self, level):
|
||||||
try:
|
try:
|
||||||
value = int(level)
|
return Levels(int(level))
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
level = self.long_levels.get(level.upper(), level)
|
return getattr(Levels, level.upper())
|
||||||
value = self.levels.get(level)
|
|
||||||
|
|
||||||
if value not in self.levels.values():
|
|
||||||
raise InvalidLevel(f'Invalid logging level: {level}')
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
def _getLevelName(self, level):
|
def update_config(self, **data):
|
||||||
for name, num in self.levels.items():
|
for key, value in data.items():
|
||||||
if level == num:
|
self.set_config(key, value)
|
||||||
return name
|
|
||||||
|
|
||||||
raise InvalidLevel(f'Invalid logging level: {level}')
|
|
||||||
|
|
||||||
|
|
||||||
def _parseConfig(self, config):
|
def set_config(self, key, value):
|
||||||
'''parse the new config and update the old values'''
|
if key == 'level' and type(value) == str:
|
||||||
date = config.get('date', self.config.get('date',True))
|
value = self.parse_level(value)
|
||||||
systemd = config.get('systemd', self.config.get('systemd,', True))
|
|
||||||
windows = config.get('windows', self.config.get('windows', False))
|
|
||||||
|
|
||||||
if not isinstance(date, bool):
|
setattr(self, key, value)
|
||||||
raise TypeError(f'value for "date" is not a boolean: {date}')
|
|
||||||
|
|
||||||
if not isinstance(systemd, bool):
|
|
||||||
raise TypeError(f'value for "systemd" is not a boolean: {date}')
|
|
||||||
|
|
||||||
level_num = self._lvlCheck(config.get('level', self.config.get('level', 'INFO')))
|
|
||||||
|
|
||||||
newconfig = {
|
|
||||||
'level': self._getLevelName(level_num),
|
|
||||||
'levelnum': level_num,
|
|
||||||
'datefmt': config.get('datefmt', self.config.get('datefmt', '%Y-%m-%d %H:%M:%S')),
|
|
||||||
'date': date,
|
|
||||||
'systemd': systemd,
|
|
||||||
'windows': windows,
|
|
||||||
'systemnotif': config.get('systemnotif', None)
|
|
||||||
}
|
|
||||||
|
|
||||||
return newconfig
|
|
||||||
|
|
||||||
|
|
||||||
def setConfig(self, config):
|
def get_config(self, key):
|
||||||
'''set the config'''
|
return self[key]
|
||||||
self.config = self._parseConfig(config)
|
|
||||||
|
|
||||||
|
|
||||||
def getConfig(self, key=None):
|
def print_config(self):
|
||||||
'''return the current config'''
|
self.print(*(f'{k}: {v}\n' for k,v in self.items()))
|
||||||
if key:
|
|
||||||
if self.config.get(key):
|
|
||||||
return self.config.get(key)
|
|
||||||
else:
|
|
||||||
raise ValueError(f'Invalid config option: {key}')
|
|
||||||
return self.config
|
|
||||||
|
|
||||||
|
|
||||||
def printConfig(self):
|
|
||||||
for k,v in self.config.items():
|
|
||||||
stdout.write(f'{k}: {v}\n')
|
|
||||||
|
|
||||||
stdout.flush()
|
|
||||||
|
|
||||||
|
|
||||||
def setLevel(self, level):
|
|
||||||
self.minimum = self._lvlCheck(level)
|
|
||||||
|
|
||||||
|
|
||||||
def log(self, level, *msg):
|
def log(self, level, *msg):
|
||||||
if self.config['windows']:
|
if level.value < self.level.value:
|
||||||
return
|
return
|
||||||
|
|
||||||
'''log to the console'''
|
default = self.name == 'Default'
|
||||||
levelNum = self._lvlCheck(level)
|
options = [
|
||||||
|
level.name + ':',
|
||||||
|
' '.join([str(message) for message in msg]),
|
||||||
|
'\n'
|
||||||
|
]
|
||||||
|
|
||||||
if type(level) == int:
|
if self.date and not getppid() == 1:
|
||||||
level = _getLevelName(level)
|
options.insert(0, datetime.now().strftime(self.format))
|
||||||
|
|
||||||
if levelNum < self.config['levelnum']:
|
if not default:
|
||||||
return
|
options.insert(0 if not self.date else 1, f'[{self.name}]')
|
||||||
|
|
||||||
message = ' '.join([str(message) for message in msg])
|
self.print(*options)
|
||||||
output = f'{level}: {message}\n'
|
|
||||||
|
|
||||||
if self.config['systemnotif']:
|
|
||||||
self.config['systemnotif'].New(level, message)
|
|
||||||
|
|
||||||
if self.config['date'] and (self.config['systemd'] and not env.get('INVOCATION_ID')):
|
|
||||||
'''only show date when not running in systemd and date var is True'''
|
|
||||||
date = datetime.now().strftime(self.config['datefmt'])
|
|
||||||
output = f'{date} {output}'
|
|
||||||
|
|
||||||
stdout.write(output)
|
|
||||||
stdout.flush()
|
|
||||||
|
|
||||||
|
|
||||||
def critical(self, *msg):
|
def get_logger(name, **config):
|
||||||
self.log('CRIT', *msg)
|
try:
|
||||||
|
return logger[name.lower()]
|
||||||
|
|
||||||
def error(self, *msg):
|
except KeyError:
|
||||||
self.log('ERROR', *msg)
|
log = Log(name, **config)
|
||||||
|
logger[name.lower()] = log
|
||||||
def warning(self, *msg):
|
return log
|
||||||
self.log('WARN', *msg)
|
|
||||||
|
|
||||||
def info(self, *msg):
|
|
||||||
self.log('INFO', *msg)
|
|
||||||
|
|
||||||
def verbose(self, *msg):
|
|
||||||
self.log('VERB', *msg)
|
|
||||||
|
|
||||||
def debug(self, *msg):
|
|
||||||
self.log('DEBUG', *msg)
|
|
||||||
|
|
||||||
def merp(self, *msg):
|
|
||||||
self.log('MERP', *msg)
|
|
||||||
|
|
||||||
|
|
||||||
def getLogger(loginst, config=None):
|
|
||||||
'''get a logging instance and create one if it doesn't exist'''
|
|
||||||
Logger = logger.get(loginst)
|
|
||||||
|
|
||||||
if not Logger:
|
|
||||||
if config:
|
|
||||||
logger[loginst] = Log(config)
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise InvalidLogger(f'logger "{loginst}" doesn\'t exist')
|
|
||||||
|
|
||||||
return logger[loginst]
|
|
||||||
|
|
||||||
class InvalidLevel(Exception):
|
|
||||||
'''Raise when an invalid logging level was specified'''
|
|
||||||
|
|
||||||
class InvalidLogger(Exception):
|
|
||||||
'''Raise when the specified logger doesn't exist'''
|
|
||||||
|
|
||||||
|
|
||||||
'''create a default logger'''
|
'''create a default logger'''
|
||||||
logger = {
|
logger = {
|
||||||
'default': Log()
|
'default': Log('Default')
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultLog = logger['default']
|
DefaultLog = logger['default']
|
||||||
|
|
||||||
|
|
||||||
'''aliases for default logger's log output functions'''
|
'''aliases for default logger's log output functions'''
|
||||||
critical = DefaultLog.critical
|
critical = DefaultLog.critical
|
||||||
error = DefaultLog.error
|
error = DefaultLog.error
|
||||||
|
@ -203,7 +117,7 @@ debug = DefaultLog.debug
|
||||||
merp = DefaultLog.merp
|
merp = DefaultLog.merp
|
||||||
|
|
||||||
'''aliases for the default logger's config functions'''
|
'''aliases for the default logger's config functions'''
|
||||||
setConfig = DefaultLog.setConfig
|
update_config = DefaultLog.update_config
|
||||||
getConfig = DefaultLog.getConfig
|
set_config = DefaultLog.set_config
|
||||||
setLevel = DefaultLog.setLevel
|
get_config = DefaultLog.get_config
|
||||||
printConfig = DefaultLog.printConfig
|
print_config = DefaultLog.print_config
|
||||||
|
|
|
@ -230,10 +230,6 @@ class DotDict(dict):
|
||||||
return (k, v)
|
return (k, v)
|
||||||
|
|
||||||
|
|
||||||
def update(self, data):
|
|
||||||
super().update(data)
|
|
||||||
|
|
||||||
|
|
||||||
def get(self, key, default=None):
|
def get(self, key, default=None):
|
||||||
value = dict.get(self, key, default)
|
value = dict.get(self, key, default)
|
||||||
return DotDict(value) if type(value) == dict else value
|
return DotDict(value) if type(value) == dict else value
|
||||||
|
@ -275,6 +271,20 @@ class DotDict(dict):
|
||||||
self.update(data)
|
self.update(data)
|
||||||
|
|
||||||
|
|
||||||
|
def load_json(self, path):
|
||||||
|
path = Path(path)
|
||||||
|
|
||||||
|
with path.open('r') as fd:
|
||||||
|
self.update(json.load(fd))
|
||||||
|
|
||||||
|
|
||||||
|
def save_json(self, path, indent=None):
|
||||||
|
path = Path(path)
|
||||||
|
|
||||||
|
with path.open('w') as fd:
|
||||||
|
json.dump(self, fd, indent=indent)
|
||||||
|
|
||||||
|
|
||||||
class DefaultDict(DotDict):
|
class DefaultDict(DotDict):
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key):
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Reference in a new issue