diff --git a/console.py b/console.py index 800d83f..25a33a0 100644 --- a/console.py +++ b/console.py @@ -1,11 +1,11 @@ import logging +import logging.config import re +from dataclasses import asdict, dataclass -from appdaemon.adapi import ADAPI from appdaemon.logging import AppNameFormatter from rich.console import Console from rich.highlighter import RegexHighlighter -from rich.logging import RichHandler from rich.theme import Theme @@ -43,6 +43,19 @@ console = Console( ) +class ContextSettingFilter(logging.Filter): + def filter(self, record: logging.LogRecord): + for name, val in asdict(self).items(): + setattr(record, name, val) + return record + + +@dataclass +class RoomControllerFilter(ContextSettingFilter): + room: str + component: str + + class UnMarkupFormatter(AppNameFormatter): md_regex = re.compile(r'(?P\[.*?\])(?P.*?)(?P\[\/\])') @@ -51,56 +64,63 @@ class UnMarkupFormatter(AppNameFormatter): return self.md_regex.sub(r'\g', result) -class RoomControllerFormatter(logging.Formatter): - def __init__(self, room: str, component: str = None): - self.log_fields = {'room': room} +def create_rich_logging_dict(parent_room: str, typ: str = None): + logger_name = f'room_control.{parent_room}' + if typ is not None: + logger_name += f'.{typ}' + fmt = '[room]{room:>12}[/] [component]{component:<9}[/]{message}' + else: + fmt = '[room]{room:>12}[/] {message}' - fmt = '[room]{room:>12}[/]' - if component is not None: - fmt += ' [component]{component:<9}[/]' - self.log_fields['component'] = component - fmt += ' {message}' - - datefmt = '%Y-%m-%d %I:%M:%S %p' - style = '{' - validate = True - - super().__init__(fmt, datefmt, style, validate) - # console.print(f'Format: [bold yellow]{fmt}[/]') - - def format(self, record: logging.LogRecord): - parts = record.name.split('.') - record.room = parts[1] - if len(parts) == 3: - record.component = parts[2] - - return super().format(record) - - -def new_handler() -> RichHandler: - return RichHandler( - console=console, - # highlighter=NullHighlighter(), - highlighter=RCHighlighter(), - markup=True, - show_path=False, - omit_repeated_times=False, - log_time_format='%Y-%m-%d %I:%M:%S %p', - ) - - -def setup_handler(**kwargs) -> RichHandler: - handler = new_handler() - handler.setFormatter(RoomControllerFormatter(**kwargs)) - return handler + LOG_CFG = { + 'version': 1, + 'disable_existing_loggers': False, + 'filters': { + logger_name: { + '()': 'console.RoomControllerFilter', + # '.': {'room': parent_room, 'component': typ}, + 'room': parent_room, + 'component': typ, + } + }, + 'formatters': { + logger_name: { + 'style': '{', + 'format': fmt, + # 'datefmt': '%Y-%m-%d %I:%M:%S %p', + } + }, + 'handlers': { + logger_name: { + 'filters': [logger_name], + 'formatter': logger_name, + '()': 'rich.logging.RichHandler', + 'markup': True, + 'log_time_format': '%Y-%m-%d %I:%M:%S %p', + 'show_path': False, + 'omit_repeated_times': False, + 'highlighter': RCHighlighter(), + 'console': console, + }, + }, + 'loggers': { + logger_name: { + 'level': 'INFO', + 'propagate': False, + 'handlers': [logger_name], + } + }, + } + return LOG_CFG def setup_component_logging(self): - typ = type(self).__name__ - logger = logging.getLogger(f'room_control.{self.args["app"]}') - self.logger = logger.getChild(typ) - if len(self.logger.handlers) == 0: - self.logger.setLevel(logging.INFO) - self.logger.addHandler(setup_handler(room=self.args['app'], component=typ)) - self.logger.propagate = False + cfg_dict = create_rich_logging_dict(parent_room=self.args['app'], typ=type(self).__name__) + logger_name = next(iter(cfg_dict['loggers'])) + try: + logging.config.dictConfig(cfg_dict) + except Exception: + console.print_exception() + else: + self.logger = logging.getLogger(logger_name) diff --git a/room_control.py b/room_control.py index 33f7d5e..3c6adf1 100755 --- a/room_control.py +++ b/room_control.py @@ -1,12 +1,13 @@ import datetime import logging +import logging.config from copy import deepcopy from typing import Dict, List from appdaemon.entity import Entity from appdaemon.plugins.hass.hassapi import Hass from appdaemon.plugins.mqtt.mqttapi import Mqtt -from console import console, setup_handler +from console import console, create_rich_logging_dict from model import ControllerStateConfig, RoomControllerConfig logger = logging.getLogger(__name__) @@ -32,11 +33,8 @@ class RoomController(Hass, Mqtt): self._room_config.states = new def initialize(self): - self.logger = logger.getChild(self.name) - if not self.logger.hasHandlers(): - self.logger.setLevel(self.args.get('rich', logging.INFO)) - self.logger.addHandler(setup_handler(room=self.name)) - # console.log(f'[yellow]Added RichHandler to {self.logger.name}[/]') + cfg_dict = create_rich_logging_dict(parent_room=self.name) + logging.config.dictConfig(cfg_dict) self.app_entities = self.gather_app_entities() # self.log(f'entities: {self.app_entities}') @@ -100,7 +98,11 @@ class RoomController(Hass, Mqtt): # t: datetime.time = state['time'] t: datetime.time = state.time try: - self.run_at(callback=self.activate_any_on, start=t.strftime('%H:%M:%S'), cause='scheduled transition') + self.run_at( + callback=self.activate_any_on, + start=t.strftime('%H:%M:%S'), + cause='scheduled transition', + ) except ValueError: # happens when the callback time is in the past pass