simplified rich logging with dictconfig

This commit is contained in:
John Lancaster
2024-04-27 10:24:45 -05:00
parent dc7151549b
commit 6250b6b20c
2 changed files with 79 additions and 57 deletions

View File

@@ -1,11 +1,11 @@
import logging import logging
import logging.config
import re import re
from dataclasses import asdict, dataclass
from appdaemon.adapi import ADAPI
from appdaemon.logging import AppNameFormatter from appdaemon.logging import AppNameFormatter
from rich.console import Console from rich.console import Console
from rich.highlighter import RegexHighlighter from rich.highlighter import RegexHighlighter
from rich.logging import RichHandler
from rich.theme import Theme 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): class UnMarkupFormatter(AppNameFormatter):
md_regex = re.compile(r'(?P<open>\[.*?\])(?P<text>.*?)(?P<close>\[\/\])') md_regex = re.compile(r'(?P<open>\[.*?\])(?P<text>.*?)(?P<close>\[\/\])')
@@ -51,56 +64,63 @@ class UnMarkupFormatter(AppNameFormatter):
return self.md_regex.sub(r'\g<text>', result) return self.md_regex.sub(r'\g<text>', result)
class RoomControllerFormatter(logging.Formatter): def create_rich_logging_dict(parent_room: str, typ: str = None):
def __init__(self, room: str, component: str = None): logger_name = f'room_control.{parent_room}'
self.log_fields = {'room': 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}[/]' LOG_CFG = {
if component is not None: 'version': 1,
fmt += ' [component]{component:<9}[/]' 'disable_existing_loggers': False,
self.log_fields['component'] = component 'filters': {
fmt += ' {message}' logger_name: {
'()': 'console.RoomControllerFilter',
datefmt = '%Y-%m-%d %I:%M:%S %p' # '.': {'room': parent_room, 'component': typ},
style = '{' 'room': parent_room,
validate = True 'component': typ,
}
super().__init__(fmt, datefmt, style, validate) },
# console.print(f'Format: [bold yellow]{fmt}[/]') 'formatters': {
logger_name: {
def format(self, record: logging.LogRecord): 'style': '{',
parts = record.name.split('.') 'format': fmt,
record.room = parts[1] # 'datefmt': '%Y-%m-%d %I:%M:%S %p',
if len(parts) == 3: }
record.component = parts[2] },
'handlers': {
return super().format(record) logger_name: {
'filters': [logger_name],
'formatter': logger_name,
def new_handler() -> RichHandler: '()': 'rich.logging.RichHandler',
return RichHandler( 'markup': True,
console=console, 'log_time_format': '%Y-%m-%d %I:%M:%S %p',
# highlighter=NullHighlighter(), 'show_path': False,
highlighter=RCHighlighter(), 'omit_repeated_times': False,
markup=True, 'highlighter': RCHighlighter(),
show_path=False, 'console': console,
omit_repeated_times=False, },
log_time_format='%Y-%m-%d %I:%M:%S %p', },
) 'loggers': {
logger_name: {
'level': 'INFO',
def setup_handler(**kwargs) -> RichHandler: 'propagate': False,
handler = new_handler() 'handlers': [logger_name],
handler.setFormatter(RoomControllerFormatter(**kwargs)) }
return handler },
}
return LOG_CFG
def setup_component_logging(self): def setup_component_logging(self):
typ = type(self).__name__ cfg_dict = create_rich_logging_dict(parent_room=self.args['app'], typ=type(self).__name__)
logger = logging.getLogger(f'room_control.{self.args["app"]}') logger_name = next(iter(cfg_dict['loggers']))
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
try:
logging.config.dictConfig(cfg_dict)
except Exception:
console.print_exception()
else:
self.logger = logging.getLogger(logger_name)

View File

@@ -1,12 +1,13 @@
import datetime import datetime
import logging import logging
import logging.config
from copy import deepcopy from copy import deepcopy
from typing import Dict, List from typing import Dict, List
from appdaemon.entity import Entity from appdaemon.entity import Entity
from appdaemon.plugins.hass.hassapi import Hass from appdaemon.plugins.hass.hassapi import Hass
from appdaemon.plugins.mqtt.mqttapi import Mqtt 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 from model import ControllerStateConfig, RoomControllerConfig
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -32,11 +33,8 @@ class RoomController(Hass, Mqtt):
self._room_config.states = new self._room_config.states = new
def initialize(self): def initialize(self):
self.logger = logger.getChild(self.name) cfg_dict = create_rich_logging_dict(parent_room=self.name)
if not self.logger.hasHandlers(): logging.config.dictConfig(cfg_dict)
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}[/]')
self.app_entities = self.gather_app_entities() self.app_entities = self.gather_app_entities()
# self.log(f'entities: {self.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']
t: datetime.time = state.time t: datetime.time = state.time
try: 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: except ValueError:
# happens when the callback time is in the past # happens when the callback time is in the past
pass pass