Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for setting the level of logging for components."""
2 
3 from __future__ import annotations
4 
5 import logging
6 import re
7 
8 import voluptuous as vol
9 
10 from homeassistant.const import EVENT_LOGGING_CHANGED # noqa: F401
11 from homeassistant.core import HomeAssistant, ServiceCall, callback
13 from homeassistant.helpers.typing import ConfigType
14 
15 from . import websocket_api
16 from .const import (
17  ATTR_LEVEL,
18  DOMAIN,
19  LOGGER_DEFAULT,
20  LOGGER_FILTERS,
21  LOGGER_LOGS,
22  LOGSEVERITY,
23  SERVICE_SET_DEFAULT_LEVEL,
24  SERVICE_SET_LEVEL,
25 )
26 from .helpers import (
27  LoggerDomainConfig,
28  LoggerSettings,
29  set_default_log_level,
30  set_log_levels,
31 )
32 
33 _VALID_LOG_LEVEL = vol.All(vol.Upper, vol.In(LOGSEVERITY), LOGSEVERITY.__getitem__)
34 
35 SERVICE_SET_DEFAULT_LEVEL_SCHEMA = vol.Schema({ATTR_LEVEL: _VALID_LOG_LEVEL})
36 SERVICE_SET_LEVEL_SCHEMA = vol.Schema({cv.string: _VALID_LOG_LEVEL})
37 
38 CONFIG_SCHEMA = vol.Schema(
39  {
40  DOMAIN: vol.Schema(
41  {
42  vol.Optional(LOGGER_DEFAULT): _VALID_LOG_LEVEL,
43  vol.Optional(LOGGER_LOGS): vol.Schema({cv.string: _VALID_LOG_LEVEL}),
44  vol.Optional(LOGGER_FILTERS): vol.Schema({cv.string: [cv.is_regex]}),
45  }
46  )
47  },
48  extra=vol.ALLOW_EXTRA,
49 )
50 
51 
52 async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
53  """Set up the logger component."""
54 
55  settings = LoggerSettings(hass, config)
56 
57  domain_config = hass.data[DOMAIN] = LoggerDomainConfig({}, settings)
58  logging.setLoggerClass(_get_logger_class(domain_config.overrides))
59 
60  websocket_api.async_load_websocket_api(hass)
61 
62  await settings.async_load()
63 
64  # Set default log severity and filter
65  logger_config = config.get(DOMAIN, {})
66 
67  if LOGGER_DEFAULT in logger_config:
68  set_default_log_level(hass, logger_config[LOGGER_DEFAULT])
69 
70  if LOGGER_FILTERS in logger_config:
71  log_filters: dict[str, list[re.Pattern]] = logger_config[LOGGER_FILTERS]
72  for key, value in log_filters.items():
73  _add_log_filter(logging.getLogger(key), value)
74 
75  # Combine log levels configured in configuration.yaml with log levels set by frontend
76  combined_logs = await settings.async_get_levels(hass)
77  set_log_levels(hass, combined_logs)
78 
79  @callback
80  def async_service_handler(service: ServiceCall) -> None:
81  """Handle logger services."""
82  if service.service == SERVICE_SET_DEFAULT_LEVEL:
83  set_default_log_level(hass, service.data[ATTR_LEVEL])
84  else:
85  set_log_levels(hass, service.data)
86 
87  hass.services.async_register(
88  DOMAIN,
89  SERVICE_SET_DEFAULT_LEVEL,
90  async_service_handler,
91  schema=SERVICE_SET_DEFAULT_LEVEL_SCHEMA,
92  )
93 
94  hass.services.async_register(
95  DOMAIN,
96  SERVICE_SET_LEVEL,
97  async_service_handler,
98  schema=SERVICE_SET_LEVEL_SCHEMA,
99  )
100 
101  return True
102 
103 
104 def _add_log_filter(logger: logging.Logger, patterns: list[re.Pattern]) -> None:
105  """Add a Filter to the logger based on a regexp of the filter_str."""
106 
107  def filter_func(logrecord: logging.LogRecord) -> bool:
108  return not any(p.search(logrecord.getMessage()) for p in patterns)
109 
110  logger.addFilter(filter_func)
111 
112 
113 def _get_logger_class(hass_overrides: dict[str, int]) -> type[logging.Logger]:
114  """Create a logger subclass.
115 
116  logging.setLoggerClass checks if it is a subclass of Logger and
117  so we cannot use partial to inject hass_overrides.
118  """
119 
120  class HassLogger(logging.Logger):
121  """Home Assistant aware logger class."""
122 
123  def setLevel(self, level: int | str) -> None:
124  """Set the log level unless overridden."""
125  if self.name in hass_overrides:
126  return
127 
128  super().setLevel(level)
129 
130  def orig_setLevel(self, level: int | str) -> None:
131  """Set the log level."""
132  super().setLevel(level)
133 
134  return HassLogger
None set_log_levels(HomeAssistant hass, Mapping[str, int] logpoints)
Definition: helpers.py:56
None set_default_log_level(HomeAssistant hass, int level)
Definition: helpers.py:49
None _add_log_filter(logging.Logger logger, list[re.Pattern] patterns)
Definition: __init__.py:104
bool async_setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:52
type[logging.Logger] _get_logger_class(dict[str, int] hass_overrides)
Definition: __init__.py:113