diff --git a/classes.py b/classes.py index 91a53c8..ff78ff5 100644 --- a/classes.py +++ b/classes.py @@ -17,7 +17,7 @@ from collections import defaultdict import hashlib from copy import deepcopy -from log import log +from log import * import world import utils @@ -37,6 +37,7 @@ class Irc(): (a string), the name of the protocol module to use for this connection, and a configuration object. """ + self.loghandler = None self.name = netname.lower() self.conf = conf self.serverdata = conf['servers'][netname] @@ -60,6 +61,23 @@ class Irc(): self.connection_thread.start() self.pingTimer = None + def logSetup(self): + """ + Initializes any channel loggers defined for the current network. + """ + try: + channels = self.conf['logging']['channels'][self.name] + except KeyError: # Not set up; just ignore. + return + + log.debug('(%s) Setting up channel logging to channels %r', self.name, + channels) + if channels and not self.loghandler: + # Only create a handler if we have channels to log to, and one + # doesn't already exist. + self.loghandler = PyLinkChannelLogger(self, channels) + log.addHandler(self.loghandler) + def initVars(self): """ (Re)sets an IRC object to its default state. This should be called when @@ -128,6 +146,9 @@ class Irc(): self.uplink = None self.start_ts = int(time.time()) + # Set up channel logging for the network + self.logSetup() + def connect(self): """ Runs the connect loop for the IRC object. This is usually called by @@ -254,6 +275,11 @@ class Irc(): log.debug('(%s) _disconnect: Setting self.aborted to True.', self.name) self.aborted.set() + if self.loghandler is not None: + log.debug('(%s) Removing channel logging handler due to disconnect.', self.name) + log.removeHandler(self.loghandler) + self.loghandler = None + try: log.debug('(%s) _disconnect: Shutting down and closing socket.', self.name) self.socket.shutdown(socket.SHUT_RDWR) diff --git a/example-conf.yml b/example-conf.yml index 026c0fe..0b9d3d0 100644 --- a/example-conf.yml +++ b/example-conf.yml @@ -30,6 +30,15 @@ login: user: admin password: changeme +logging: + # This configuration block defines channels that PyLink can log to. + + # Note: DEBUG logging is not supported here: any log level settings below + # INFO be automatically raised to INFO. + channels: + inspnet: + - "#services" + servers: inspnet: # Server IP, port, and passwords diff --git a/log.py b/log.py index f36ee1d..2b8927b 100644 --- a/log.py +++ b/log.py @@ -35,3 +35,33 @@ logfile.setFormatter(logformat) global log log = logging.getLogger() log.addHandler(logfile) + +class PyLinkChannelLogger(logging.Handler): + """ + Log handler to log to channels in PyLink. + """ + def __init__(self, irc, channels): + super(PyLinkChannelLogger, self).__init__() + self.irc = irc + self.channels = channels + + # Use a slightly simpler message formatter - logging to IRC doesn't need + # logging the time. + formatter = logging.Formatter('[%(levelname)s] %(message)s') + self.setFormatter(formatter) + + # Log level has to be at least 20 (INFO) to prevent loops due + # to outgoing messages being logged + loglevel = max(log.getEffectiveLevel(), 20) + self.setLevel(loglevel) + + def emit(self, record): + """ + Logs a record to the configured channels for the network given. + """ + # Only start logging if we're finished bursting + if hasattr(self.irc, 'pseudoclient') and self.irc.connected.is_set(): + msg = self.format(record) + for channel in self.channels: + self.irc.msg(channel, msg) +