Coverage for seedboxsync/core/logger.py: 100%
34 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-31 17:10 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-31 17:10 +0000
1#
2# Copyright (C) 2015-2026 Guillaume Kulakowski <guillaume@kulakowski.fr>
3#
4# For the full copyright and license information, please view the LICENSE
5# file that was distributed with this source code.
6"""Setup logger for Flask."""
8from datetime import datetime
9import logging
10from typing import ClassVar
11import click
12from seedboxsync.core.database.models import Log
15class DatabaseLogHandler(logging.Handler):
16 """Persist log records using the application's Log model."""
18 CLEANUP_INTERVAL = 500
19 MAX_RECORDS = 1000
21 def emit(self, record: logging.LogRecord) -> None:
22 """Write a formatted log record to the database."""
23 try:
24 log_entry = Log.create(
25 level=record.levelname,
26 logger=record.name,
27 module=record.module,
28 message=record.getMessage(),
29 timestamp=datetime.fromtimestamp(record.created),
30 )
31 if log_entry.id % self.CLEANUP_INTERVAL == 0:
32 latest_logs = Log.select(Log.id).order_by(Log.id.desc()).limit(self.MAX_RECORDS)
33 Log.delete().where(Log.id.not_in(latest_logs)).execute()
34 except Exception:
35 self.handleError(record)
38class ColorFormatter(logging.Formatter):
39 """Format log records with colors based on their level."""
41 COLORS: ClassVar[dict[int, str]] = {
42 logging.DEBUG: "bright_black",
43 logging.INFO: "blue",
44 logging.WARNING: "yellow",
45 logging.ERROR: "red",
46 logging.CRITICAL: "bright_red",
47 }
49 def format(self, record: logging.LogRecord) -> str:
50 """
51 Format a log record and apply a color based on its log level.
53 Args:
54 record: The log record to format.
56 Returns:
57 str: The formatted and colorized log message.
58 """
59 message = super().format(record)
60 color = self.COLORS.get(record.levelno)
62 if color is None:
63 return message
65 return click.style(message, fg=color)
68def configure_logger(logger: logging.Logger) -> None:
69 """
70 Configure the logger by applying the custom formatter to all existing handlers.
72 Args:
73 logger: The logger to configure.
74 """
75 formatter = ColorFormatter(
76 fmt="%(asctime)s [%(levelname)-8s] [%(module)s] %(message)s",
77 datefmt="%H:%M:%S",
78 )
80 for handler in logger.handlers:
81 handler.setFormatter(formatter)
84def add_database_log_handler(logger: logging.Logger) -> None:
85 """
86 Add a handler that persists records to the database.
88 Args:
89 logger: The logger to connect to the database log handler.
90 """
91 for handler in [handler for handler in logger.handlers if isinstance(handler, DatabaseLogHandler)]:
92 logger.removeHandler(handler)
93 handler.close()
95 database_handler = DatabaseLogHandler()
96 logger.addHandler(database_handler)