diff --git a/.gitignore b/.gitignore index 0ecf2d9..dc58055 100644 --- a/.gitignore +++ b/.gitignore @@ -10,9 +10,11 @@ plugins/__pycache__/ *.pyd *$py.class custom_plugins/ +logs/* plugins/custom/* plugins/community/* # Allow the directories themselves to be tracked +!logs/.gitkeep !plugins/custom/.gitkeep !plugins/community/.gitkeep \ No newline at end of file diff --git a/log_utils.py b/log_utils.py index 38c3c51..c8f3ac5 100644 --- a/log_utils.py +++ b/log_utils.py @@ -1,21 +1,46 @@ import logging - +import os +from logging.handlers import RotatingFileHandler from config import relay_config - def get_logger(name): logger = logging.getLogger(name=name) log_level = getattr(logging, relay_config["logging"]["level"].upper()) - logger.setLevel(log_level) - logger.propagate = False # Add this line to prevent double logging + logger.propagate = False - handler = logging.StreamHandler() - handler.setFormatter( + # Add stream handler (console logging) + stream_handler = logging.StreamHandler() + stream_handler.setFormatter( logging.Formatter( fmt="%(asctime)s %(levelname)s:%(name)s:%(message)s", datefmt="%Y-%m-%d %H:%M:%S %z", ) ) - logger.addHandler(handler) + logger.addHandler(stream_handler) + + # Check if file logging is enabled + if relay_config["logging"].get("log_to_file", False): + # Default to `logs/mmrelay.log` if no filename is provided + log_file = relay_config["logging"].get("filename", "logs/mmrelay.log") + + # Only create directories if the path is not the default + if log_file != "logs/mmrelay.log": + log_dir = os.path.dirname(log_file) + if log_dir: # Ensure non-empty directory paths exist + os.makedirs(log_dir, exist_ok=True) + + # Set up size-based log rotation + max_bytes = relay_config["logging"].get("max_log_size", 10 * 1024 * 1024) # Default 10 MB + backup_count = relay_config["logging"].get("backup_count", 5) # Default to 5 backups + file_handler = RotatingFileHandler(log_file, maxBytes=max_bytes, backupCount=backup_count) + + file_handler.setFormatter( + logging.Formatter( + fmt="%(asctime)s %(levelname)s:%(name)s:%(message)s", + datefmt="%Y-%m-%d %H:%M:%S %z", + ) + ) + logger.addHandler(file_handler) + return logger diff --git a/logs/.gitkeep b/logs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/plugins/map_plugin.py b/plugins/map_plugin.py index 47895de..4c2756c 100644 --- a/plugins/map_plugin.py +++ b/plugins/map_plugin.py @@ -6,11 +6,13 @@ import s2sphere import staticmaps +from log_utils import get_logger from nio import AsyncClient, UploadResponse from PIL import Image, ImageFont from plugins.base_plugin import BasePlugin +logger = get_logger(__name__) class TextLabel(staticmaps.Object): def __init__(self, latlng: s2sphere.LatLng, text: str, fontSize: int = 12) -> None: @@ -53,7 +55,8 @@ def render_pillow(self, renderer: staticmaps.PillowRenderer) -> None: try: font = ImageFont.truetype(path, self._font_size) break - except Exception: + except OSError: + logger.warning(f"Failed to load font from {path}") pass if not font: @@ -281,7 +284,7 @@ async def handle_room_message(self, room, event, full_message): try: zoom = int(zoom) - except: + except ValueError: zoom = self.config.get("zoom", 13) if zoom < 0 or zoom > 30: @@ -289,7 +292,7 @@ async def handle_room_message(self, room, event, full_message): try: image_size = (int(image_size[0]), int(image_size[1])) - except: + except (ValueError, TypeError): image_size = ( self.config.get("image_width", 1000), self.config.get("image_height", 1000), diff --git a/sample_config.yaml b/sample_config.yaml index 7167781..bf8ec84 100644 --- a/sample_config.yaml +++ b/sample_config.yaml @@ -20,6 +20,10 @@ meshtastic: logging: level: info + log_to_file: true + filename: logs/mmrelay.log # Default location + max_log_size: 10485760 # 10 MB (default if omitted) + backup_count: 5 # Keep 5 backups (default if omitted) #Note: Some plugins are experimental and some need maintenance. plugins: @@ -28,6 +32,7 @@ plugins: nodes: active: true # Other core plugins.. + #community-plugins: # Note: Community plugins are a new feature. Please report any issues. # sample_plugin: # active: true