Compare commits

...

3 Commits

Author SHA1 Message Date
Clemens Schwaighofer
743a0a8ac9 v0.14.0: logging exception automatically writes to error for console output 2025-07-15 17:57:49 +09:00
Clemens Schwaighofer
694712ed2e logging update for exceptions with special error log to console
When calling log.exception it automatically writes a short message to the console as error
This error message is tagged <=EXCEPTION> and contains only the message without the stack trace
2025-07-15 17:56:56 +09:00
Clemens Schwaighofer
ea3b4f1790 v0.13.2: move domain regex to the regex constant file 2025-07-15 11:16:01 +09:00
4 changed files with 45 additions and 12 deletions

View File

@@ -1,7 +1,7 @@
# MARK: Project info # MARK: Project info
[project] [project]
name = "corelibs" name = "corelibs"
version = "0.13.1" version = "0.14.0"
description = "Collection of utils for Python scripts" description = "Collection of utils for Python scripts"
readme = "README.md" readme = "README.md"
requires-python = ">=3.13" requires-python = ">=3.13"

View File

@@ -73,11 +73,32 @@ class CustomConsoleFormatter(logging.Formatter):
message = super().format(record) message = super().format(record)
return f"{color}{message}{reset}" return f"{color}{message}{reset}"
# TODO: add custom handlers for stack_trace, if not set fill with %(filename)s:%(funcName)s:%(lineno)d # TODO: add custom handlers for stack_trace, if not set fill with %(filename)s:%(funcName)s:%(lineno)d
# hasattr(record, 'stack_trace') # hasattr(record, 'stack_trace')
class CustomHandlerFilter(logging.Filter):
"""
Add a custom handler for filtering
"""
def __init__(self, handler_name: str, filter_exceptions: bool = False):
super().__init__(name=handler_name)
self.handler_name = handler_name
self.filter_exceptions = filter_exceptions
def filter(self, record: logging.LogRecord) -> bool:
# if console and exception do not show
if self.handler_name == 'console' and self.filter_exceptions:
return record.levelname != "EXCEPTION"
# if cnosole entry is true and traget file filter
if hasattr(record, 'console') and getattr(record, 'console') is True and self.handler_name == 'file':
return False
return True
# def __filter_exceptions(self, record: logging.LogRecord) -> bool:
# return record.levelname != "EXCEPTION"
# MARK: Log class # MARK: Log class
class Log: class Log:
""" """
@@ -142,7 +163,7 @@ class Log:
# in the file writer too, for the ones where color is set BEFORE the format # in the file writer too, for the ones where color is set BEFORE the format
# Any is logging.StreamHandler, logging.FileHandler and all logging.handlers.* # Any is logging.StreamHandler, logging.FileHandler and all logging.handlers.*
self.handlers: dict[str, Any] = {} self.handlers: dict[str, Any] = {}
self.add_handler('file_handler', self.__create_time_rotating_file_handler( self.add_handler('file_handler', self.__create_timed_rotating_file_handler(
self.log_settings['log_level_file'], log_path) self.log_settings['log_level_file'], log_path)
) )
if self.log_settings['console_enabled']: if self.log_settings['console_enabled']:
@@ -211,8 +232,8 @@ class Log:
default_log_settings['log_queue'] = __setting default_log_settings['log_queue'] = __setting
return default_log_settings return default_log_settings
def __filter_exceptions(self, record: logging.LogRecord) -> bool: # def __filter_exceptions(self, record: logging.LogRecord) -> bool:
return record.levelname != "EXCEPTION" # return record.levelname != "EXCEPTION"
# MARK: add a handler # MARK: add a handler
def add_handler( def add_handler(
@@ -259,16 +280,15 @@ class Log:
formatter_console = CustomConsoleFormatter(format_string, datefmt=format_date) formatter_console = CustomConsoleFormatter(format_string, datefmt=format_date)
else: else:
formatter_console = logging.Formatter(format_string, datefmt=format_date) formatter_console = logging.Formatter(format_string, datefmt=format_date)
console_handler.setLevel(log_level_console.name)
console_handler.set_name('console') console_handler.set_name('console')
console_handler.setLevel(log_level_console.name)
# do not show exceptions logs on console # do not show exceptions logs on console
if filter_exceptions: console_handler.addFilter(CustomHandlerFilter('console', filter_exceptions))
console_handler.addFilter(self.__filter_exceptions)
console_handler.setFormatter(formatter_console) console_handler.setFormatter(formatter_console)
return console_handler return console_handler
# MARK: file handler # MARK: file handler
def __create_time_rotating_file_handler( def __create_timed_rotating_file_handler(
self, log_level_file: LoggingLevel, log_path: Path, self, log_level_file: LoggingLevel, log_path: Path,
when: str = "D", interval: int = 1, backup_count: int = 0 when: str = "D", interval: int = 1, backup_count: int = 0
) -> logging.handlers.TimedRotatingFileHandler: ) -> logging.handlers.TimedRotatingFileHandler:
@@ -304,6 +324,8 @@ class Log:
) )
file_handler.set_name('file_timed_rotate') file_handler.set_name('file_timed_rotate')
file_handler.setLevel(log_level_file.name) file_handler.setLevel(log_level_file.name)
# do not show errors flagged with console (they are from exceptions)
file_handler.addFilter(CustomHandlerFilter('file'))
file_handler.setFormatter(formatter_file_handler) file_handler.setFormatter(formatter_file_handler)
return file_handler return file_handler
@@ -454,7 +476,11 @@ class Log:
self.logger.log(LoggingLevel.EMERGENCY.value, msg, *args, extra=extra, stacklevel=2) self.logger.log(LoggingLevel.EMERGENCY.value, msg, *args, extra=extra, stacklevel=2)
# MARK: EXCEPTION: 70 # MARK: EXCEPTION: 70
def exception(self, msg: object, *args: object, extra: MutableMapping[str, object] | None = None) -> None: def exception(
self,
msg: object, *args: object, extra: MutableMapping[str, object] | None = None,
log_error: bool = True
) -> None:
""" """
log on exceotion level, this is log.exception, but logs with a new level log on exceotion level, this is log.exception, but logs with a new level
@@ -462,12 +488,19 @@ class Log:
msg (object): _description_ msg (object): _description_
*args (object): arguments for msg *args (object): arguments for msg
extra: Mapping[str, object] | None: extra arguments for the formatting if needed extra: Mapping[str, object] | None: extra arguments for the formatting if needed
log_error: (bool): If set to false will not write additional error message for console (Default True)
""" """
if not hasattr(self, 'logger'): if not hasattr(self, 'logger'):
raise ValueError('Logger is not yet initialized') raise ValueError('Logger is not yet initialized')
if extra is None: if extra is None:
extra = {} extra = {}
extra['stack_trace'] = traceback_call_str(start=3) extra['stack_trace'] = traceback_call_str(start=3)
# write to console first with extra flag for filtering in file
if log_error:
self.logger.log(
LoggingLevel.ERROR.value,
f"<=EXCEPTION> {msg}", *args, extra=dict(extra) | {'console': True}, stacklevel=2
)
self.logger.log(LoggingLevel.EXCEPTION.value, msg, *args, exc_info=True, extra=extra, stacklevel=2) self.logger.log(LoggingLevel.EXCEPTION.value, msg, *args, exc_info=True, extra=extra, stacklevel=2)
# MARK: break line # MARK: break line

View File

@@ -76,7 +76,7 @@ def main():
print(f"Divied: {__test}") print(f"Divied: {__test}")
except ZeroDivisionError as e: except ZeroDivisionError as e:
log.logger.critical("Divison through zero: %s", e) log.logger.critical("Divison through zero: %s", e)
log.exception("Divison through zero") log.exception("Divison through zero: %s", e)
for handler in log.logger.handlers: for handler in log.logger.handlers:
print(f"Handler (logger) {handler} -> {handler.level} -> {LoggingLevel.from_any(handler.level)}") print(f"Handler (logger) {handler} -> {handler.level} -> {LoggingLevel.from_any(handler.level)}")

2
uv.lock generated
View File

@@ -44,7 +44,7 @@ wheels = [
[[package]] [[package]]
name = "corelibs" name = "corelibs"
version = "0.12.6" version = "0.13.2"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "jmespath" }, { name = "jmespath" },