"""A first pass application to configure the :mod:`logging` system."""__author__='Paul Landes'fromtypingimportDict,Union,Anyfromdataclassesimportdataclass,fieldfromenumimportEnumimportloggingfromloggingimportLoggerfrompathlibimportPathfrom..importActionCliError,ApplicationErrorfromzensols.util.logimportadd_trace_levellogger=logging.getLogger(__name__)add_trace_level()
[docs]classLogLevel(Enum):"""Set of configurable log levels on the command line. Note that we don't include all so as to not overwhelm the help usage. """notset=logging.NOTSETtrace=logging.TRACEdebug=logging.DEBUGinfo=logging.INFOwarn=logging.WARNINGerr=logging.ERRORcrit=logging.CRITICAL
[docs]@dataclassclassLogConfigurator(object):"""A simple log configuration utility. """CLI_META={'first_pass':True,# not a separate action# don't add the '-l' as a short option'option_overrides':{'level':{'short_name':None}},# we configure this class, but use a better naming for# debugging'mnemonic_overrides':{'config':'log'},'mnemonic_includes':{'config'},# only set 'level' as a command line option so we can configure# the rest in the application context.'option_includes':{'level'}}"""Command line meta data to avoid having to decorate this class in the configuration. Given the complexity of this class, this configuration only exposes the parts of this class necessary for the CLI. """log_name:str=field(default=None)"""The log name space."""default_level:LogLevel=field(default=None)"""The root logger level."""level:LogLevel=field(default=None)"""The application logger level."""default_app_level:LogLevel=field(default=LogLevel.info)"""The default log level to set the application logger when not given on the command line. """config_file:Path=field(default=None)"""If provided, configure the log system with this configuration file."""format:str=field(default=None)"""The format string to use for the logging system."""loggers:Dict[str,Union[str,LogLevel]]=field(default=None)"""Additional loggers to configure."""debug:bool=field(default=False)"""Print some logging to standard out to debug this class."""def__post_init__(self):if((self.default_levelisnotNone)or(self.formatisnotNone)) \
and(self.config_fileisnotNone):raiseActionCliError("Cannot set 'default_level' or 'format' "+"while setting a log configuration file 'config_file'")ifself.default_levelisNone:self.default_level=LogLevel.warndef_to_level(self,name:str,level:Any)->int:ifisinstance(level,LogLevel):level=level.valueelifisinstance(level,str):obj=LogLevel.__members__.get(level)ifobjisNone:raiseApplicationError(f'No such level for {name}: {level}')level=obj.valueifnotisinstance(level,int):raiseActionCliError(f'Unknown level: {level}({type(level)})')returnleveldef_debug(self,msg:str):iflogger.isEnabledFor(logging.DEBUG):logger.debug(msg)ifself.debug:print(msg)def_config_file(self):importlogging.configself._debug(f'configuring from file: {self.config_file}')logging.config.fileConfig(self.config_file,disable_existing_loggers=False)def_config_basic(self):self._debug(f'configuring root logger to {self.default_level}')level:int=self._to_level('default',self.default_level)params={'level':level}ifself.formatisnotNone:params['format']=self.format.replace('%%','%')self._debug(f'config log system with level {level} '+f'({self.default_level})')logging.basicConfig(**params)
[docs]defconfig(self):"""Configure the log system. """modified_logger:Logger=Noneifself.config_fileisnotNone:self._config_file()else:self._config_basic()ifself.log_nameisnotNone:app_level=self.default_app_level \
ifself.levelisNoneelseself.levellevel:int=self._to_level('app',app_level)self._debug(f'setting logger {self.log_name} to {level} '+f'({app_level})')modified_logger=logging.getLogger(self.log_name)modified_logger.setLevel(level)# avoid clobbering CLI given level with app config ``loggers`` entryifself.levelisNoneandself.loggersisnotNone:forname,levelinself.loggers.items():level=self._to_level(name,level)assertisinstance(level,int)self._debug(f'setting logger: {name} -> {level}')modified_logger=logging.getLogger(name)modified_logger.setLevel(level)returnmodified_logger
[docs]@classmethoddefset_format(cls,format:str):"""Set the format of the logger after previously set using this class. """root=logging.getLogger()hdlr:logging.StreamHandler=root.handlers[0]asserttype(hdlr)==logging.StreamHandlerfmt=logging.Formatter(format)hdlr.setFormatter(fmt)
[docs]@staticmethoddefreset():"""Reset the logging system. All configuration is removed."""fromimportlibimportreloadlogging.shutdown()reload(logging)