Newer
Older
import logging
from functools import wraps
# Always also log the hostname because it makes the origin of the log clear.
import socket
hostname = socket.gethostname()
def configure_logger(logger: logging.Logger, log_extra=None):
logger.setLevel(logging.DEBUG)
try:
from logstash_async.handler import AsynchronousLogstashHandler, LogstashFormatter
# log to the tcp_input of logstash in our ELK stack
handler = AsynchronousLogstashHandler("elk", 5959, database_path='pending_log_messages.db')
formatter = LogstashFormatter(extra=log_extra, tags=["python", "lofar"])
handler.setFormatter(formatter)
# install the handler
logger.addHandler(handler)
# for now, also log to stderr
# Set up logging in a way that it can be understood by a human reader, be
# easily grep'ed, be parsed with a couple of shell commands and
# easily fed into an Kibana/Elastic search system.
handler = logging.StreamHandler()
formatter = logging.Formatter(fmt = '%(asctime)s.%(msecs)d %(levelname)s - HOST="{}" PID="%(process)d" TNAME="%(threadName)s" TID="%(thread)d" FILE="%(pathname)s" LINE="%(lineno)d" FUNC="%(funcName)s" MSG="%(message)s"'.format(hostname), datefmt = '%Y-%m-%dT%H:%M:%S')
handler.setFormatter(formatter)
logger.addHandler(handler)
except Exception:
logger.exception("Cannot import or configure logstash_async module, not forwarding logs to ELK stack.")
return logger
def device_logging_to_python(log_extra: dict = None):
""" Call this on a Tango Device instance or class to have your Tango Device log to python instead. """
def inner(cls):
# Create a logger that logs to ELK, dedicated for this class
logger = logging.getLogger(cls.__name__)
configure_logger(logger, log_extra)
# Monkey patch the python logger to replace the tango logger
cls.debug_stream = logger.debug
cls.info_stream = logger.info
cls.warn_stream = logger.warning
cls.warning_stream = logger.warning
cls.error_stream = logger.error
cls.fatal_stream = logger.fatal
cls.critical_stream = logger.critical
return cls
return inner
def log_exceptions():
""" Decorator that logs all exceptions that the function raises. """
def wrapper(func):
@wraps(func)
def inner(self, *args, **kwargs):
try:
return func(self, *args, **kwargs)
except Exception as e:
self.error_stream("Caught exception: %s: %s", e.__class__.__name__, e, exc_info=1)
raise e
return inner
return wrapper