Skip to content
Snippets Groups Projects
lofar_logging.py 2.67 KiB
Newer Older
# 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

    """ 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