Async logging file handler (#4901)
* Async logging file handler * add time rotation handle * new layout * address paulus comments * fix lint
This commit is contained in:
parent
326cc83a17
commit
6d6abab358
2 changed files with 100 additions and 1 deletions
|
@ -20,6 +20,7 @@ import homeassistant.loader as loader
|
||||||
import homeassistant.util.package as pkg_util
|
import homeassistant.util.package as pkg_util
|
||||||
from homeassistant.util.async import (
|
from homeassistant.util.async import (
|
||||||
run_coroutine_threadsafe, run_callback_threadsafe)
|
run_coroutine_threadsafe, run_callback_threadsafe)
|
||||||
|
from homeassistant.util.logging import AsyncHandler
|
||||||
from homeassistant.util.yaml import clear_secret_cache
|
from homeassistant.util.yaml import clear_secret_cache
|
||||||
from homeassistant.const import EVENT_COMPONENT_LOADED, PLATFORM_FORMAT
|
from homeassistant.const import EVENT_COMPONENT_LOADED, PLATFORM_FORMAT
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
@ -548,8 +549,11 @@ def enable_logging(hass: core.HomeAssistant, verbose: bool=False,
|
||||||
err_handler.setFormatter(
|
err_handler.setFormatter(
|
||||||
logging.Formatter('%(asctime)s %(name)s: %(message)s',
|
logging.Formatter('%(asctime)s %(name)s: %(message)s',
|
||||||
datefmt='%y-%m-%d %H:%M:%S'))
|
datefmt='%y-%m-%d %H:%M:%S'))
|
||||||
|
|
||||||
|
async_handler = AsyncHandler(hass.loop, err_handler)
|
||||||
|
|
||||||
logger = logging.getLogger('')
|
logger = logging.getLogger('')
|
||||||
logger.addHandler(err_handler)
|
logger.addHandler(async_handler)
|
||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
"""Logging utilities."""
|
"""Logging utilities."""
|
||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
import threading
|
||||||
|
|
||||||
|
from .async import run_coroutine_threadsafe
|
||||||
|
|
||||||
|
|
||||||
class HideSensitiveDataFilter(logging.Filter):
|
class HideSensitiveDataFilter(logging.Filter):
|
||||||
|
@ -15,3 +19,94 @@ class HideSensitiveDataFilter(logging.Filter):
|
||||||
record.msg = record.msg.replace(self.text, '*******')
|
record.msg = record.msg.replace(self.text, '*******')
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
class AsyncHandler(object):
|
||||||
|
"""Logging handler wrapper to add a async layer."""
|
||||||
|
|
||||||
|
def __init__(self, loop, handler):
|
||||||
|
"""Initialize async logging handler wrapper."""
|
||||||
|
self.handler = handler
|
||||||
|
self.loop = loop
|
||||||
|
self._queue = asyncio.Queue(loop=loop)
|
||||||
|
self._thread = threading.Thread(target=self._process)
|
||||||
|
|
||||||
|
# Delegate from handler
|
||||||
|
self.setLevel = handler.setLevel
|
||||||
|
self.setFormatter = handler.setFormatter
|
||||||
|
self.addFilter = handler.addFilter
|
||||||
|
self.removeFilter = handler.removeFilter
|
||||||
|
self.filter = handler.filter
|
||||||
|
self.flush = handler.flush
|
||||||
|
self.handle = handler.handle
|
||||||
|
self.handleError = handler.handleError
|
||||||
|
self.format = handler.format
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""Wrap close to handler."""
|
||||||
|
self.emit(None)
|
||||||
|
|
||||||
|
def open(self):
|
||||||
|
"""Wrap open to handler."""
|
||||||
|
self._thread.start()
|
||||||
|
self.handler.open()
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
"""Process a record."""
|
||||||
|
ident = self.loop.__dict__.get("_thread_ident")
|
||||||
|
|
||||||
|
# inside eventloop
|
||||||
|
if ident is not None and ident == threading.get_ident():
|
||||||
|
self._queue.put_nowait(record)
|
||||||
|
# from a thread/executor
|
||||||
|
else:
|
||||||
|
self.loop.call_soon_threadsafe(self._queue.put_nowait, record)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""String name of this."""
|
||||||
|
return str(self.handler)
|
||||||
|
|
||||||
|
def _process(self):
|
||||||
|
"""Process log in a thread."""
|
||||||
|
while True:
|
||||||
|
record = run_coroutine_threadsafe(
|
||||||
|
self._queue.get(), self.loop).result()
|
||||||
|
|
||||||
|
if record is None:
|
||||||
|
self.handler.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
self.handler.emit(record)
|
||||||
|
|
||||||
|
def createLock(self):
|
||||||
|
"""Ignore lock stuff."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def acquire(self):
|
||||||
|
"""Ignore lock stuff."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def release(self):
|
||||||
|
"""Ignore lock stuff."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def level(self):
|
||||||
|
"""Wrap property level to handler."""
|
||||||
|
return self.handler.level
|
||||||
|
|
||||||
|
@property
|
||||||
|
def formatter(self):
|
||||||
|
"""Wrap property formatter to handler."""
|
||||||
|
return self.handler.formatter
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Wrap property set_name to handler."""
|
||||||
|
return self.handler.get_name()
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def set_name(self, name):
|
||||||
|
"""Wrap property get_name to handler."""
|
||||||
|
self.handler.name = name
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue