Enviando mensagens de log para um usuário xmpp
Sempre gostei de bibliotecas de logging. Acho mais fácil lidar com logs bem feitos do que ficar depurando programas em IDEs.
Todas as bibliotecas que usei tem praticamente as mesmas opções: arquivo, e-mail, syslog, etc. Arquivo e syslog depende das pessoas lerem. E-mail não é viável no meu caso: mais de mil máquina para administrar eu facilmente perderia erro de sistema e não é em tempo real.
A solução é simples: mensagens instantâneas. Extendi a API do python e de uma maneira natural é possível integrá-la a códigos prontos.
Sempre quis fazer algum projeto aberto, mas sempre fiz códigos que só faziam sentido dentro da empresa que trabalhei. Esse caso é o primeiro código que realmente é útil para mais de uma pessoa e não depende de nada.
Pensei em criar um projeto no SourceForge ou no code.google.com, mas no meio do processo fiquei vendo: um projeto com apenas uma classe é um pouco ridículo.
Postarei esse código aqui por enquanto e criarei um pacote legal para ele. Quem sabe quando eu colocar mais handlers de log no pacote.
'''
xmpphandler: send logging messages to a xmpp client.
'''
import logging
from Queue import Queue, Empty
from threading import Thread
from time import sleep
import xmpp
class XMPPHandlerException(Exception): pass
class XMPPHandler(logging.Handler):
"""
A Logging Handler that send messages to a XMPP.
This class uses a asynchronous approach: emit just queue the record
and the internal thread handle it.
"""
# record queue
_queue = Queue()
# exit clean
_exit = False
def __init__(self, jid, password, tojid):
logging.Handler.__init__(self)
# try a first connection to validade all parameters
# if we cannot connect, invalidate the Handler
if jid == None:
raise XMPPHandlerException("JID required")
self._jid = xmpp.protocol.JID(jid)
if tojid == None:
raise XMPPHandlerException("ToJid required")
self._tojid = tojid
self._password = password
server = self._jid.getDomain()
self._client = xmpp.Client(server, debug=[])
try:
self._connect()
except (Exception), e:
raise XMPPHandlerException(e)
# Passwords shouldn't be cached, but we need it
# to reconnect if it is the case
self._password = password
# internal thread to handle the messages
self._thread = Thread(name="xmpphandler", target=self._mainLoop)
self._thread.setDaemon(True)
self._thread.start()
# Exit cases need more work
def __del__(self):
self._exit = True
def _canExit(self):
'TODO: a safe exit condition'
return self._exit
def _connect(self):
'(re-)connect on xmpp server'
user = self._jid.getNode()
self._client.connect()
self._client.auth(user, self._password, 'XMPPHandler')
#self._client.sendInitPresence()
def emit(self, record):
'''Queue the record and return fast'''
self._queue.put(record)
def _mainLoop(self):
'Main function. Handle xmpp connection'
while True:
# "missing" peek() method on Queue
r = None
try:
r = self._queue.get(timeout=5)
# Reconnect if disconnected
is_conn = self._client.isConnected()
if is_conn == None or is_conn == '':
self._connect()
self._send(r)
except (Empty), e:
if self._canExit():
print "Exiting..."
return
except (Exception), e:
print "Oh no, exception!" + str(e)
# retry only afer 5 seconds
sleep(5)
def _send(self, record):
'Send the message'
msg = self.format(record)
xmpp_message = xmpp.protocol.Message(self._tojid,msg)
self._client.send(xmpp_message)
# Visible to logging, so we can use logging.config.fileConfig transparently
logging.XMPPHandler = XMPPHandler
Tem três coisas que ficarei devendo:
- um bom critério para matar a thread interna (pode travar o python)
- tentar reenviar as mensagens os invés de descartar (um método peek na Queue ajudaria muito)
- testes (assim que eu decidir a melhor lib de mock eu termino isso)
Posted at 11:43PM Mai 20, 2009 by ze in Python | Comments[0]


