From e6d0de8f3c47cff20cf024d0fd38c8f5375da58b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Misty=20De=20M=C3=A9o?= Date: Mon, 24 Feb 2025 14:53:13 -0800 Subject: [PATCH] CLI: enable log prefixing This adds a commandline option which enables log level prefixing. These prefixes enable log level-based filtering in journalctl when present so long as logs are going to the journal, and `SyslogLevelPrefix=` is set to `true` (which it is by default). For documentation: https://manpages.debian.org/testing/libsystemd-dev/sd-daemon.3.en.html --- brozzler/cli.py | 62 ++++++++++++++++++++++++++++++----------- tests/test_brozzling.py | 4 ++- tests/test_frontier.py | 4 ++- 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/brozzler/cli.py b/brozzler/cli.py index 2112ae6..7a7b7b1 100755 --- a/brozzler/cli.py +++ b/brozzler/cli.py @@ -74,6 +74,12 @@ def add_common_options(arg_parser, argv=None): const=logging.DEBUG, help=("very verbose logging"), ) + arg_parser.add_argument( + "--syslogd-log-prefix", + dest="syslogd_log_prefix", + action="store_true", + help="add syslogd log level prefix for journalctl filtering", + ) # arg_parser.add_argument( # '-s', '--silent', dest='log_level', action='store_const', # default=logging.INFO, const=logging.CRITICAL) @@ -131,24 +137,48 @@ def decorate_logger_name(a, b, event_dict): return event_dict +# https://manpages.debian.org/testing/libsystemd-dev/sd-daemon.3.en.html +def _systemd_log_prefix(_, level, log): + SYSLOG_MAP = { + "critical": 2, + "error": 3, + "exception": 3, + "warn": 3, + "warning": 3, + "info": 6, + "debug": 7, + "notset": 7, + } + prefix = SYSLOG_MAP.get(level) + if prefix is not None: + log = f"<{prefix}>{log}" + + return log + + def configure_logging(args): + processors = [ + structlog.contextvars.merge_contextvars, + structlog.processors.add_log_level, + structlog.processors.StackInfoRenderer(), + structlog.dev.set_exc_info, + structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S", utc=True), + structlog.processors.CallsiteParameterAdder( + [ + structlog.processors.CallsiteParameter.FILENAME, + structlog.processors.CallsiteParameter.FUNC_NAME, + structlog.processors.CallsiteParameter.LINENO, + ], + ), + decorate_logger_name, + structlog.dev.ConsoleRenderer(), + ] + + if args.syslogd_log_prefix: + processors.append(_systemd_log_prefix) + structlog.configure( - processors=[ - structlog.contextvars.merge_contextvars, - structlog.processors.add_log_level, - structlog.processors.StackInfoRenderer(), - structlog.dev.set_exc_info, - structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S", utc=True), - structlog.processors.CallsiteParameterAdder( - [ - structlog.processors.CallsiteParameter.FILENAME, - structlog.processors.CallsiteParameter.FUNC_NAME, - structlog.processors.CallsiteParameter.LINENO, - ], - ), - decorate_logger_name, - structlog.dev.ConsoleRenderer(), - ], + processors=processors, wrapper_class=structlog.make_filtering_bound_logger(args.log_level), context_class=dict, logger_factory=structlog.PrintLoggerFactory(), diff --git a/tests/test_brozzling.py b/tests/test_brozzling.py index 744a09a..c187c42 100755 --- a/tests/test_brozzling.py +++ b/tests/test_brozzling.py @@ -29,7 +29,9 @@ import json import threading import socket -args = argparse.Namespace() +arg_parser = argparse.ArgumentParser() +brozzler.cli.add_common_options(arg_parser) +args = arg_parser.parse_args([]) args.log_level = logging.INFO brozzler.cli.configure_logging(args) diff --git a/tests/test_frontier.py b/tests/test_frontier.py index 760962d..31628c4 100644 --- a/tests/test_frontier.py +++ b/tests/test_frontier.py @@ -28,7 +28,9 @@ import pytest import brozzler -args = argparse.Namespace() +arg_parser = argparse.ArgumentParser() +brozzler.cli.add_common_options(arg_parser) +args = arg_parser.parse_args([]) args.log_level = logging.INFO brozzler.cli.configure_logging(args)