# Source code for taurus.core.util.remotelogmonitor

    #!/usr/bin/env python

    # ###########################################################################
    #
    # This file is part of Taurus
    #
    # http://taurus-scada.org
    #
    # Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
    #
    # Taurus is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Lesser General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.
    #
    # Taurus is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Lesser General Public License for more details.
    #
    # You should have received a copy of the GNU Lesser General Public License
    # along with Taurus.  If not, see <http://www.gnu.org/licenses/>.
    #
    # ###########################################################################

    """Useful module for remote logging
    """

    import time
    import socket
    import pickle
    import logging
    import logging.handlers
    import struct
    import weakref
    import click

    import socketserver

    import taurus.cli.common

    __all__ = ["LogRecordStreamHandler", "LogRecordSocketReceiver", "log"]

    class LogRecordStreamHandler(socketserver.StreamRequestHandler):

        def handle(self):
            try:
                return self._handle()
            except Exception:
                pass

        def _handle(self):
            self._stop = stop = 0
            self.hostName = self.server.hostName
            self.server.registerHandler(self)
            while not stop:
                chunk = self.connection.recv(4)
                if len(chunk) < 4:
                    break
                slen = struct.unpack(">L", chunk)[0]
                chunk = self.connection.recv(slen)
                while len(chunk) < slen:
                    chunk = chunk + self.connection.recv(slen - len(chunk))
                obj = self.unPickle(chunk)
                record = self.makeLogRecord(obj)
                self.handleLogRecord(record)
                stop = self._stop

        def unPickle(self, data):
            return pickle.loads(data)

        def makeLogRecord(self, obj):
            record = logging.makeLogRecord(obj)
            if not hasattr(record, "hostName"):
                record.hostName = self.hostName
            return record

        def handleLogRecord(self, record):
            logger = self.server.data.get("logger")
            if logger is None:
                logger = logging.getLogger(record.name)
            if not logger.isEnabledFor(record.levelno):
                return
            logger.handle(record)

        def stop(self):
            self._stop = 1

    class LogRecordSocketReceiver(socketserver.ThreadingTCPServer):
        """
        Simple TCP socket-based logging receiver suitable for testing.
        """

        allow_reuse_address = 1
        daemon_threads = True

        def __init__(
            self,
            host="localhost",
            port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
            handler=LogRecordStreamHandler,
            **kwargs
        ):
            socketserver.ThreadingTCPServer.__init__(self, (host, port), handler)
            self.hostName = socket.gethostbyaddr(host)[0]
            self.port = port
            self._stop = 0
            self._stopped = 0
            self.timeout = 1
            self.data = kwargs
            self.__handlers = []

        def registerHandler(self, handler):
            if handler is not None:
                self.__handlers.append(weakref.ref(handler))

        def unregisterHandler(self, handler):
            try:
                self.__handlers.remove(handler)
            except ValueError:
                pass

        def serve_until_stopped(self):
            import select

            stop = 0
            while not stop:
                rd, wr, ex = select.select(
                    [self.socket.fileno()], [], [], self.timeout
                )
                if rd:
                    self.handle_request()
                stop = self._stop
            self._stopped = 1

        def stop(self):
            self._stop = True
            while not self._stopped:
                time.sleep(0.1)
            for handler in self.__handlers:
                h = handler()
                if h is not None:
                    h.stop()
                    h.finish()
                    self.close_request(h.connection)
            self.socket.close()

    class LogNameFilter(logging.Filter):
        def __init__(self, name=None):
            self.name = name

        def filter(self, record):
            name = self.name
            if name is None:
                return True
            return record.name == name

    def log(host, port, name=None, level=None):
        local_logger_name = "RemoteLogger.%s.%d" % (host, port)
        local_logger = logging.getLogger(local_logger_name)

        if name is not None:
            local_logger.addFilter(LogNameFilter(name=name))

        if level is not None:
            local_logger.setLevel(level)

        tcpserver = LogRecordSocketReceiver(
            host=host, port=port, logger=local_logger
        )
        msg = "logging for '%s' on port %d" % (host, port)
        if name is not None:
            msg += " for " + name
        print("Start", msg)

        try:
            tcpserver.serve_until_stopped()
        except KeyboardInterrupt:
            print("\nCancelled", msg)

    @click.command("logmon")
    @click.option(
        "--port",
        "port",
        type=int,
        default=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
        show_default=True,
        help="Port where log server is running",
    )
    @click.option(
        "--log-name",
        "log_name",
        default=None,
        help="Filter specific log object",
    )
    @taurus.cli.common.log_level
    def logmon_cmd(port, log_name, log_level):
        """Show the console-based Taurus Remote Log Monitor"""
        import taurus

        host = socket.gethostname()
        level = getattr(taurus, log_level.capitalize(), taurus.Trace)

        log(host=host, port=port, name=log_name, level=level)

    if __name__ == "__main__":
        logmon_cmd()