Update logging with console output format changes

"console_format_type" with "normal", "condensed", "minimal" options
This sets the format of the console output, controlling the amount of detail shown.
normal show log title, file, function and line number
condensed show file and line number only
minimal shows only timestamp, log level and message
Default is normal

"console_iso_precision" with "seconds", "milliseconds", "microseconds" options
This sets the precision of the ISO timestamp in console logs.
Default is milliseconds

The timestamp output is now ISO8601 formatatted with time zone.
This commit is contained in:
Clemens Schwaighofer
2025-11-18 15:31:16 +09:00
parent 6a1724695e
commit 592652cff1
10 changed files with 290 additions and 7 deletions

View File

@@ -28,6 +28,8 @@ class LogSettings(TypedDict):
per_run_log: bool
console_enabled: bool
console_color_output_enabled: bool
console_format_type: str
console_iso_precision: str
add_start_info: bool
add_end_info: bool
log_queue: 'Queue[str] | None'
@@ -39,6 +41,18 @@ class LoggerInit(TypedDict):
log_queue: 'Queue[str] | None'
# show log title, file, function and line number types
CONSOLE_FORMAT_TYPE_NORMAL = 'normal'
# show file and line number only
CONSOLE_FORMAT_TYPE_CONDENSED = 'condensed'
# only show timestamp, log level and message
CONSOLE_FORMAT_TYPE_MINIMAL = 'minimal'
# for console ISO time format
CONSOLE_ISO_TIME_SECONDS = 'seconds'
CONSOLE_ISO_TIME_MILLISECONDS = 'milliseconds'
CONSOLE_ISO_TIME_MICROSECONDS = 'microseconds'
# MARK: Custom color filter
class CustomConsoleFormatter(logging.Formatter):
"""
@@ -56,6 +70,21 @@ class CustomConsoleFormatter(logging.Formatter):
LoggingLevel.EXCEPTION.name: Colors.magenta_bright, # will never be written to console
}
# def formatTime(self, record: logging.LogRecord, datefmt: str | None = None):
# """
# Set timestamp in ISO8601 format
# Arguments:
# record {logging.LogRecord} -- _description_
# Keyword Arguments:
# datefmt {str | None} -- _description_ (default: {None})
# Returns:
# _type_ -- _description_
# """
# return datetime.fromtimestamp(record.created).astimezone().isoformat(sep=' ', timespec='milliseconds')
def format(self, record: logging.LogRecord) -> str:
"""
set the color highlight
@@ -409,6 +438,9 @@ class Log(LogParent):
"per_run_log": False,
"console_enabled": True,
"console_color_output_enabled": True,
# do not print log title, file, function and line number
"console_format_type": CONSOLE_FORMAT_TYPE_NORMAL,
"console_iso_precision": CONSOLE_ISO_TIME_MILLISECONDS,
"add_start_info": True,
"add_end_info": False,
"log_queue": None,
@@ -461,8 +493,11 @@ class Log(LogParent):
if self.log_settings['console_enabled']:
# console
self.add_handler('stream_handler', self.__create_console_handler(
'stream_handler', self.log_settings['log_level_console'])
)
'stream_handler',
self.log_settings['log_level_console'],
console_format_type=self.log_settings['console_format_type'],
console_iso_precision=self.log_settings['console_iso_precision']
))
# add other handlers,
if other_handlers is not None:
for handler_key, handler in other_handlers.items():
@@ -518,6 +553,27 @@ class Log(LogParent):
if not isinstance(__setting := log_settings.get(__log_entry, ''), bool):
__setting = self.DEFAULT_LOG_SETTINGS.get(__log_entry, True)
default_log_settings[__log_entry] = __setting
# check console log type
default_log_settings['console_format_type'] = cast('str', log_settings.get(
'console_format_type', self.DEFAULT_LOG_SETTINGS['console_format_type']
))
# if not valid
if default_log_settings['console_format_type'] not in [
CONSOLE_FORMAT_TYPE_NORMAL,
CONSOLE_FORMAT_TYPE_CONDENSED,
CONSOLE_FORMAT_TYPE_MINIMAL,
]:
default_log_settings['console_format_type'] = self.DEFAULT_LOG_SETTINGS['console_format_type']
# check console iso time precision
default_log_settings['console_iso_precision'] = cast('str', log_settings.get(
'console_iso_precision', self.DEFAULT_LOG_SETTINGS['console_iso_precision']
))
if default_log_settings['console_iso_precision'] not in [
CONSOLE_ISO_TIME_SECONDS,
CONSOLE_ISO_TIME_MILLISECONDS,
CONSOLE_ISO_TIME_MICROSECONDS,
]:
default_log_settings['console_iso_precision'] = self.DEFAULT_LOG_SETTINGS['console_iso_precision']
# check log queue
__setting = log_settings.get('log_queue', self.DEFAULT_LOG_SETTINGS['log_queue'])
if __setting is not None:
@@ -554,7 +610,10 @@ class Log(LogParent):
# MARK: console handler
def __create_console_handler(
self, handler_name: str,
log_level_console: LoggingLevel = LoggingLevel.WARNING, filter_exceptions: bool = True
log_level_console: LoggingLevel = LoggingLevel.WARNING,
filter_exceptions: bool = True,
console_format_type: str = CONSOLE_FORMAT_TYPE_NORMAL,
console_iso_precision: str = CONSOLE_ISO_TIME_MILLISECONDS
) -> logging.StreamHandler[TextIO]:
# console logger
if not self.validate_log_level(log_level_console):
@@ -562,18 +621,42 @@ class Log(LogParent):
console_handler = logging.StreamHandler()
# format layouts
format_string = (
'[%(asctime)s.%(msecs)03d] '
# '[%(asctime)s.%(msecs)03d] '
'[%(asctime)s] '
'[%(name)s] '
'[%(filename)s:%(funcName)s:%(lineno)d] '
'<%(levelname)s> '
'%(message)s'
)
if console_format_type == CONSOLE_FORMAT_TYPE_CONDENSED:
format_string = (
'[%(asctime)s] '
'[%(filename)s:%(lineno)d] '
'<%(levelname)s> '
'%(message)s'
)
elif console_format_type == CONSOLE_FORMAT_TYPE_MINIMAL:
format_string = (
'[%(asctime)s] '
'<%(levelname)s> '
'%(message)s'
)
format_date = "%Y-%m-%d %H:%M:%S"
# color or not
if self.log_settings['console_color_output_enabled']:
formatter_console = CustomConsoleFormatter(format_string, datefmt=format_date)
else:
formatter_console = logging.Formatter(format_string, datefmt=format_date)
print(f"PREC: {console_iso_precision}")
# this one needs lambda self, ...
# logging.Formatter.formatTime = (
formatter_console.formatTime = (
lambda record, datefmt=None:
datetime
.fromtimestamp(record.created)
.astimezone()
.isoformat(sep="T", timespec=console_iso_precision)
)
console_handler.set_name(handler_name)
console_handler.setLevel(log_level_console.name)
# do not show exceptions logs on console
@@ -614,7 +697,8 @@ class Log(LogParent):
formatter_file_handler = logging.Formatter(
(
# time stamp
'[%(asctime)s.%(msecs)03d] '
# '[%(asctime)s.%(msecs)03d] '
'[%(asctime)s] '
# log name
'[%(name)s] '
# filename + pid
@@ -628,6 +712,13 @@ class Log(LogParent):
),
datefmt="%Y-%m-%dT%H:%M:%S",
)
formatter_file_handler.formatTime = (
lambda record, datefmt=None:
datetime
.fromtimestamp(record.created)
.astimezone()
.isoformat(sep="T", timespec="microseconds")
)
file_handler.set_name(handler_name)
file_handler.setLevel(log_level_file.name)
# do not show errors flagged with console (they are from exceptions)