From 48a5fa22855cd2f586bd431bb2967075cdec6c44 Mon Sep 17 00:00:00 2001 From: BreadTube Date: Wed, 27 May 2026 14:56:39 +0900 Subject: [PATCH] Add logging sharing in bot channel --- breadtube_bot/bot.py | 10 ++++++++-- breadtube_bot/config.py | 1 + breadtube_bot/logger.py | 33 +++++++++++++++++++++++++++++---- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/breadtube_bot/bot.py b/breadtube_bot/bot.py index 2fee54b..50647d1 100644 --- a/breadtube_bot/bot.py +++ b/breadtube_bot/bot.py @@ -60,7 +60,8 @@ class Bot: log_level: int = logging.INFO): self.config: Config = config or Config() self.guild_id = guild_id - self.logger = create_logger('breadtube', log_level, stdout=True) + self.logger, self.logger_memory = create_logger( + 'breadtube', log_level, self.config.log_buffer_length, stdout=True) self.version = self._get_code_version() self.discord_manager = DiscordManager(bot_token=bot_token, bot_version=self.version, logger=self.logger) @@ -91,6 +92,7 @@ class Bot: self.guild_text_channels: list[TextChannel] = text_channel self.guild_categories: list[ChannelCategory] = categories self.init_message: Message | None = None + self.init_message_update: float = time.time() bot_channel: TextChannel | None = None for _ in range(self.config.bot_channel_init_retries): @@ -392,6 +394,7 @@ class Bot: # New init message is needed, previous need to be deleted if self.init_message is not None and ( new_config is not None or new_subscriptions is not None + or self.logger_memory.last_update < self.init_message_update or self.init_message.content != self.INIT_MESSAGE): self.logger.info('Refreshing init message') immediate_delete[self.init_message.id] = self.init_message @@ -404,8 +407,11 @@ class Bot: self.bot_channel, {'content': self.INIT_MESSAGE}, request_timeout=self.config.request_timeout, upload_files=[ ('config.txt', FileMime.TEXT_PLAIN, self.config.to_str().encode()), - ('subscriptions.csv', FileMime.TEXT_CSV, SubscriptionHelper.generate_text(self._yt_subscriptions)) + ('subscriptions.csv', FileMime.TEXT_CSV, SubscriptionHelper.generate_text(self._yt_subscriptions)), + ('bot.log', FileMime.TEXT_PLAIN, b'\n'.join([ + r.getMessage().encode() for r in self.logger_memory.buffer])) ]) + self.init_message_update = self.logger_memory.last_update for message in immediate_delete.values(): try: diff --git a/breadtube_bot/config.py b/breadtube_bot/config.py index 8475756..76f41f0 100644 --- a/breadtube_bot/config.py +++ b/breadtube_bot/config.py @@ -11,6 +11,7 @@ class Config: bot_channel_init_retries: int = 3 bot_message_duration: float = 150. delete_duplicate_channels: bool = True + log_buffer_length: int = 200 request_timeout: float = 3. unmanaged_categories: str = '' youtube_channel_refresh_interval: float = 600 diff --git a/breadtube_bot/logger.py b/breadtube_bot/logger.py index 62fe446..6a5a665 100644 --- a/breadtube_bot/logger.py +++ b/breadtube_bot/logger.py @@ -1,7 +1,27 @@ +from collections import deque from logging import handlers import logging from pathlib import Path import sys +import time + + +class MemoryHandler(logging.Handler): + def __init__(self, capacity): + """ + Initialize the handler with the buffer size. + """ + logging.Handler.__init__(self) + self.buffer: deque[logging.LogRecord] = deque(maxlen=capacity) + self.last_update = time.time() + + def emit(self, record: logging.LogRecord): + self.buffer.append(record) + self.last_update = time.time() + + def close(self): + self.buffer.clear() + logging.Handler.close(self) class ConsoleColor: @@ -34,19 +54,24 @@ class ColoredFormatter(logging.Formatter): return logging.Formatter.format(self, record) -def create_logger(name: str, level: int, log_dir: Path | None = None, stdout=False) -> logging.Logger: +def create_logger(name: str, level: int, buffer_capacity: int, log_dir: Path | None = None, + stdout=False) -> tuple[logging.Logger, MemoryHandler]: logger = logging.getLogger(name) logger.setLevel(level) + log_formatter = logging.Formatter('%(asctime)s %(levelname)s : %(message)s') + buffer_handler = MemoryHandler(buffer_capacity) + buffer_handler.setFormatter(log_formatter) + buffer_handler.setLevel(level) + logger.addHandler(buffer_handler) + if log_dir is not None: log_dir.mkdir(parents=True, exist_ok=True) - logger.setLevel(level) file_log_handler = handlers.RotatingFileHandler( log_dir / f'{name}.log', maxBytes=500000, backupCount=5) file_log_handler.setLevel(level) - log_formatter = logging.Formatter('%(asctime)s %(levelname)s : %(message)s') file_log_handler.setFormatter(log_formatter) logger.addHandler(file_log_handler) @@ -57,4 +82,4 @@ def create_logger(name: str, level: int, log_dir: Path | None = None, stdout=Fal terminal_log_handler.setFormatter(colored_log_formatter) logger.addHandler(terminal_log_handler) - return logger + return logger, buffer_handler