From 92c483e2ce4a631c32f4f0f2121a9d652088a916 Mon Sep 17 00:00:00 2001 From: Yannick Dayer <60428834+Yannick-Dayer@users.noreply.github.com> Date: Thu, 26 Sep 2024 21:50:43 +0200 Subject: [PATCH] Feature: add an option for a custom formatter in logging setup (#10) * feat(logging): setup support user-given formatters Allow the user to provide a custom formatter class to replace the default `logging.Formatter`. This should allow more flexibility to customize the logs output. The `format` argument is kept for backward compatibility. * test(logging): ensure setup's formatter is handled Test that the user can set a custom formatter in `clapper.logging.setup` --- src/clapper/logging.py | 19 +++++++++++++------ tests/test_logging.py | 26 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/clapper/logging.py b/src/clapper/logging.py index c5845a7..4e72ede 100644 --- a/src/clapper/logging.py +++ b/src/clapper/logging.py @@ -26,6 +26,7 @@ def setup( format: str = "[%(levelname)s] %(message)s (%(name)s, %(asctime)s)", # noqa: A002 low_level_stream: typing.TextIO = sys.stdout, high_level_stream: typing.TextIO = sys.stderr, + formatter: logging.Formatter | None = None, ) -> logging.Logger: """Return a logger object that is ready for console logging. @@ -40,10 +41,13 @@ def setup( and above to the text-I/O stream ``high_level_stream``, with an internal level set to ``logging.WARNING``. - A new formatter, with the format string as defined by the ``format`` - argument is set on both handlers. In this way, the global logger level can - still be controlled from one single place. If output is generated, then it - is sent to the right stream. + If you do not provide a ``formatter`` argument, a new formatter, with the + format string as defined by the ``format`` argument is set on both handlers. + In this way, the global logger level can still be controlled from one single + place. If output is generated, then it is sent to the right stream. + + You can provide a custom formatter if ``format`` is not sufficient to + customize the looks of your logs (e.g. you want to add colors). Parameters ---------- @@ -52,11 +56,13 @@ def setup( format The format of the logs, see :py:class:`logging.LogRecord` for more details. By default, the log contains the logger name, the log time, - the log level and the massage. + the log level and the message. *Ignored if ``formatter`` is set*. low_level_stream The stream where to output info messages and below high_level_stream The stream where to output warning messages and above + formatter + A formatter to replace the default :py:class:`logging.Formatter`. Returns ------- @@ -66,7 +72,8 @@ def setup( logger = logging.getLogger(logger_name) - formatter = logging.Formatter(format) + if formatter is None: + formatter = logging.Formatter(format) handlers_installed = {k.name: k for k in logger.handlers} debug_logger_name = f"debug_info+{logger_name}" diff --git a/tests/test_logging.py b/tests/test_logging.py index 6d4a4e9..ed6daed 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -35,6 +35,32 @@ def test_logger_setup(): assert hi.getvalue() == "warning message\nerror message\n" +def test_logger_setup_formatter(): + log_output = io.StringIO() + + custom_formatter = logging.Formatter(fmt="custom {message:s}", style="{") + + logger = clapper.logging.setup( + "awesome.logger", + low_level_stream=log_output, + high_level_stream=log_output, + formatter=custom_formatter, + ) + logger.setLevel(logging.DEBUG) + + logger.debug("debug message") + logger.info("info message") + logger.warning("warning message") + logger.error("error message") + + assert log_output.getvalue() == ( + "custom debug message\n" + "custom info message\n" + "custom warning message\n" + "custom error message\n" + ) + + def test_logger_click_no_v(): lo = io.StringIO() hi = io.StringIO()