classLogger:def__init__(self,script_name,console=True,logfile=True,console_log_output="stdout",console_log_level="info",console_log_color=True,logfile_log_level="debug",logfile_log_color=False,logdir=".",asctime=True,threadname=False,verbose=False,log=None,):self.__name__=f"spacekit.{script_name}"ifscript_name!="spacekit"elsescript_nameself.short_name=script_nameself.console=consoleself.logfile=logfileself.log_level=logging.INFOself.console_log_output=console_log_output.lower()self.console_log_level=console_log_level.upper()self.console_log_color=console_log_colorself.console_formatter=Noneself.logdir=logdirself.logfile_file=os.path.join(self.logdir,"spacekit"+".log")self.logfile_log_level=logfile_log_level.upper()self.logfile_log_color=logfile_log_colorself.logfile_formatter=Noneself.asctime=asctimeself.threadname=threadnameself.console_handler=Noneself.logfile_handler=Noneself.verbose=verboseself.log=logself.log_line_template=self.set_log_line_template()defset_name(self):ifself.verboseisTrue:name=" [%(threadName)s - %(name)-16s]"ifself.threadnameisTrueelse" [%(name)-16s]"else:name=" [%(threadName)s - %(name)s]"ifself.threadnameisTrueelse" [%(name)s]"returnnamedefset_log_line_template(self):start_template="%(color_on)s"timestamp="[%(asctime)s]"ifself.asctimeisTrueelse"[%(created)d]"name=self.set_name()levelname=" [%(levelname)-8s]"end_template=" %(message)s%(color_off)s"self.log_line_template=start_template+timestamp+name+levelname+end_templatedefset_formatters(self):self.set_log_line_template()self.console_formatter=LogFormatter(fmt=self.log_line_template,color=self.console_log_color)self.logfile_formatter=LogFormatter(fmt=self.log_line_template,color=self.logfile_log_color)defadd_console_handler(self):# Create console handlerifself.console_log_output=="stdout":self.console_log_output=sys.stdoutelifself.console_log_output=="stderr":self.console_log_output=sys.stderrelse:print("Failed to set console output: invalid output: '%s'"%self.console_log_output)returnFalseself.console_handler=logging.StreamHandler(self.console_log_output)# Set console log leveltry:self.console_handler.setLevel(self.console_log_level)exceptException:print("Failed to set console log level: invalid level: '%s'"%self.console_log_level)returnFalse# Create and set formatter, add console handler to loggerself.console_handler.setFormatter(self.console_formatter)defadd_file_handler(self):try:self.logfile_handler=logging.FileHandler(self.logfile_file)exceptExceptionase:print("Failed to set up log file: %s"%str(e))returnFalse# Set log file log leveltry:self.logfile_handler.setLevel(self.logfile_log_level)exceptException:print("Failed to set log file log level: invalid level: '%s'"%self.logfile_log_level)returnFalse# Create and set formatter, add log file handler to loggerself.logfile_handler.setFormatter(self.logfile_formatter)defadd_handlers(self,logger):# add console handlerifself.consoleisTrue:self.add_console_handler()logger.addHandler(self.console_handler)# Create log file handlerifself.logfileisTrue:os.makedirs(self.logdir,exist_ok=True)self.add_file_handler()logger.addHandler(self.logfile_handler)returnloggerdefupdate_handlers(self,logger):try:forhandlerinlogger.handlers:ifisinstance(handler,logging.FileHandler):handler.setLevel(self.logfile_log_level)handler.setFormatter(self.logfile_formatter)elifisinstance(handler,logging.StreamHandler):handler.setLevel(self.console_log_level)handler.setFormatter(self.console_formatter)exceptException:print("Failed to modify handlers.")returnloggerdefsetup_logger(self,logger=None):self.set_formatters()# Create loggerifloggerisNone:logger=logging.getLogger("spacekit")# Set global log level to 'debug' (required for handler levels to work)logger.setLevel(self.log_level)# don't add additional handlers if they already existiflogger.hasHandlers()isTrue:logger=self.update_handlers(logger)else:logger=self.add_handlers(logger)ifself.verbose:# identify source module in each log statementlogger=logger.getChild(self.short_name)logger.name=self.__name__[:16]# Successreturnloggerdefspacekit_logger(self):# inherit settings from script via `log` attrifself.log:logger=self.setup_logger(logger=self.log)else:# Called by submodules (not scripts)logger=self.setup_logger()returnloggerglobalSPACEKIT_LOGSPACEKIT_LOG=Logger("spacekit").setup_logger()# Command line testdeflog_test():name=Noneloglevel="warning"args=sys.argviflen(args)>1:name=args[1]iflen(args)>2:loglevel=args[2]script_name=nameifnameisnotNoneelseos.path.splitext(os.path.basename(sys.argv[0]))[0]# Setup logging# script_name = os.path.splitext(os.path.basename(sys.argv[0]))[0]log=Logger(script_name,console_log_level=loglevel).setup_logger()ifnotlog:print("Failed to setup logging, aborting.")return1# Log some messageslog.debug("Debug message")log.info("Info message")log.warning("Warning message")log.error("Error message")log.critical("Critical message")# Call main functionif__name__=="__main__":sys.exit(log_test())