mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-03-09 11:43:39 +00:00
125 lines
5.1 KiB
Python
125 lines
5.1 KiB
Python
#
|
|
# Copyright (c) 2021-2026 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/>.
|
|
#
|
|
import logging
|
|
|
|
from logging.config import fileConfig
|
|
from pathlib import Path
|
|
from typing import ClassVar, Literal
|
|
|
|
from ahriman.core.configuration import Configuration
|
|
from ahriman.core.log.http_log_handler import HttpLogHandler
|
|
from ahriman.core.log.log_context import LogContext
|
|
from ahriman.models.log_handler import LogHandler
|
|
from ahriman.models.repository_id import RepositoryId
|
|
|
|
|
|
class LogLoader:
|
|
"""
|
|
simple static method class which setups application loggers
|
|
|
|
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_LOG_STYLE(str): (class attribute) default log style (in case of fallback)
|
|
DEFAULT_SYSLOG_DEVICE(Path): (class attribute) default path to syslog device
|
|
"""
|
|
|
|
DEFAULT_LOG_FORMAT: ClassVar[str] = "[{levelname} {asctime}] [{name}]: {message}"
|
|
DEFAULT_LOG_LEVEL: ClassVar[int] = logging.DEBUG
|
|
DEFAULT_LOG_STYLE: ClassVar[Literal["%", "{", "$"]] = "{"
|
|
DEFAULT_SYSLOG_DEVICE: ClassVar[Path] = Path("/") / "dev" / "log"
|
|
|
|
@staticmethod
|
|
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
|
|
:attr:`ahriman.models.log_handler.LogHandler.Journald` if library is available. Otherwise, it will check if
|
|
there is ``/dev/log`` device and returns :attr:`ahriman.models.log_handler.LogHandler.Syslog` in this
|
|
case. And, finally, it will fall back to :attr:`ahriman.models.log_handler.LogHandler.Console`
|
|
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-untyped]
|
|
del JournalHandler
|
|
return LogHandler.Journald # journald import was found
|
|
except ImportError:
|
|
if LogLoader.DEFAULT_SYSLOG_DEVICE.exists():
|
|
return LogHandler.Syslog
|
|
return LogHandler.Console
|
|
|
|
@staticmethod
|
|
def load(repository_id: RepositoryId, configuration: Configuration, handler: LogHandler, *,
|
|
quiet: bool, report: bool) -> None:
|
|
"""
|
|
setup logging settings from configuration
|
|
|
|
Args:
|
|
repository_id(RepositoryId): repository unique identifier
|
|
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:
|
|
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=LogLoader.DEFAULT_LOG_FORMAT,
|
|
style=LogLoader.DEFAULT_LOG_STYLE, level=LogLoader.DEFAULT_LOG_LEVEL)
|
|
logging.exception("could not load logging from configuration, fallback to stderr")
|
|
|
|
HttpLogHandler.load(repository_id, configuration, report=report)
|
|
LogLoader.register_context()
|
|
|
|
if quiet:
|
|
logging.disable(logging.WARNING) # only print errors here
|
|
|
|
@staticmethod
|
|
def register_context() -> None:
|
|
"""
|
|
register logging context
|
|
"""
|
|
# predefined context variables
|
|
for variable in ("package_id", "request_id"):
|
|
LogContext.register(variable)
|
|
logging.setLogRecordFactory(LogContext.log_record_factory)
|