From bdd7ddc0e6b5e7937a7bd68f7147ec5c2a19cb22 Mon Sep 17 00:00:00 2001 From: krahabb <13969600+krahabb@users.noreply.github.com> Date: Mon, 6 Feb 2023 21:01:15 +0000 Subject: [PATCH] Patch Logger to reduce repeated messages frequency --- custom_components/vicare/__init__.py | 4 +-- custom_components/vicare/binary_sensor.py | 4 +-- custom_components/vicare/button.py | 4 +-- custom_components/vicare/climate.py | 22 ++++++++---- custom_components/vicare/helpers.py | 44 +++++++++++++++++++++++ custom_components/vicare/sensor.py | 4 +-- custom_components/vicare/water_heater.py | 4 +-- 7 files changed, 70 insertions(+), 16 deletions(-) create mode 100644 custom_components/vicare/helpers.py diff --git a/custom_components/vicare/__init__.py b/custom_components/vicare/__init__.py index b177a4c..a8491e4 100644 --- a/custom_components/vicare/__init__.py +++ b/custom_components/vicare/__init__.py @@ -3,7 +3,6 @@ from collections.abc import Callable from dataclasses import dataclass -import logging from PyViCare.PyViCare import PyViCare from PyViCare.PyViCareDevice import Device @@ -23,8 +22,9 @@ VICARE_DEVICE_CONFIG, HeatingType, ) +from . import helpers -_LOGGER = logging.getLogger(__name__) +_LOGGER = helpers.getLogger(__name__) @dataclass() diff --git a/custom_components/vicare/binary_sensor.py b/custom_components/vicare/binary_sensor.py index 3f54e5b..90f22c1 100644 --- a/custom_components/vicare/binary_sensor.py +++ b/custom_components/vicare/binary_sensor.py @@ -3,7 +3,6 @@ from contextlib import suppress from dataclasses import dataclass -import logging from PyViCare.PyViCareUtils import ( PyViCareInvalidDataError, @@ -24,8 +23,9 @@ from . import ViCareRequiredKeysMixin from .const import DOMAIN, VICARE_API, VICARE_DEVICE_CONFIG, VICARE_NAME +from . import helpers -_LOGGER = logging.getLogger(__name__) +_LOGGER = helpers.getLogger(__name__) @dataclass diff --git a/custom_components/vicare/button.py b/custom_components/vicare/button.py index 95be680..ed5b403 100644 --- a/custom_components/vicare/button.py +++ b/custom_components/vicare/button.py @@ -3,7 +3,6 @@ from contextlib import suppress from dataclasses import dataclass -import logging from PyViCare.PyViCareUtils import ( PyViCareInvalidDataError, @@ -20,8 +19,9 @@ from . import ViCareRequiredKeysMixinWithSet from .const import DOMAIN, VICARE_API, VICARE_DEVICE_CONFIG, VICARE_NAME +from . import helpers -_LOGGER = logging.getLogger(__name__) +_LOGGER = helpers.getLogger(__name__) BUTTON_DHW_ACTIVATE_ONETIME_CHARGE = "activate_onetimecharge" diff --git a/custom_components/vicare/climate.py b/custom_components/vicare/climate.py index 678bf13..d601006 100644 --- a/custom_components/vicare/climate.py +++ b/custom_components/vicare/climate.py @@ -2,8 +2,7 @@ from __future__ import annotations from contextlib import suppress -import logging -from typing import Any +from typing import Any, TYPE_CHECKING from PyViCare.PyViCareUtils import ( PyViCareCommandError, @@ -43,8 +42,9 @@ VICARE_DEVICE_CONFIG, VICARE_NAME, ) +from . import helpers -_LOGGER = logging.getLogger(__name__) +_LOGGER = helpers.getLogger(__name__) SERVICE_SET_VICARE_MODE = "set_vicare_mode" SERVICE_SET_VICARE_MODE_ATTR_MODE = "vicare_mode" @@ -99,8 +99,11 @@ PRESET_NONE: VICARE_PROGRAM_NORMAL, } +if TYPE_CHECKING: + from PyViCare import PyViCareDevice, PyViCareDeviceConfig -def _get_circuits(vicare_api): + +def _get_circuits(vicare_api: 'PyViCareDevice.Device'): """Return the list of circuits.""" try: return vicare_api.circuits @@ -117,7 +120,7 @@ async def async_setup_entry( """Set up the ViCare climate platform.""" name = VICARE_NAME entities = [] - api = hass.data[DOMAIN][config_entry.entry_id][VICARE_API] + api: 'PyViCareDevice.Device' = hass.data[DOMAIN][config_entry.entry_id][VICARE_API] circuits = await hass.async_add_executor_job(_get_circuits, api) for circuit in circuits: @@ -163,7 +166,14 @@ class ViCareClimate(ClimateEntity): ) _attr_temperature_unit = TEMP_CELSIUS - def __init__(self, name, api, circuit, device_config, heating_type): + def __init__( + self, + name, + api: 'PyViCareDevice.Device', + circuit: 'PyViCareDevice.HeatingCircuit', + device_config: 'PyViCareDeviceConfig.PyViCareDeviceConfig', + heating_type + ): """Initialize the climate device.""" self._name = name self._state = None diff --git a/custom_components/vicare/helpers.py b/custom_components/vicare/helpers.py new file mode 100644 index 0000000..9c99192 --- /dev/null +++ b/custom_components/vicare/helpers.py @@ -0,0 +1,44 @@ +""" Helper utilities""" +import logging +from time import time + + +def getLogger(name): + """ + Replaces the default Logger with our wrapped implementation: + replace your logging.getLogger with helpers.getLogger et voilĂ  + """ + logger = logging.getLogger(name) + logger.__class__ = type('Logger', (_Logger, logger.__class__, ), {}) + return logger + + +class _Logger(logging.Logger): + """ + This wrapper will 'filter' log messages and avoid + verbose over-logging for the same message by using a timeout + to prevent repeating the very same log before the timeout expires. + The implementation 'hacks' a standard Logger instance by mixin-ing + """ + + # default timeout: these can be overriden at the log call level + # by passing in the 'timeout=' param + # for example: LOGGER.error("This error will %s be logged again", "soon", timeout=5) + # it can also be overriden at the 'Logger' instance level + default_timeout = 60 * 60 * 8 + # cache of logged messages with relative last-thrown-epoch + _LOGGER_TIMEOUTS = {} + + def _log(self, level, msg, args, **kwargs): # pylint: disable=arguments-differ + + timeout = kwargs.pop('timeout', self.default_timeout) + epoch = time() + trap_key = (hash(msg), args) + if trap_key in _Logger._LOGGER_TIMEOUTS: + if ((epoch - _Logger._LOGGER_TIMEOUTS[trap_key]) < timeout): + if self.isEnabledFor(logging.DEBUG): + super()._log(logging.DEBUG, f"dropped log message for {msg}", args) + return + + super()._log(level, msg, args, **kwargs) + _Logger._LOGGER_TIMEOUTS[trap_key] = epoch diff --git a/custom_components/vicare/sensor.py b/custom_components/vicare/sensor.py index 4c61ed0..c40f044 100644 --- a/custom_components/vicare/sensor.py +++ b/custom_components/vicare/sensor.py @@ -4,7 +4,6 @@ from collections.abc import Callable from contextlib import suppress from dataclasses import dataclass -import logging from PyViCare.PyViCareDevice import Device from PyViCare.PyViCareUtils import ( @@ -42,8 +41,9 @@ VICARE_UNIT_TO_DEVICE_CLASS, VICARE_UNIT_TO_UNIT_OF_MEASUREMENT, ) +from . import helpers -_LOGGER = logging.getLogger(__name__) +_LOGGER = helpers.getLogger(__name__) @dataclass diff --git a/custom_components/vicare/water_heater.py b/custom_components/vicare/water_heater.py index 1ad5c0d..db5dc57 100644 --- a/custom_components/vicare/water_heater.py +++ b/custom_components/vicare/water_heater.py @@ -1,6 +1,5 @@ """Viessmann ViCare water_heater device.""" from contextlib import suppress -import logging from typing import Any from PyViCare.PyViCareUtils import ( @@ -32,8 +31,9 @@ VICARE_DEVICE_CONFIG, VICARE_NAME, ) +from . import helpers -_LOGGER = logging.getLogger(__name__) +_LOGGER = helpers.getLogger(__name__) VICARE_MODE_DHW = "dhw" VICARE_MODE_HEATING = "heating"