moved default logging config to a yaml file

This commit is contained in:
John Lancaster
2024-05-07 23:31:38 -05:00
parent a7b27cf916
commit d481a1344f
8 changed files with 121 additions and 82 deletions

View File

@@ -1,14 +0,0 @@
version: 1
disable_existing_loggers: false
formatters:
rich:
style: "{"
format: "[room]{appname}[/] {message}"
datefmt: '%H:%M:%S.%f'
handlers:
rich:
formatter: rich
'()': 'rich.logging.RichHandler'
markup: True
show_path: false
omit_repeated_times: false

View File

@@ -0,0 +1,26 @@
version: 1
disable_existing_loggers: false
formatters:
rich:
style: "{"
format: "[room]{room}[/] {message}"
# format: "{message}"
datefmt: '%H:%M:%S.%f'
rich_component:
style: "{"
format: "[room]{room}[/] [component]{component}[/] {message}"
# format: "{message}"
datefmt: '%H:%M:%S.%f'
handlers:
rich:
formatter: rich
'()': 'rich.logging.RichHandler'
markup: True
show_path: false
omit_repeated_times: false
rich_component:
formatter: rich_component
'()': 'rich.logging.RichHandler'
markup: True
show_path: false
omit_repeated_times: false

View File

@@ -3,4 +3,4 @@ from .motion import Motion
from .button import Button from .button import Button
from .door import Door from .door import Door
__all__ = ['RoomController', 'Motion', 'Button', 'Door'] __all__ = ['RoomController', 'Motion', 'Button', 'Door']

View File

@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, List
from appdaemon.plugins.mqtt.mqttapi import Mqtt from appdaemon.plugins.mqtt.mqttapi import Mqtt
from .console import setup_component_logging from . import console
from .model import ButtonConfig from .model import ButtonConfig
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -20,9 +20,9 @@ class Button(Mqtt):
logger: Logger logger: Logger
async def initialize(self): async def initialize(self):
self.config = ButtonConfig(**self.args)
self.logger = setup_component_logging(self)
self.app: 'RoomController' = await self.get_app(self.args['app']) self.app: 'RoomController' = await self.get_app(self.args['app'])
self.logger = console.load_rich_config(self.app.name, type(self).__name__)
self.config = ButtonConfig(**self.args)
self.log(f'Connected to AD app [room]{self.app.name}[/]', level='DEBUG') self.log(f'Connected to AD app [room]{self.app.name}[/]', level='DEBUG')
self.button = self.config.button self.button = self.config.button

View File

@@ -1,13 +1,13 @@
import json import json
import logging import logging
import logging.config import logging.config
from pathlib import Path
import re import re
from abc import ABC from abc import ABC
from dataclasses import asdict from dataclasses import asdict
from importlib.resources import files from importlib.resources import files
import yaml import yaml
from appdaemon.adapi import ADAPI
from rich.console import Console from rich.console import Console
from rich.highlighter import RegexHighlighter from rich.highlighter import RegexHighlighter
from rich.theme import Theme from rich.theme import Theme
@@ -46,12 +46,38 @@ class RCHighlighter(RegexHighlighter):
] ]
def load_rich_config(): def load_rich_config(
with files('room_control.config').joinpath('rich_logging.yaml').open('r') as f: room: str = None, component: str = None, level: str = 'INFO'
) -> logging.LoggerAdapter:
logger_name = f'Appdaemon.{room}'
if component is not None:
logger_name += f'.{component}'
with files('room_control').joinpath('.rich_logging.yaml').open('r') as f:
RICH_CFG = yaml.safe_load(f) RICH_CFG = yaml.safe_load(f)
RICH_CFG['handlers']['rich']['console'] = console RICH_CFG['handlers']['rich']['console'] = console
RICH_CFG['handlers']['rich']['highlighter'] = RCHighlighter() RICH_CFG['handlers']['rich']['highlighter'] = RCHighlighter()
return RICH_CFG RICH_CFG['handlers']['rich_component']['console'] = console
RICH_CFG['handlers']['rich_component']['highlighter'] = RCHighlighter()
RICH_CFG['loggers'] = {
logger_name: {
'handlers': ['rich' if component is None else 'rich_component'],
'propagate': False,
'level': level,
}
}
extra = {'room': room}
if component is not None:
extra['component'] = component
logging.config.dictConfig(RICH_CFG)
logger = logging.getLogger(logger_name)
adapter = logging.LoggerAdapter(logger, extra)
return adapter
RICH_HANDLER_CFG = { RICH_HANDLER_CFG = {
@@ -163,51 +189,50 @@ def room_logging_config(name: str):
} }
def component_logging_config(parent_room: str, component: str): # def component_logging_config(parent_room: str, component: str):
logger_name = f'AppDaemon.{parent_room}.{component}' # logger_name = f'AppDaemon.{parent_room}.{component}'
cfg = load_rich_config() # cfg = load_rich_config()
LOG_CFG = { # LOG_CFG = {
'version': 1, # 'version': 1,
'disable_existing_loggers': False, # 'disable_existing_loggers': False,
'formatters': { # 'formatters': {
'rich_component': { # 'rich_component': {
'style': '{', # 'style': '{',
'format': '[room]{room}[/] [component]{component}[/] {message}', # 'format': '[room]{room}[/] [component]{component}[/] {message}',
'datefmt': '%H:%M:%S.%f', # 'datefmt': '%H:%M:%S.%f',
}, # },
}, # },
'handlers': { # 'handlers': {
'rich_component': { # 'rich_component': {
'formatter': 'rich_component', # 'formatter': 'rich_component',
**RICH_HANDLER_CFG, # **RICH_HANDLER_CFG,
}, # },
}, # },
'loggers': { # 'loggers': {
logger_name: { # logger_name: {
# 'level': 'INFO', # # 'level': 'INFO',
'propagate': True, # 'propagate': True,
'handlers': ['rich_component'], # 'handlers': ['rich_component'],
} # }
}, # },
} # }
return LOG_CFG # return LOG_CFG
def setup_component_logging(self) -> logging.Logger: # def setup_component_logging(self) -> logging.Logger:
"""Creates a logger for a subcomponent with a RichHandler""" # """Creates a logger for a subcomponent with a RichHandler"""
component = type(self).__name__ # component = type(self).__name__
parent = self.args['app'] # parent = self.args['app']
cfg_dict = component_logging_config(parent_room=parent, component=component) # cfg_dict = component_logging_config(parent_room=parent, component=component)
logger_name = next(iter(cfg_dict['loggers'])) # logger_name = next(iter(cfg_dict['loggers']))
try: # try:
logging.config.dictConfig(cfg_dict) # logging.config.dictConfig(cfg_dict)
except Exception: # except Exception:
console.print_exception() # console.print_exception()
else: # else:
logger = logging.getLogger(logger_name) # logger = logging.getLogger(logger_name)
logger = logging.LoggerAdapter(logger, {'room': parent, 'component': component}) # logger = logging.LoggerAdapter(logger, {'room': parent, 'component': component})
return logger # return logger

