#!/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"]
[docs]
class LogRecordStreamHandler(socketserver.StreamRequestHandler):
[docs]
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
[docs]
def unPickle(self, data):
return pickle.loads(data)
[docs]
def makeLogRecord(self, obj):
record = logging.makeLogRecord(obj)
if not hasattr(record, "hostName"):
record.hostName = self.hostName
return record
[docs]
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)
[docs]
def stop(self):
self._stop = 1
[docs]
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 = []
[docs]
def registerHandler(self, handler):
if handler is not None:
self.__handlers.append(weakref.ref(handler))
[docs]
def unregisterHandler(self, handler):
try:
self.__handlers.remove(handler)
except ValueError:
pass
[docs]
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
[docs]
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
[docs]
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()