From 51e9b1ce7ce67911d5255c0ef7e8406ac0955975 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Wed, 19 Nov 2025 17:35:27 +0900 Subject: [PATCH] Add "LEVEL" option to console log format So we can set output to onle the message without any information (NONE), only level (BARE), time and level (MINIMAL), time, file, line and level (CONDENSED) or (ALL) full information. --- src/corelibs/logging_handling/log.py | 23 +++-- test-run/logging_handling/log.py | 8 +- ...g_1_settings_parsing_spacers_parameters.py | 19 ++++ .../test_log_3_custom_console_formatter.py | 25 +++++ .../test_log_5_handler_management.py | 98 +++++++++++++++++++ 5 files changed, 162 insertions(+), 11 deletions(-) diff --git a/src/corelibs/logging_handling/log.py b/src/corelibs/logging_handling/log.py index b2f20cb..15bdea5 100644 --- a/src/corelibs/logging_handling/log.py +++ b/src/corelibs/logging_handling/log.py @@ -32,6 +32,7 @@ class ConsoleFormat(Flag): FILE = auto() FUNCTION = auto() LINENO = auto() + LEVEL = auto() class ConsoleFormatSettings: @@ -43,14 +44,17 @@ class ConsoleFormatSettings: ConsoleFormat.NAME | ConsoleFormat.FILE | ConsoleFormat.FUNCTION | - ConsoleFormat.LINENO + ConsoleFormat.LINENO | + ConsoleFormat.LEVEL ) - # show time with no time zone, file and line - CONDENSED = ConsoleFormat.TIME | ConsoleFormat.FILE | ConsoleFormat.LINENO - # only time - MINIMAL = ConsoleFormat.TIME + # show time with no time zone, file, line and level + CONDENSED = ConsoleFormat.TIME | ConsoleFormat.FILE | ConsoleFormat.LINENO | ConsoleFormat.LEVEL + # only time and level + MINIMAL = ConsoleFormat.TIME | ConsoleFormat.LEVEL + # only level + BARE = ConsoleFormat.LEVEL # only message - BARE = ConsoleFormat(0) + NONE = ConsoleFormat(0) @staticmethod def from_string(setting_str: str, default: ConsoleFormat | None = None) -> ConsoleFormat | None: @@ -655,8 +659,11 @@ class Log(LogParent): set_group.append('%(lineno)d') format_string += ':'.join(set_group) format_string += '] ' - # always level + message - format_string += '<%(levelname)s> %(message)s' + # level if wanted + if ConsoleFormat.LEVEL in console_format_type: + format_string += '<%(levelname)s> ' + # always message + format_string += '%(message)s' return format_string def __set_time_format_for_console_formatter( diff --git a/test-run/logging_handling/log.py b/test-run/logging_handling/log.py index e1d0dc1..2a11213 100644 --- a/test-run/logging_handling/log.py +++ b/test-run/logging_handling/log.py @@ -25,11 +25,13 @@ def main(): "log_level_file": 'DEBUG', # "console_color_output_enabled": False, "per_run_log": True, - # "console_format_type": ConsoleFormatSettings.BARE, + # "console_format_type": ConsoleFormatSettings.NONE, # "console_format_type": ConsoleFormatSettings.MINIMAL, - "console_format_type": ConsoleFormat.TIME_MICROSECONDS | ConsoleFormat.NAME, + "console_format_type": ConsoleFormat.TIME_MICROSECONDS | ConsoleFormat.NAME | ConsoleFormat.LEVEL, # "console_format_type": ConsoleFormat.NAME, - # "console_format_type": ConsoleFormat.TIME | ConsoleFormat.TIMEZONE | ConsoleFormat.LINENO, + # "console_format_type": ( + # ConsoleFormat.TIME | ConsoleFormat.TIMEZONE | ConsoleFormat.LINENO | ConsoleFormat.LEVEL + # ), } ) logn = Logger(log.get_logger_settings()) diff --git a/tests/unit/logging_handling/log_testing/test_log_1_settings_parsing_spacers_parameters.py b/tests/unit/logging_handling/log_testing/test_log_1_settings_parsing_spacers_parameters.py index 60b4002..cd3e1f2 100644 --- a/tests/unit/logging_handling/log_testing/test_log_1_settings_parsing_spacers_parameters.py +++ b/tests/unit/logging_handling/log_testing/test_log_1_settings_parsing_spacers_parameters.py @@ -153,6 +153,19 @@ class TestLogSettingsParsing: assert log.log_settings["console_format_type"] == ConsoleFormatSettings.BARE + def test_parse_console_format_type_none(self, tmp_log_path: Path): + """Test parsing with console_format_type set to NONE""" + settings: dict[str, Any] = { + "console_format_type": ConsoleFormatSettings.NONE, + } + log = Log( + log_path=tmp_log_path, + log_name="test", + log_settings=settings # type: ignore + ) + + assert log.log_settings["console_format_type"] == ConsoleFormatSettings.NONE + def test_parse_console_format_type_invalid(self, tmp_log_path: Path): """Test parsing with invalid console_format_type raises TypeError""" settings: dict[str, Any] = { @@ -207,6 +220,11 @@ class TestConsoleFormatSettingsFromString: result = ConsoleFormatSettings.from_string('BARE') assert result == ConsoleFormatSettings.BARE + def test_from_string_none(self): + """Test from_string with 'NONE' returns correct format""" + result = ConsoleFormatSettings.from_string('NONE') + assert result == ConsoleFormatSettings.NONE + def test_from_string_invalid_returns_none(self): """Test from_string with invalid string returns None""" result = ConsoleFormatSettings.from_string('INVALID') @@ -234,6 +252,7 @@ class TestConsoleFormatSettingsFromString: ("CONDENSED", ConsoleFormatSettings.CONDENSED), ("MINIMAL", ConsoleFormatSettings.MINIMAL), ("BARE", ConsoleFormatSettings.BARE), + ("NONE", ConsoleFormatSettings.NONE), ]) def test_from_string_all_valid_settings(self, setting_name: str, expected: Any): """Test from_string with all valid setting names""" diff --git a/tests/unit/logging_handling/log_testing/test_log_3_custom_console_formatter.py b/tests/unit/logging_handling/log_testing/test_log_3_custom_console_formatter.py index 18b3bca..cd68375 100644 --- a/tests/unit/logging_handling/log_testing/test_log_3_custom_console_formatter.py +++ b/tests/unit/logging_handling/log_testing/test_log_3_custom_console_formatter.py @@ -178,6 +178,17 @@ class TestUpdateConsoleFormatter: # Verify formatter was updated assert formatter is not None + def test_update_console_formatter_to_none(self, log_instance: Log): + """Test updating console formatter to NONE format""" + log_instance.update_console_formatter(ConsoleFormatSettings.NONE) + + # Get the console handler's formatter + console_handler = log_instance.handlers[log_instance.CONSOLE_HANDLER] + formatter = console_handler.formatter + + # Verify formatter was updated + assert formatter is not None + def test_update_console_formatter_to_all(self, log_instance: Log): """Test updating console formatter to ALL format""" log_instance.update_console_formatter(ConsoleFormatSettings.ALL) @@ -283,4 +294,18 @@ class TestUpdateConsoleFormatter: # Verify message was logged assert "Test warning message" in caplog.text + def test_update_console_formatter_none_format_output( + self, log_instance: Log, caplog: pytest.LogCaptureFixture + ): + """Test that NONE formatter outputs only the message without any formatting""" + # Set to NONE format (message only, no level indicator) + log_instance.update_console_formatter(ConsoleFormatSettings.NONE) + + # Configure caplog to capture at the appropriate level + with caplog.at_level(logging.WARNING): + log_instance.warning("Test warning message") + + # Verify message was logged + assert "Test warning message" in caplog.text + # __END__ diff --git a/tests/unit/logging_handling/log_testing/test_log_5_handler_management.py b/tests/unit/logging_handling/log_testing/test_log_5_handler_management.py index bde8652..c97cdce 100644 --- a/tests/unit/logging_handling/log_testing/test_log_5_handler_management.py +++ b/tests/unit/logging_handling/log_testing/test_log_5_handler_management.py @@ -12,6 +12,7 @@ from corelibs.logging_handling.log import ( LogParent, LogSettings, ConsoleFormatSettings, + ConsoleFormat, ) from corelibs.logging_handling.logging_level_handling.logging_level import LoggingLevel @@ -108,4 +109,101 @@ class TestHandlerManagement: result2 = log.add_handler("test", handler2) assert result2 is False + def test_change_console_format_to_minimal(self, log_instance: Log): + """Test changing console handler format to MINIMAL""" + original_formatter = log_instance.handlers[log_instance.CONSOLE_HANDLER].formatter + + log_instance.update_console_formatter(ConsoleFormatSettings.MINIMAL) + + new_formatter = log_instance.handlers[log_instance.CONSOLE_HANDLER].formatter + assert new_formatter is not original_formatter + assert new_formatter is not None + + def test_change_console_format_to_condensed(self, log_instance: Log): + """Test changing console handler format to CONDENSED""" + log_instance.update_console_formatter(ConsoleFormatSettings.CONDENSED) + + formatter = log_instance.handlers[log_instance.CONSOLE_HANDLER].formatter + assert formatter is not None + + def test_change_console_format_to_bare(self, log_instance: Log): + """Test changing console handler format to BARE""" + log_instance.update_console_formatter(ConsoleFormatSettings.BARE) + + formatter = log_instance.handlers[log_instance.CONSOLE_HANDLER].formatter + assert formatter is not None + + def test_change_console_format_to_none(self, log_instance: Log): + """Test changing console handler format to NONE""" + log_instance.update_console_formatter(ConsoleFormatSettings.NONE) + + formatter = log_instance.handlers[log_instance.CONSOLE_HANDLER].formatter + assert formatter is not None + + def test_change_console_format_to_all(self, log_instance: Log): + """Test changing console handler format to ALL""" + # Start with a different format + log_instance.update_console_formatter(ConsoleFormatSettings.MINIMAL) + + log_instance.update_console_formatter(ConsoleFormatSettings.ALL) + + formatter = log_instance.handlers[log_instance.CONSOLE_HANDLER].formatter + assert formatter is not None + + def test_change_console_format_multiple_times(self, log_instance: Log): + """Test changing console handler format multiple times""" + formatters: list[logging.Formatter | None] = [] + + for format_type in [ + ConsoleFormatSettings.MINIMAL, + ConsoleFormatSettings.CONDENSED, + ConsoleFormatSettings.BARE, + ConsoleFormatSettings.NONE, + ConsoleFormatSettings.ALL, + ]: + log_instance.update_console_formatter(format_type) + formatter = log_instance.handlers[log_instance.CONSOLE_HANDLER].formatter + formatters.append(formatter) + assert formatter is not None + + # Verify each formatter is unique (new instance each time) + for i, formatter in enumerate(formatters): + for j, other_formatter in enumerate(formatters): + if i != j: + assert formatter is not other_formatter + + def test_change_console_format_with_disabled_console( + self, tmp_log_path: Path, basic_log_settings: LogSettings + ): + """Test changing console format when console is disabled does nothing""" + basic_log_settings['console_enabled'] = False + log = Log( + log_path=tmp_log_path, + log_name="test_log", + log_settings=basic_log_settings + ) + + # Should not raise error, just return early + log.update_console_formatter(ConsoleFormatSettings.MINIMAL) + + # Console handler should not exist + assert log.CONSOLE_HANDLER not in log.handlers + + @pytest.mark.parametrize("format_type", [ + ConsoleFormatSettings.ALL, + ConsoleFormatSettings.CONDENSED, + ConsoleFormatSettings.MINIMAL, + ConsoleFormatSettings.BARE, + ConsoleFormatSettings.NONE, + ]) + def test_change_console_format_parametrized( + self, log_instance: Log, format_type: ConsoleFormat # type: ignore + ): + """Test changing console format with all format types""" + log_instance.update_console_formatter(format_type) + + formatter = log_instance.handlers[log_instance.CONSOLE_HANDLER].formatter + assert formatter is not None + assert isinstance(formatter, logging.Formatter) + # __END__