View File

@@ -1,18 +1,23 @@
from logging import Logger from logging import Logger
from typing import TYPE_CHECKING
from appdaemon.plugins.hass.hassapi import Hass from appdaemon.plugins.hass.hassapi import Hass
from room_control import RoomController from . import console
from .console import setup_component_logging if TYPE_CHECKING:
from room_control import RoomController
class Door(Hass): class Door(Hass):
app: 'RoomController'
logger: Logger logger: Logger
async def initialize(self): async def initialize(self):
self.logger = setup_component_logging(self) self.app: 'RoomController' = await self.get_app(self.args['app'])
self.app: RoomController = await self.get_app(self.args['app']) self.logger = console.load_rich_config(
room=self.app.name, component=type(self).__name__
)
self.log(f'Connected to AD app [room]{self.app.name}[/]', level='DEBUG') self.log(f'Connected to AD app [room]{self.app.name}[/]', level='DEBUG')
await self.listen_state( await self.listen_state(

View File

@@ -5,9 +5,9 @@ from typing import TYPE_CHECKING, Literal, Optional
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 pydantic import BaseModel, TypeAdapter, ValidationError from pydantic import BaseModel, ValidationError
from .console import setup_component_logging from . import console
if TYPE_CHECKING: if TYPE_CHECKING:
from room_control import RoomController from room_control import RoomController
@@ -52,9 +52,8 @@ class Motion(Hass):
return self.sensor_state != self.ref_entity_state return self.sensor_state != self.ref_entity_state
def initialize(self): def initialize(self):
self.logger = setup_component_logging(self)
self.app: 'RoomController' = self.get_app(self.args['app']) self.app: 'RoomController' = self.get_app(self.args['app'])
self.log(f'Connected to AD app [room]{self.app.name}[/]', level='DEBUG') self.logger = console.load_rich_config(self.app.name, type(self).__name__)
assert self.entity_exists(self.args['sensor']) assert self.entity_exists(self.args['sensor'])
assert self.entity_exists(self.args['ref_entity']) assert self.entity_exists(self.args['ref_entity'])
@@ -85,6 +84,8 @@ class Motion(Hass):
for handle, cb in self.callbacks(): for handle, cb in self.callbacks():
self.log(f'Handle [yellow]{handle[:4]}[/]: {cb.function}', level='DEBUG') self.log(f'Handle [yellow]{handle[:4]}[/]: {cb.function}', level='DEBUG')
self.log(f'Initialized [bold green]{type(self).__name__}[/]')
def callbacks(self): def callbacks(self):
"""Returns a dictionary of validated CallbackEntry objects that are associated with this app""" """Returns a dictionary of validated CallbackEntry objects that are associated with this app"""

View File

@@ -9,7 +9,7 @@ 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 room_logging_config from . import console
from .model import ControllerStateConfig, RoomControllerConfig from .model import ControllerStateConfig, RoomControllerConfig
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -34,14 +34,8 @@ class RoomController(Hass, Mqtt):
assert all(isinstance(s, ControllerStateConfig) for s in new), f'Invalid: {new}' assert all(isinstance(s, ControllerStateConfig) for s in new), f'Invalid: {new}'
self._room_config.states = new self._room_config.states = new
def configure_logging(self) -> dict:
logging.config.dictConfig(room_logging_config(self.name))
self.logger = logging.LoggerAdapter(
logging.getLogger(f'AppDaemon.{self.name}'), {'room': self.name}
)
def initialize(self): def initialize(self):
self.configure_logging() self.logger = console.load_rich_config(self.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}')
self.refresh_state_times() self.refresh_state_times()
@@ -206,7 +200,9 @@ class RoomController(Hass, Mqtt):
elif isinstance(scene_kwargs, dict): elif isinstance(scene_kwargs, dict):
self.call_service('scene/apply', **scene_kwargs) self.call_service('scene/apply', **scene_kwargs)
self.log(f'Applied scene:\n{json.dumps(scene_kwargs, indent=2)}', level='DEBUG') self.log(
f'Applied scene:\n{json.dumps(scene_kwargs, indent=2)}', level='DEBUG'
)
elif scene_kwargs is None: elif scene_kwargs is None:
self.log('No scene, ignoring...') self.log('No scene, ignoring...')