mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 12:17:07 +00:00
Simplify logger integration (#37780)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
8ed1a29c82
commit
9ac1475251
@ -127,9 +127,6 @@ async def async_setup(hass, config):
|
|||||||
netdisco = NetworkDiscovery()
|
netdisco = NetworkDiscovery()
|
||||||
already_discovered = set()
|
already_discovered = set()
|
||||||
|
|
||||||
# Disable zeroconf logging, it spams
|
|
||||||
logging.getLogger("zeroconf").setLevel(logging.CRITICAL)
|
|
||||||
|
|
||||||
if DOMAIN in config:
|
if DOMAIN in config:
|
||||||
# Platforms ignore by config
|
# Platforms ignore by config
|
||||||
ignored_platforms = config[DOMAIN][CONF_IGNORE]
|
ignored_platforms = config[DOMAIN][CONF_IGNORE]
|
||||||
|
@ -17,10 +17,6 @@ import homeassistant.helpers.config_validation as cv
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
_GNTP_LOGGER = logging.getLogger("gntp")
|
|
||||||
_GNTP_LOGGER.setLevel(logging.ERROR)
|
|
||||||
|
|
||||||
|
|
||||||
CONF_APP_NAME = "app_name"
|
CONF_APP_NAME = "app_name"
|
||||||
CONF_APP_ICON = "app_icon"
|
CONF_APP_ICON = "app_icon"
|
||||||
CONF_HOSTNAME = "hostname"
|
CONF_HOSTNAME = "hostname"
|
||||||
@ -42,6 +38,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||||||
|
|
||||||
def get_service(hass, config, discovery_info=None):
|
def get_service(hass, config, discovery_info=None):
|
||||||
"""Get the GNTP notification service."""
|
"""Get the GNTP notification service."""
|
||||||
|
logging.getLogger("gntp").setLevel(logging.ERROR)
|
||||||
|
|
||||||
if config.get(CONF_APP_ICON) is None:
|
if config.get(CONF_APP_ICON) is None:
|
||||||
icon_file = os.path.join(
|
icon_file = os.path.join(
|
||||||
os.path.dirname(__file__),
|
os.path.dirname(__file__),
|
||||||
|
@ -82,10 +82,6 @@ from .const import (
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
# dicttoxml (used by huawei-lte-api) has uselessly verbose INFO level.
|
|
||||||
# https://github.com/quandyfactory/dicttoxml/issues/60
|
|
||||||
logging.getLogger("dicttoxml").setLevel(logging.WARNING)
|
|
||||||
|
|
||||||
SCAN_INTERVAL = timedelta(seconds=10)
|
SCAN_INTERVAL = timedelta(seconds=10)
|
||||||
|
|
||||||
NOTIFY_SCHEMA = vol.Any(
|
NOTIFY_SCHEMA = vol.Any(
|
||||||
@ -460,6 +456,10 @@ async def async_unload_entry(
|
|||||||
async def async_setup(hass: HomeAssistantType, config) -> bool:
|
async def async_setup(hass: HomeAssistantType, config) -> bool:
|
||||||
"""Set up Huawei LTE component."""
|
"""Set up Huawei LTE component."""
|
||||||
|
|
||||||
|
# dicttoxml (used by huawei-lte-api) has uselessly verbose INFO level.
|
||||||
|
# https://github.com/quandyfactory/dicttoxml/issues/60
|
||||||
|
logging.getLogger("dicttoxml").setLevel(logging.WARNING)
|
||||||
|
|
||||||
# Arrange our YAML config to dict with normalized URLs as keys
|
# Arrange our YAML config to dict with normalized URLs as keys
|
||||||
domain_config = {}
|
domain_config = {}
|
||||||
if DOMAIN not in hass.data:
|
if DOMAIN not in hass.data:
|
||||||
|
@ -291,18 +291,13 @@ def async_register_services(hass):
|
|||||||
|
|
||||||
def print_aldb_to_log(aldb):
|
def print_aldb_to_log(aldb):
|
||||||
"""Print the All-Link Database to the log file."""
|
"""Print the All-Link Database to the log file."""
|
||||||
# This service is useless if the log level is not INFO for the
|
logger = logging.getLogger(f"{__name__}.links")
|
||||||
# insteon component. Setting the log level to INFO and resetting it
|
logger.info("%s ALDB load status is %s", aldb.address, aldb.status.name)
|
||||||
# back when we are done
|
|
||||||
orig_log_level = _LOGGER.level
|
|
||||||
if orig_log_level > logging.INFO:
|
|
||||||
_LOGGER.setLevel(logging.INFO)
|
|
||||||
_LOGGER.info("%s ALDB load status is %s", aldb.address, aldb.status.name)
|
|
||||||
if aldb.status not in [ALDBStatus.LOADED, ALDBStatus.PARTIAL]:
|
if aldb.status not in [ALDBStatus.LOADED, ALDBStatus.PARTIAL]:
|
||||||
_LOGGER.warning("All-Link database not loaded")
|
_LOGGER.warning("All-Link database not loaded")
|
||||||
|
|
||||||
_LOGGER.info("RecID In Use Mode HWM Group Address Data 1 Data 2 Data 3")
|
logger.info("RecID In Use Mode HWM Group Address Data 1 Data 2 Data 3")
|
||||||
_LOGGER.info("----- ------ ---- --- ----- -------- ------ ------ ------")
|
logger.info("----- ------ ---- --- ----- -------- ------ ------ ------")
|
||||||
for mem_addr in aldb:
|
for mem_addr in aldb:
|
||||||
rec = aldb[mem_addr]
|
rec = aldb[mem_addr]
|
||||||
# For now we write this to the log
|
# For now we write this to the log
|
||||||
@ -315,8 +310,7 @@ def print_aldb_to_log(aldb):
|
|||||||
f"{rec.group:3d} {str(rec.target):s} {rec.data1:3d} "
|
f"{rec.group:3d} {str(rec.target):s} {rec.data1:3d} "
|
||||||
f"{rec.data2:3d} {rec.data3:3d}"
|
f"{rec.data2:3d} {rec.data3:3d}"
|
||||||
)
|
)
|
||||||
_LOGGER.info(log_msg)
|
logger.info(log_msg)
|
||||||
_LOGGER.setLevel(orig_log_level)
|
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
"""Support for setting the level of logging for components."""
|
"""Support for setting the level of logging for components."""
|
||||||
import logging
|
import logging
|
||||||
import re
|
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.core import callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
DOMAIN = "logger"
|
DOMAIN = "logger"
|
||||||
|
|
||||||
DATA_LOGGER = "logger"
|
|
||||||
|
|
||||||
SERVICE_SET_DEFAULT_LEVEL = "set_default_level"
|
SERVICE_SET_DEFAULT_LEVEL = "set_default_level"
|
||||||
SERVICE_SET_LEVEL = "set_level"
|
SERVICE_SET_LEVEL = "set_level"
|
||||||
|
|
||||||
HIGHEST_LOG_LEVEL = logging.CRITICAL
|
|
||||||
|
|
||||||
LOGSEVERITY = {
|
LOGSEVERITY = {
|
||||||
"CRITICAL": 50,
|
"CRITICAL": 50,
|
||||||
"FATAL": 50,
|
"FATAL": 50,
|
||||||
@ -26,6 +22,8 @@ LOGSEVERITY = {
|
|||||||
"NOTSET": 0,
|
"NOTSET": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFAULT_LOGSEVERITY = "DEBUG"
|
||||||
|
|
||||||
LOGGER_DEFAULT = "default"
|
LOGGER_DEFAULT = "default"
|
||||||
LOGGER_LOGS = "logs"
|
LOGGER_LOGS = "logs"
|
||||||
|
|
||||||
@ -49,114 +47,36 @@ CONFIG_SCHEMA = vol.Schema(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantLogFilter(logging.Filter):
|
|
||||||
"""A log filter."""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""Initialize the filter."""
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self._default = None
|
|
||||||
self._logs = {}
|
|
||||||
self._log_rx = None
|
|
||||||
|
|
||||||
def update_default_level(self, default_level):
|
|
||||||
"""Update the default logger level."""
|
|
||||||
self._default = default_level
|
|
||||||
|
|
||||||
def update_log_filter(self, logs):
|
|
||||||
"""Rebuild the internal filter from new config."""
|
|
||||||
#
|
|
||||||
# A precompiled regex is used to avoid
|
|
||||||
# the overhead of a list transversal
|
|
||||||
#
|
|
||||||
# Sort to make sure the longer
|
|
||||||
# names are always matched first
|
|
||||||
# so they take precedence of the shorter names
|
|
||||||
# to allow for more granular settings.
|
|
||||||
#
|
|
||||||
names_by_len = sorted(list(logs), key=len, reverse=True)
|
|
||||||
self._log_rx = re.compile("".join(["^(?:", "|".join(names_by_len), ")"]))
|
|
||||||
self._logs = logs
|
|
||||||
|
|
||||||
def set_logger_level(self):
|
|
||||||
"""Find the lowest log level set to allow logger to pre-filter log messages."""
|
|
||||||
#
|
|
||||||
# We set the root logger level to lowest log level
|
|
||||||
# specified in default or for in the log filter so
|
|
||||||
# logger.isEnabledFor function will work as designed
|
|
||||||
# to avoid making logger records that will always be
|
|
||||||
# discarded.
|
|
||||||
#
|
|
||||||
# This can make the logger performance significantly
|
|
||||||
# faster if no integrations are requesting debug logs
|
|
||||||
# because we can avoid the record creation and filtering
|
|
||||||
# overhead.
|
|
||||||
#
|
|
||||||
logger = logging.getLogger("")
|
|
||||||
logger.setLevel(min(HIGHEST_LOG_LEVEL, self._default, *self._logs.values()))
|
|
||||||
|
|
||||||
def filter(self, record):
|
|
||||||
"""Filter the log entries."""
|
|
||||||
# Log with filtered severity
|
|
||||||
if self._log_rx:
|
|
||||||
match = self._log_rx.match(record.name)
|
|
||||||
if match:
|
|
||||||
return record.levelno >= self._logs[match.group(0)]
|
|
||||||
|
|
||||||
# Log with default severity
|
|
||||||
return record.levelno >= self._default
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup(hass, config):
|
async def async_setup(hass, config):
|
||||||
"""Set up the logger component."""
|
"""Set up the logger component."""
|
||||||
logfilter = {}
|
hass.data[DOMAIN] = {}
|
||||||
hass_filter = HomeAssistantLogFilter()
|
logging.setLoggerClass(_get_logger_class(hass.data[DOMAIN]))
|
||||||
|
|
||||||
|
@callback
|
||||||
def set_default_log_level(level):
|
def set_default_log_level(level):
|
||||||
"""Set the default log level for components."""
|
"""Set the default log level for components."""
|
||||||
logfilter[LOGGER_DEFAULT] = LOGSEVERITY[level]
|
_set_log_level(logging.getLogger(""), level)
|
||||||
hass_filter.update_default_level(LOGSEVERITY[level])
|
|
||||||
|
|
||||||
|
@callback
|
||||||
def set_log_levels(logpoints):
|
def set_log_levels(logpoints):
|
||||||
"""Set the specified log levels."""
|
"""Set the specified log levels."""
|
||||||
logs = {}
|
hass.data[DOMAIN].update(logpoints)
|
||||||
|
|
||||||
# Preserve existing logs
|
|
||||||
if LOGGER_LOGS in logfilter:
|
|
||||||
logs.update(logfilter[LOGGER_LOGS])
|
|
||||||
|
|
||||||
# Add new logpoints mapped to correct severity
|
|
||||||
for key, value in logpoints.items():
|
for key, value in logpoints.items():
|
||||||
logs[key] = LOGSEVERITY[value]
|
_set_log_level(logging.getLogger(key), value)
|
||||||
|
|
||||||
logfilter[LOGGER_LOGS] = logs
|
|
||||||
|
|
||||||
hass_filter.update_log_filter(logs)
|
|
||||||
|
|
||||||
# Set default log severity
|
# Set default log severity
|
||||||
if LOGGER_DEFAULT in config.get(DOMAIN):
|
set_default_log_level(config[DOMAIN].get(LOGGER_DEFAULT, DEFAULT_LOGSEVERITY))
|
||||||
set_default_log_level(config.get(DOMAIN)[LOGGER_DEFAULT])
|
|
||||||
else:
|
|
||||||
set_default_log_level("DEBUG")
|
|
||||||
|
|
||||||
# Set log filter for all log handler
|
if LOGGER_LOGS in config[DOMAIN]:
|
||||||
for handler in logging.root.handlers:
|
set_log_levels(config[DOMAIN][LOGGER_LOGS])
|
||||||
handler.setLevel(logging.NOTSET)
|
|
||||||
handler.addFilter(hass_filter)
|
|
||||||
|
|
||||||
if LOGGER_LOGS in config.get(DOMAIN):
|
@callback
|
||||||
set_log_levels(config.get(DOMAIN)[LOGGER_LOGS])
|
def async_service_handler(service):
|
||||||
|
|
||||||
hass_filter.set_logger_level()
|
|
||||||
|
|
||||||
async def async_service_handler(service):
|
|
||||||
"""Handle logger services."""
|
"""Handle logger services."""
|
||||||
if service.service == SERVICE_SET_DEFAULT_LEVEL:
|
if service.service == SERVICE_SET_DEFAULT_LEVEL:
|
||||||
set_default_log_level(service.data.get(ATTR_LEVEL))
|
set_default_log_level(service.data.get(ATTR_LEVEL))
|
||||||
else:
|
else:
|
||||||
set_log_levels(service.data)
|
set_log_levels(service.data)
|
||||||
hass_filter.set_logger_level()
|
|
||||||
|
|
||||||
hass.services.async_register(
|
hass.services.async_register(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@ -173,3 +93,36 @@ async def async_setup(hass, config):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _set_log_level(logger, level):
|
||||||
|
"""Set the log level.
|
||||||
|
|
||||||
|
Any logger fetched before this integration is loaded will use old class.
|
||||||
|
"""
|
||||||
|
getattr(logger, "orig_setLevel", logger.setLevel)(LOGSEVERITY[level])
|
||||||
|
|
||||||
|
|
||||||
|
def _get_logger_class(hass_overrides):
|
||||||
|
"""Create a logger subclass.
|
||||||
|
|
||||||
|
logging.setLoggerClass checks if it is a subclass of Logger and
|
||||||
|
so we cannot use partial to inject hass_overrides.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class HassLogger(logging.Logger):
|
||||||
|
"""Home Assistant aware logger class."""
|
||||||
|
|
||||||
|
def setLevel(self, level) -> None:
|
||||||
|
"""Set the log level unless overridden."""
|
||||||
|
if self.name in hass_overrides:
|
||||||
|
return
|
||||||
|
|
||||||
|
super().setLevel(level)
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
def orig_setLevel(self, level) -> None:
|
||||||
|
"""Set the log level."""
|
||||||
|
super().setLevel(level)
|
||||||
|
|
||||||
|
return HassLogger
|
||||||
|
@ -36,8 +36,6 @@ SERVICE_RECORD_SCHEMA = STREAM_SERVICE_SCHEMA.extend(
|
|||||||
vol.Optional(CONF_LOOKBACK, default=0): int,
|
vol.Optional(CONF_LOOKBACK, default=0): int,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
# Set log level to error for libav
|
|
||||||
logging.getLogger("libav").setLevel(logging.ERROR)
|
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
@ -80,6 +78,9 @@ def request_stream(hass, stream_source, *, fmt="hls", keepalive=False, options=N
|
|||||||
|
|
||||||
async def async_setup(hass, config):
|
async def async_setup(hass, config):
|
||||||
"""Set up stream."""
|
"""Set up stream."""
|
||||||
|
# Set log level to error for libav
|
||||||
|
logging.getLogger("libav").setLevel(logging.ERROR)
|
||||||
|
|
||||||
# Keep import here so that we can import stream integration without installing reqs
|
# Keep import here so that we can import stream integration without installing reqs
|
||||||
# pylint: disable=import-outside-toplevel
|
# pylint: disable=import-outside-toplevel
|
||||||
from .recorder import async_setup_recorder
|
from .recorder import async_setup_recorder
|
||||||
|
@ -14,7 +14,6 @@ from zeroconf import (
|
|||||||
ServiceInfo,
|
ServiceInfo,
|
||||||
ServiceStateChange,
|
ServiceStateChange,
|
||||||
Zeroconf,
|
Zeroconf,
|
||||||
log as zeroconf_log,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from homeassistant import util
|
from homeassistant import util
|
||||||
@ -71,6 +70,7 @@ async def async_get_instance(hass):
|
|||||||
|
|
||||||
def _get_instance(hass, default_interface=False):
|
def _get_instance(hass, default_interface=False):
|
||||||
"""Create an instance."""
|
"""Create an instance."""
|
||||||
|
logging.getLogger("zeroconf").setLevel(logging.NOTSET)
|
||||||
args = [InterfaceChoice.Default] if default_interface else []
|
args = [InterfaceChoice.Default] if default_interface else []
|
||||||
zeroconf = HaZeroconf(*args)
|
zeroconf = HaZeroconf(*args)
|
||||||
|
|
||||||
@ -115,8 +115,6 @@ class HaZeroconf(Zeroconf):
|
|||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
"""Set up Zeroconf and make Home Assistant discoverable."""
|
"""Set up Zeroconf and make Home Assistant discoverable."""
|
||||||
# Zeroconf sets its log level to WARNING, reset it to allow filtering by the logger component.
|
|
||||||
zeroconf_log.setLevel(logging.NOTSET)
|
|
||||||
zeroconf = hass.data[DOMAIN] = _get_instance(
|
zeroconf = hass.data[DOMAIN] = _get_instance(
|
||||||
hass, config.get(DOMAIN, {}).get(CONF_DEFAULT_INTERFACE)
|
hass, config.get(DOMAIN, {}).get(CONF_DEFAULT_INTERFACE)
|
||||||
)
|
)
|
||||||
|
@ -1,143 +1,190 @@
|
|||||||
"""The tests for the Logger component."""
|
"""The tests for the Logger component."""
|
||||||
from collections import namedtuple
|
from collections import defaultdict
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import logger
|
from homeassistant.components import logger
|
||||||
|
from homeassistant.components.logger import LOGSEVERITY
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
RECORD = namedtuple("record", ("name", "levelno"))
|
from tests.async_mock import Mock, patch
|
||||||
|
|
||||||
NO_DEFAULT_CONFIG = {"logger": {}}
|
HASS_NS = "unused.homeassistant"
|
||||||
NO_LOGS_CONFIG = {"logger": {"default": "info"}}
|
COMPONENTS_NS = f"{HASS_NS}.components"
|
||||||
TEST_CONFIG = {
|
ZONE_NS = f"{COMPONENTS_NS}.zone"
|
||||||
|
GROUP_NS = f"{COMPONENTS_NS}.group"
|
||||||
|
CONFIGED_NS = "otherlibx"
|
||||||
|
UNCONFIG_NS = "unconfigurednamespace"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def restore_logging_class():
|
||||||
|
"""Restore logging class."""
|
||||||
|
klass = logging.getLoggerClass()
|
||||||
|
yield
|
||||||
|
logging.setLoggerClass(klass)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_setting_level(hass):
|
||||||
|
"""Test we set log levels."""
|
||||||
|
mocks = defaultdict(Mock)
|
||||||
|
|
||||||
|
with patch("logging.getLogger", mocks.__getitem__):
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
"logger",
|
||||||
|
{
|
||||||
"logger": {
|
"logger": {
|
||||||
"default": "warning",
|
"default": "warning",
|
||||||
"logs": {"test": "info", "test.child": "debug", "test.child.child": "warning"},
|
"logs": {
|
||||||
|
"test": "info",
|
||||||
|
"test.child": "debug",
|
||||||
|
"test.child.child": "warning",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(mocks) == 4
|
||||||
|
|
||||||
|
assert len(mocks[""].orig_setLevel.mock_calls) == 1
|
||||||
|
assert mocks[""].orig_setLevel.mock_calls[0][1][0] == LOGSEVERITY["WARNING"]
|
||||||
|
|
||||||
|
assert len(mocks["test"].orig_setLevel.mock_calls) == 1
|
||||||
|
assert mocks["test"].orig_setLevel.mock_calls[0][1][0] == LOGSEVERITY["INFO"]
|
||||||
|
|
||||||
|
assert len(mocks["test.child"].orig_setLevel.mock_calls) == 1
|
||||||
|
assert mocks["test.child"].orig_setLevel.mock_calls[0][1][0] == LOGSEVERITY["DEBUG"]
|
||||||
|
|
||||||
|
assert len(mocks["test.child.child"].orig_setLevel.mock_calls) == 1
|
||||||
|
assert (
|
||||||
|
mocks["test.child.child"].orig_setLevel.mock_calls[0][1][0]
|
||||||
|
== LOGSEVERITY["WARNING"]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test set default level
|
||||||
|
with patch("logging.getLogger", mocks.__getitem__):
|
||||||
|
await hass.services.async_call(
|
||||||
|
"logger", "set_default_level", {"level": "fatal"}, blocking=True
|
||||||
|
)
|
||||||
|
assert len(mocks[""].orig_setLevel.mock_calls) == 2
|
||||||
|
assert mocks[""].orig_setLevel.mock_calls[1][1][0] == LOGSEVERITY["FATAL"]
|
||||||
|
|
||||||
|
# Test update other loggers
|
||||||
|
with patch("logging.getLogger", mocks.__getitem__):
|
||||||
|
await hass.services.async_call(
|
||||||
|
"logger",
|
||||||
|
"set_level",
|
||||||
|
{"test.child": "info", "new_logger": "notset"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert len(mocks) == 5
|
||||||
|
|
||||||
|
assert len(mocks["test.child"].orig_setLevel.mock_calls) == 2
|
||||||
|
assert mocks["test.child"].orig_setLevel.mock_calls[1][1][0] == LOGSEVERITY["INFO"]
|
||||||
|
|
||||||
|
assert len(mocks["new_logger"].orig_setLevel.mock_calls) == 1
|
||||||
|
assert (
|
||||||
|
mocks["new_logger"].orig_setLevel.mock_calls[0][1][0] == LOGSEVERITY["NOTSET"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_can_set_level(hass):
|
||||||
|
"""Test logger propagation."""
|
||||||
|
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
"logger",
|
||||||
|
{
|
||||||
|
"logger": {
|
||||||
|
"logs": {
|
||||||
|
CONFIGED_NS: "warning",
|
||||||
|
f"{CONFIGED_NS}.info": "info",
|
||||||
|
f"{CONFIGED_NS}.debug": "debug",
|
||||||
|
HASS_NS: "warning",
|
||||||
|
COMPONENTS_NS: "info",
|
||||||
|
ZONE_NS: "debug",
|
||||||
|
GROUP_NS: "info",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert logging.getLogger(UNCONFIG_NS).level == logging.NOTSET
|
||||||
|
assert logging.getLogger(UNCONFIG_NS).isEnabledFor(logging.CRITICAL) is True
|
||||||
|
assert (
|
||||||
|
logging.getLogger(f"{UNCONFIG_NS}.any").isEnabledFor(logging.CRITICAL) is True
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
logging.getLogger(f"{UNCONFIG_NS}.any.any").isEnabledFor(logging.CRITICAL)
|
||||||
|
is True
|
||||||
|
)
|
||||||
|
|
||||||
async def async_setup_logger(hass, config):
|
assert logging.getLogger(CONFIGED_NS).isEnabledFor(logging.DEBUG) is False
|
||||||
"""Set up logger and save log filter."""
|
assert logging.getLogger(CONFIGED_NS).isEnabledFor(logging.WARNING) is True
|
||||||
await async_setup_component(hass, logger.DOMAIN, config)
|
assert logging.getLogger(f"{CONFIGED_NS}.any").isEnabledFor(logging.WARNING) is True
|
||||||
return logging.root.handlers[-1].filters[0]
|
assert (
|
||||||
|
logging.getLogger(f"{CONFIGED_NS}.any.any").isEnabledFor(logging.WARNING)
|
||||||
|
is True
|
||||||
|
)
|
||||||
|
assert logging.getLogger(f"{CONFIGED_NS}.info").isEnabledFor(logging.DEBUG) is False
|
||||||
|
assert logging.getLogger(f"{CONFIGED_NS}.info").isEnabledFor(logging.INFO) is True
|
||||||
|
assert (
|
||||||
|
logging.getLogger(f"{CONFIGED_NS}.info.any").isEnabledFor(logging.DEBUG)
|
||||||
|
is False
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
logging.getLogger(f"{CONFIGED_NS}.info.any").isEnabledFor(logging.INFO) is True
|
||||||
|
)
|
||||||
|
assert logging.getLogger(f"{CONFIGED_NS}.debug").isEnabledFor(logging.DEBUG) is True
|
||||||
|
assert (
|
||||||
|
logging.getLogger(f"{CONFIGED_NS}.debug.any").isEnabledFor(logging.DEBUG)
|
||||||
|
is True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert logging.getLogger(HASS_NS).isEnabledFor(logging.DEBUG) is False
|
||||||
|
assert logging.getLogger(HASS_NS).isEnabledFor(logging.WARNING) is True
|
||||||
|
|
||||||
async def test_logger_setup(hass):
|
assert logging.getLogger(COMPONENTS_NS).isEnabledFor(logging.DEBUG) is False
|
||||||
"""Use logger to create a logging filter."""
|
assert logging.getLogger(COMPONENTS_NS).isEnabledFor(logging.WARNING) is True
|
||||||
await async_setup_logger(hass, TEST_CONFIG)
|
assert logging.getLogger(COMPONENTS_NS).isEnabledFor(logging.INFO) is True
|
||||||
|
|
||||||
assert len(logging.root.handlers) > 0
|
assert logging.getLogger(GROUP_NS).isEnabledFor(logging.DEBUG) is False
|
||||||
handler = logging.root.handlers[-1]
|
assert logging.getLogger(GROUP_NS).isEnabledFor(logging.WARNING) is True
|
||||||
|
assert logging.getLogger(GROUP_NS).isEnabledFor(logging.INFO) is True
|
||||||
|
|
||||||
assert len(handler.filters) == 1
|
assert logging.getLogger(f"{GROUP_NS}.any").isEnabledFor(logging.DEBUG) is False
|
||||||
|
assert logging.getLogger(f"{GROUP_NS}.any").isEnabledFor(logging.WARNING) is True
|
||||||
|
assert logging.getLogger(f"{GROUP_NS}.any").isEnabledFor(logging.INFO) is True
|
||||||
|
|
||||||
|
assert logging.getLogger(ZONE_NS).isEnabledFor(logging.DEBUG) is True
|
||||||
|
assert logging.getLogger(f"{ZONE_NS}.any").isEnabledFor(logging.DEBUG) is True
|
||||||
|
|
||||||
async def test_logger_test_filters(hass):
|
await hass.services.async_call(
|
||||||
"""Test resulting filter operation."""
|
logger.DOMAIN, "set_level", {f"{UNCONFIG_NS}.any": "debug"}, blocking=True
|
||||||
log_filter = await async_setup_logger(hass, TEST_CONFIG)
|
)
|
||||||
|
|
||||||
|
logging.getLogger(UNCONFIG_NS).level == logging.NOTSET
|
||||||
|
logging.getLogger(f"{UNCONFIG_NS}.any").level == logging.DEBUG
|
||||||
|
logging.getLogger(UNCONFIG_NS).level == logging.NOTSET
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
logger.DOMAIN, "set_default_level", {"level": "debug"}, blocking=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert logging.getLogger(UNCONFIG_NS).isEnabledFor(logging.DEBUG) is True
|
||||||
|
assert logging.getLogger(f"{UNCONFIG_NS}.any").isEnabledFor(logging.DEBUG) is True
|
||||||
|
assert (
|
||||||
|
logging.getLogger(f"{UNCONFIG_NS}.any.any").isEnabledFor(logging.DEBUG) is True
|
||||||
|
)
|
||||||
assert logging.getLogger("").isEnabledFor(logging.DEBUG) is True
|
assert logging.getLogger("").isEnabledFor(logging.DEBUG) is True
|
||||||
|
|
||||||
# Blocked default record
|
assert logging.getLogger(COMPONENTS_NS).isEnabledFor(logging.DEBUG) is False
|
||||||
assert not log_filter.filter(RECORD("asdf", logging.DEBUG))
|
assert logging.getLogger(GROUP_NS).isEnabledFor(logging.DEBUG) is False
|
||||||
|
|
||||||
# Allowed default record
|
logging.getLogger(CONFIGED_NS).setLevel(logging.INFO)
|
||||||
assert log_filter.filter(RECORD("asdf", logging.WARNING))
|
assert logging.getLogger(CONFIGED_NS).level == logging.WARNING
|
||||||
|
|
||||||
# Blocked named record
|
logging.getLogger("").setLevel(logging.NOTSET)
|
||||||
assert not log_filter.filter(RECORD("test", logging.DEBUG))
|
|
||||||
|
|
||||||
# Allowed named record
|
|
||||||
assert log_filter.filter(RECORD("test", logging.INFO))
|
|
||||||
|
|
||||||
# Allowed named record child
|
|
||||||
assert log_filter.filter(RECORD("test.child", logging.INFO))
|
|
||||||
|
|
||||||
# Allowed named record child
|
|
||||||
assert log_filter.filter(RECORD("test.child", logging.DEBUG))
|
|
||||||
|
|
||||||
# Blocked named record child of child
|
|
||||||
assert not log_filter.filter(RECORD("test.child.child", logging.DEBUG))
|
|
||||||
|
|
||||||
# Allowed named record child of child
|
|
||||||
assert log_filter.filter(RECORD("test.child.child", logging.WARNING))
|
|
||||||
|
|
||||||
|
|
||||||
async def test_set_filter_empty_config(hass):
|
|
||||||
"""Test change log level from empty configuration."""
|
|
||||||
log_filter = await async_setup_logger(hass, NO_LOGS_CONFIG)
|
|
||||||
|
|
||||||
assert logging.getLogger("").isEnabledFor(logging.DEBUG) is False
|
|
||||||
|
|
||||||
assert not log_filter.filter(RECORD("test", logging.DEBUG))
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
|
||||||
logger.DOMAIN, "set_default_level", {"level": "warning"}
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert not log_filter.filter(RECORD("test", logging.DEBUG))
|
|
||||||
|
|
||||||
assert logging.getLogger("").isEnabledFor(logging.DEBUG) is False
|
|
||||||
assert logging.getLogger("").isEnabledFor(logging.WARNING) is True
|
|
||||||
|
|
||||||
await hass.services.async_call(logger.DOMAIN, "set_level", {"test": "debug"})
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert log_filter.filter(RECORD("test", logging.DEBUG))
|
|
||||||
|
|
||||||
assert logging.getLogger("").isEnabledFor(logging.DEBUG) is True
|
|
||||||
|
|
||||||
|
|
||||||
async def test_set_filter(hass):
|
|
||||||
"""Test change log level of existing filter."""
|
|
||||||
log_filter = await async_setup_logger(hass, TEST_CONFIG)
|
|
||||||
|
|
||||||
assert not log_filter.filter(RECORD("asdf", logging.DEBUG))
|
|
||||||
assert log_filter.filter(RECORD("dummy", logging.WARNING))
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
|
||||||
logger.DOMAIN, "set_level", {"asdf": "debug", "dummy": "info"}
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert log_filter.filter(RECORD("asdf", logging.DEBUG))
|
|
||||||
assert log_filter.filter(RECORD("dummy", logging.WARNING))
|
|
||||||
|
|
||||||
|
|
||||||
async def test_set_default_filter_empty_config(hass):
|
|
||||||
"""Test change default log level from empty configuration."""
|
|
||||||
log_filter = await async_setup_logger(hass, NO_DEFAULT_CONFIG)
|
|
||||||
|
|
||||||
assert logging.getLogger("").isEnabledFor(logging.DEBUG) is True
|
|
||||||
assert logging.getLogger("").isEnabledFor(logging.WARNING) is True
|
|
||||||
|
|
||||||
assert log_filter.filter(RECORD("test", logging.DEBUG))
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
|
||||||
logger.DOMAIN, "set_default_level", {"level": "warning"}
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert not log_filter.filter(RECORD("test", logging.DEBUG))
|
|
||||||
|
|
||||||
assert logging.getLogger("").isEnabledFor(logging.DEBUG) is False
|
|
||||||
assert logging.getLogger("").isEnabledFor(logging.INFO) is False
|
|
||||||
assert logging.getLogger("").isEnabledFor(logging.WARNING) is True
|
|
||||||
|
|
||||||
|
|
||||||
async def test_set_default_filter(hass):
|
|
||||||
"""Test change default log level with existing default."""
|
|
||||||
log_filter = await async_setup_logger(hass, TEST_CONFIG)
|
|
||||||
|
|
||||||
assert not log_filter.filter(RECORD("asdf", logging.DEBUG))
|
|
||||||
assert log_filter.filter(RECORD("dummy", logging.WARNING))
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
|
||||||
logger.DOMAIN, "set_default_level", {"level": "debug"}
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert log_filter.filter(RECORD("asdf", logging.DEBUG))
|
|
||||||
assert log_filter.filter(RECORD("dummy", logging.WARNING))
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user