import logging from functools import wraps import sys # 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') # configure log messages 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