diff --git a/Log.py b/Log.py new file mode 100644 index 00000000..dbfa7a56 --- /dev/null +++ b/Log.py @@ -0,0 +1,79 @@ +import inspect +import logging +import sys +import threading +from typing import Any + +lock = threading.Lock() + +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +traceFormatter = logging.Formatter('%(asctime)s - %(name)s@%(filename)s:%(lineno)d:%(funcName)s - %(levelname)s - %(message)s') +# Create a handler that writes log messages to stdout +handler = logging.StreamHandler(sys.stdout) +handler.setLevel(logging.DEBUG) + +errHandler = logging.StreamHandler(sys.stderr) +errHandler.setLevel(logging.ERROR) + +# Create a formatter and set it for the handler +handler.setFormatter(formatter) +errHandler.setFormatter(formatter) + +class Log: + TRACE = 6 + ENTER = 4 + EXIT = 2 + RETURN = 3 + APPLICATION_END = 5 + +class CrossMgrLogger(logging.Logger): + def __init__(self, name: str, level: int | str = 0) -> None: + super().__init__(name, level) + super().addHandler(handler) + super().addHandler(errHandler) + + def log(self, level: int, msg: object, *args: object, **kwargs: Any) -> None: + if level == Log.ENTER < logging.DEBUG: + filename, lineNumber, functionName, stack = self.findCaller() + caller = '{}:{}#{}'.format(filename, lineNumber, functionName) + updatedMsg = '{}: {}'.format(caller, msg) + return super().log(level, updatedMsg, *args, **kwargs) + + return super().log(level, msg, args, kwargs) + + def entering(self, msg: object, *args: object, **kwargs: Any) -> None: + return self.log(Log.ENTER, msg, *args, **kwargs) + + def exiting(self, msg: object, *args: object, **kwargs: Any) -> None: + return self.log(Log.EXIT, msg, *args, **kwargs) + + def trace(self, msg: object, *args: object, **kwargs: Any) -> None: + return self.log(Log.TRACE, msg, *args, **kwargs) + + def returning(self, msg: object, *args: object, **kwargs: Any) -> None: + return self.log(Log.RETURN, msg, *args, **kwargs) + + def exitApp(self, msg: object = 'Application exiting', *args: object, **kwargs: Any) -> None: + return self.log(Log.APPLICATION_END, msg, *args, **kwargs) + +def getLogger(name: str = None, level: int = logging.INFO) -> CrossMgrLogger: + if name is None: + frame = inspect.stack()[1] + module = inspect.getmodule(frame[0]) + logger_name = module.__name__ if module else '__main__' + else: + logger_name = name + + with lock: + lastLogger = logging.getLoggerClass() + logging.setLoggerClass(CrossMgrLogger) + + log = logging.getLogger(logger_name) + assert isinstance(log, CrossMgrLogger) + if lastLogger != CrossMgrLogger: + logging.setLoggerClass(lastLogger) + + log.setLevel(level) + + return log + diff --git a/MainWin.py b/MainWin.py index 2e1c6c5e..c51368b6 100644 --- a/MainWin.py +++ b/MainWin.py @@ -4088,7 +4088,7 @@ def onPageChanging( self, event ): self.callPageCommit( event.GetOldSelection() ) self.callPageRefresh( event.GetSelection() ) try: - Utils.writeLog( 'page: {}\n'.format(notebook.GetPage(event.GetSelection()).__class__.__name__) ) + Utils.writeLog( 'page: {}'.format(notebook.GetPage(event.GetSelection()).__class__.__name__) ) except IndexError: pass event.Skip() # Required to properly repaint the screen. diff --git a/Utils.py b/Utils.py index 373d55f9..545fb657 100644 --- a/Utils.py +++ b/Utils.py @@ -8,6 +8,7 @@ # import wx import os +from Log import getLogger import wx.lib.agw.genericmessagedialog @@ -591,21 +592,9 @@ def approximateMatch( s1, s2 ): #------------------------------------------------------------------------ PlatformName = platform.system() AppVer = 'v' + AppVerName.split(' ')[1] -def writeLog( message ): - try: - dt = datetime.datetime.now() - dt = dt.replace( microsecond = 0 ) - msg = '{} ({} {}) {}{}'.format( - dt.isoformat(), - AppVer, - PlatformName, - message, - '\n' if not message or message[-1] != '\n' else '', - ) - sys.stdout.write( removeDiacritic(msg) ) - sys.stdout.flush() - except IOError: - pass +def writeLog( message:str ) -> None: + log = getLogger() + log.info(message.strip() if message else '') def disable_stdout_buffering(): # No longer necessary as if output goes to the terminal it will be flushed if it ends in newline. @@ -621,14 +610,9 @@ def new_f( *args, **kwargs ): return f( *args, **kwargs) return new_f -def logException( e, exc_info ): - eType, eValue, eTraceback = exc_info - ex = traceback.format_exception( eType, eValue, eTraceback ) - writeLog( '**** Begin Exception ****' ) - for d in ex: - for line in d.split( '\n' ): - writeLog( line ) - writeLog( '**** End Exception ****' ) +def logException( e: Exception, exc_info ) -> None: + log = getLogger() + log.exception( e ) #------------------------------------------------------------------------ mainWin = None