runtime logger handler selector

This commit is contained in:
2023-05-29 03:41:11 +03:00
parent 6c3b2ef266
commit 91e548569d
23 changed files with 272 additions and 48 deletions

View File

@ -30,6 +30,7 @@ from ahriman.application import handlers
from ahriman.core.util import enum_values
from ahriman.models.action import Action
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.log_handler import LogHandler
from ahriman.models.package_source import PackageSource
from ahriman.models.sign_settings import SignSettings
from ahriman.models.user_access import UserAccess
@ -58,6 +59,7 @@ def _formatter(prog: str) -> argparse.HelpFormatter:
return argparse.ArgumentDefaultsHelpFormatter(prog, width=120)
# pylint: disable=too-many-statements
def _parser() -> argparse.ArgumentParser:
"""
command line parser generator
@ -75,6 +77,9 @@ def _parser() -> argparse.ArgumentParser:
parser.add_argument("--force", help="force run, remove file lock", action="store_true")
parser.add_argument("-l", "--lock", help="lock file", type=Path,
default=Path(tempfile.gettempdir()) / "ahriman.lock")
parser.add_argument("--log-handler", help="explicit log handler specification. If none set, the handler will be "
"guessed from environment",
type=LogHandler, choices=enum_values(LogHandler))
parser.add_argument("--report", help="force enable or disable reporting to web service",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("-q", "--quiet", help="force disable any logging", action="store_true")

View File

@ -94,7 +94,8 @@ class Handler:
"""
try:
configuration = Configuration.from_path(args.configuration, architecture)
Log.load(configuration, quiet=args.quiet, report=args.report)
log_handler = Log.handler(args.log_handler)
Log.load(configuration, log_handler, quiet=args.quiet, report=args.report)
with Lock(args, architecture, configuration):
cls.run(args, architecture, configuration, report=args.report, unsafe=args.unsafe)
return True

View File

@ -0,0 +1,47 @@
#
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from logging import NullHandler
from typing import Any
__all__ = ["JournalHandler"]
class _JournalHandler(NullHandler):
"""
wrapper for unexpected args and kwargs
"""
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""
default constructor
Args:
*args(Any): positional arguments
**kwargs(Any): keyword arguments
"""
NullHandler.__init__(self)
del args, kwargs
try:
from systemd.journal import JournalHandler # type: ignore[import]
except ImportError:
JournalHandler = _JournalHandler

View File

@ -20,9 +20,11 @@
import logging
from logging.config import fileConfig
from pathlib import Path
from ahriman.core.configuration import Configuration
from ahriman.core.log.http_log_handler import HttpLogHandler
from ahriman.models.log_handler import LogHandler
class Log:
@ -32,24 +34,65 @@ class Log:
Attributes:
DEFAULT_LOG_FORMAT(str): (class attribute) default log format (in case of fallback)
DEFAULT_LOG_LEVEL(int): (class attribute) default log level (in case of fallback)
DEFAULT_SYSLOG_DEVICE(Path): (class attribute) default path to syslog device
"""
DEFAULT_LOG_FORMAT = "[%(levelname)s %(asctime)s] [%(filename)s:%(lineno)d %(funcName)s]: %(message)s"
DEFAULT_LOG_LEVEL = logging.DEBUG
DEFAULT_SYSLOG_DEVICE = Path("/dev") / "log"
@staticmethod
def load(configuration: Configuration, *, quiet: bool, report: bool) -> None:
def handler(selected: LogHandler | None) -> LogHandler:
"""
try to guess default log handler. In case if ``selected`` is set, it will return specified value with appended
_handler suffix. Otherwise, it will try to import journald handler and returns ``journald_handler`` if library
is available. Otherwise, it will check if there is ``/dev/log`` device and returns ``syslog_handler`` in this
case. And, finally, it will fall back to ``console_handler`` if none were found
Args:
selected(LogHandler | None): user specified handler if any
Returns:
LogHandler: selected log handler
"""
if selected is not None:
return selected
try:
from systemd.journal import JournalHandler # type: ignore[import]
del JournalHandler
return LogHandler.Journald # journald import was found
except ImportError:
if Log.DEFAULT_SYSLOG_DEVICE.exists():
return LogHandler.Syslog
return LogHandler.Console
@staticmethod
def load(configuration: Configuration, handler: LogHandler, *, quiet: bool, report: bool) -> None:
"""
setup logging settings from configuration
Args:
configuration(Configuration): configuration instance
handler(LogHandler): selected default log handler, which will be used if no handlers were set
quiet(bool): force disable any log messages
report(bool): force enable or disable reporting
"""
default_handler = f"{handler.value}_handler"
try:
path = configuration.logging_path
fileConfig(path)
log_configuration = Configuration()
log_configuration.read(configuration.logging_path)
# set handlers if they are not set
for section in filter(lambda s: s.startswith("logger_"), log_configuration.sections()):
if "handlers" in log_configuration[section]:
continue
log_configuration.set_option(section, "handlers", default_handler)
# load logging configuration
fileConfig(log_configuration, disable_existing_loggers=True)
logging.debug("using %s logger", default_handler)
except Exception:
logging.basicConfig(filename=None, format=Log.DEFAULT_LOG_FORMAT,
level=Log.DEFAULT_LOG_LEVEL)

View File

@ -0,0 +1,35 @@
#
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from enum import Enum
class LogHandler(str, Enum):
"""
log handler as described by default configuration
Attributes:
Console(LogHandler): (class attribute) write logs to console
Syslog(LogHandler): (class attribute) write logs to syslog device /dev/null
Journald(LogHandler): (class attribute) write logs to journald directly
"""
Console = "console"
Syslog = "syslog"
Journald = "journald"