Source code for ewokscore.events.global_state
"""Manage the EWOKS event logger which is a global object
"""
import os
import logging
import warnings
from contextlib import contextmanager
from typing import Dict, Iterable, List, Optional, Tuple
from ewoksutils.logging_utils.cleanup import cleanup_logger
from ewoksutils.logging_utils.cleanup import protect_logging_state
from ewoksutils.logging_utils.asyncwrapper import AsyncHandlerWrapper
from .handlers import is_ewoks_event_handler, instantiate_handler
_app_logger = logging.getLogger(__name__)
EWOKS_EVENT_LOGGER_NAME = f"__{__name__}__"
ENABLE_EWOKS_EVENTS_BY_DEFAULT = True
[docs]
def send(
*args,
handlers: Optional[List[Dict[str, str]]] = None,
asynchronous: Optional[bool] = None,
**kw,
) -> None:
"""Log an EWOKS event with the EWOKS event handlers and the application handlers."""
with _ewoks_event_logger(handlers=handlers, asynchronous=asynchronous) as logger:
# Send to the EWOKS event handlers
logger.info(*args, **kw)
# Send to the application log handlers
_app_logger.info(*args, **kw)
[docs]
def add_handler(
handler,
asynchronous: Optional[bool] = None,
) -> None:
"""Add a handler to the global EWOKS event logger."""
with _ewoks_event_logger() as logger:
if _has_handler_instance(logger, handler):
return
if asynchronous is None and is_ewoks_event_handler(handler):
asynchronous = handler.BLOCKING
if asynchronous:
handler = AsyncHandlerWrapper(handler)
logger.addHandler(handler)
[docs]
def remove_handler(handler: logging.Handler) -> None:
"""Remove a handler from all loggers that receive EWOKS event."""
warnings.warn(
"Explicit Ewoks handler removal will be removed.",
DeprecationWarning,
stacklevel=2,
)
with _ewoks_event_logger() as logger:
for linstance, hinstance in _iter_handler_owners(logger, handler):
linstance.removeHandler(hinstance)
hinstance.close()
[docs]
def cleanup():
"""Pending events will be dropped"""
with protect_logging_state():
_cleanup_ewoks_event_logger()
def _after_fork_in_child():
cleanup()
if hasattr(os, "register_at_fork"):
os.register_at_fork(after_in_child=_after_fork_in_child)
@contextmanager
def _ewoks_event_logger(
handlers: Optional[List[Dict[str, str]]] = None, asynchronous: Optional[bool] = None
) -> Iterable[logging.Logger]:
"""Initialize and yield the EWOKS event logger"""
# Issue with logging and forking:
# https://pythonspeed.com/articles/python-multiprocessing/
with protect_logging_state():
if _ewoks_event_logger_requires_cleanup():
_cleanup_ewoks_event_logger()
if _ewoks_event_logger_requires_init():
_init_ewoks_event_logger(handlers, asynchronous)
yield logging.getLogger(EWOKS_EVENT_LOGGER_NAME)
def _cleanup_ewoks_event_logger():
"""Cleanup and delete the global EWOKS event logger"""
cleanup_logger(EWOKS_EVENT_LOGGER_NAME)
def _ewoks_event_logger_requires_init() -> bool:
logger = logging.getLogger(EWOKS_EVENT_LOGGER_NAME)
return not hasattr(logger, "ewoks_pid")
def _ewoks_event_logger_requires_cleanup() -> bool:
logger = logging.getLogger(EWOKS_EVENT_LOGGER_NAME)
ewoks_pid = getattr(logger, "ewoks_pid", None)
return ewoks_pid is not None and ewoks_pid != os.getpid()
def _init_ewoks_event_logger(
handlers: Optional[List[Dict[str, str]]], asynchronous: Optional[bool]
):
logger = logging.getLogger(EWOKS_EVENT_LOGGER_NAME)
logger.setLevel(logging.DEBUG)
logger.ewoks_pid = os.getpid()
logger.propagate = False
if not handlers:
return
for desc in handlers:
try:
kwargs = {
arg["name"]: arg["value"] for arg in desc.get("arguments", list())
}
handler = instantiate_handler(desc["class"], **kwargs)
except Exception as e:
raise RuntimeError(
"cannot create an EWOKS event handler from the description"
) from e
asynchronous_handler = desc.get("asynchronous", asynchronous)
add_handler(handler, asynchronous=asynchronous_handler)
def _iter_loggers(logger: logging.Logger) -> Iterable[logging.Logger]:
"""Yield all loggers which will receive EWOKS events."""
_logger = logger
while _logger is not None:
yield _logger
if not _logger.propagate:
return
_logger = _logger.parent
def _iter_handler_owners(
logger: logging.Logger, instance: logging.Handler
) -> Iterable[Tuple[logging.Logger, logging.Handler]]:
"""Yield all loggers which have a specific handler (or a handler that wraps the specific event handler)."""
for _logger in _iter_loggers(logger):
for handler in _logger.handlers:
if handler is instance:
yield _logger, instance
elif isinstance(handler, AsyncHandlerWrapper):
instance2 = handler.wrapped_handler
if instance2 is instance:
yield _logger, instance2
def _has_handler_instance(logger: logging.Logger, instance: logging.Handler) -> bool:
"""Is this handler registered with a logger or it's parents?"""
for _ in _iter_handler_owners(logger, instance):
return True
return False