summaryrefslogtreecommitdiffstats
path: root/python/knot_resolver_manager/manager/log.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/knot_resolver_manager/manager/log.py')
-rw-r--r--python/knot_resolver_manager/manager/log.py105
1 files changed, 105 insertions, 0 deletions
diff --git a/python/knot_resolver_manager/manager/log.py b/python/knot_resolver_manager/manager/log.py
new file mode 100644
index 00000000..95043f89
--- /dev/null
+++ b/python/knot_resolver_manager/manager/log.py
@@ -0,0 +1,105 @@
+import logging
+import logging.handlers
+import os
+import sys
+from typing import Optional
+
+from knot_resolver_manager.manager.config_store import ConfigStore, only_on_real_changes_update
+from knot_resolver_manager.manager.constants import STARTUP_LOG_LEVEL
+from knot_resolver_manager.manager.datamodel.config_schema import KresConfig
+from knot_resolver_manager.manager.datamodel.logging_schema import LogTargetEnum
+
+logger = logging.getLogger(__name__)
+
+
+def get_log_format(config: KresConfig) -> str:
+ """
+ Based on an environment variable $KRES_SUPRESS_LOG_PREFIX, returns the appropriate format string for logger.
+ """
+
+ if os.environ.get("KRES_SUPRESS_LOG_PREFIX") == "true":
+ # In this case, we are running under supervisord and it's adding prefixes to our output
+ return "[%(levelname)s] %(name)s: %(message)s"
+ else:
+ # In this case, we are running standalone during inicialization and we need to add a prefix to each line
+ # by ourselves to make it consistent
+ assert config.logging.target != "syslog"
+ stream = ""
+ if config.logging.target == "stderr":
+ stream = " (stderr)"
+
+ pid = os.getpid()
+ return f"%(asctime)s manager[{pid}]{stream}: [%(levelname)s] %(name)s: %(message)s"
+
+
+async def _set_log_level(config: KresConfig) -> None:
+ levels_map = {
+ "crit": "CRITICAL",
+ "err": "ERROR",
+ "warning": "WARNING",
+ "notice": "WARNING",
+ "info": "INFO",
+ "debug": "DEBUG",
+ }
+
+ # when logging group is set to make us log with DEBUG
+ if config.logging.groups and "manager" in config.logging.groups:
+ target = "DEBUG"
+ # otherwise, follow the standard log level
+ else:
+ target = levels_map[config.logging.level]
+
+ # expect exactly one existing log handler on the root
+ logger.warning(f"Changing logging level to '{target}'")
+ logging.getLogger().setLevel(target)
+
+
+async def _set_logging_handler(config: KresConfig) -> None:
+ target: Optional[LogTargetEnum] = config.logging.target
+
+ if target is None:
+ target = "stdout"
+
+ handler: logging.Handler
+ if target == "syslog":
+ handler = logging.handlers.SysLogHandler(address="/dev/log")
+ handler.setFormatter(logging.Formatter("%(name)s: %(message)s"))
+ elif target == "stdout":
+ handler = logging.StreamHandler(sys.stdout)
+ handler.setFormatter(logging.Formatter(get_log_format(config)))
+ elif target == "stderr":
+ handler = logging.StreamHandler(sys.stderr)
+ handler.setFormatter(logging.Formatter(get_log_format(config)))
+ else:
+ raise RuntimeError(f"Unexpected value '{target}' for log target in the config")
+
+ root = logging.getLogger()
+
+ # if we had a MemoryHandler before, we should give it the new handler where we can flush it
+ if isinstance(root.handlers[0], logging.handlers.MemoryHandler):
+ root.handlers[0].setTarget(handler)
+
+ # stop the old handler
+ root.handlers[0].flush()
+ root.handlers[0].close()
+ root.removeHandler(root.handlers[0])
+
+ # configure the new handler
+ root.addHandler(handler)
+
+
+@only_on_real_changes_update(lambda config: config.logging)
+async def _configure_logger(config: KresConfig) -> None:
+ await _set_logging_handler(config)
+ await _set_log_level(config)
+
+
+async def logger_init(config_store: ConfigStore) -> None:
+ await config_store.register_on_change_callback(_configure_logger)
+
+
+def logger_startup() -> None:
+ logging.getLogger().setLevel(STARTUP_LOG_LEVEL)
+ err_handler = logging.StreamHandler(sys.stderr)
+ err_handler.setFormatter(logging.Formatter(logging.BASIC_FORMAT))
+ logging.getLogger().addHandler(logging.handlers.MemoryHandler(10_000, logging.ERROR, err_handler))