# -*- coding: utf-8 -*- # leap_log_handler.py # Copyright (C) 2013 LEAP # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ Custom handler for the logger window. """ import logging from PySide import QtCore class LogHandler(logging.Handler): """ This is the custom handler that implements our desired formatting and also keeps a history of all the logged events. """ MESSAGE_KEY = 'message' RECORD_KEY = 'record' def __init__(self, qtsignal): """ LogHander initialization. Calls parent method and keeps a reference to the qtsignal that will be used to fire the gui update. """ # TODO This is going to eat lots of memory after some time. # Should be pruned at some moment. self._log_history = [] logging.Handler.__init__(self) self._qtsignal = qtsignal def _get_format(self, logging_level): """ Sets the log format depending on the parameter. It uses html and css to set the colors for the logs. :param logging_level: the debug level to define the color. :type logging_level: str. """ html_style = { 'DEBUG': "color: blue", 'INFO': "color: black", 'WARNING': "color: black; background: yellow;", 'ERROR': "color: red", 'CRITICAL': "color: red; font-weight: bold;" } style_open = "" style_close = "" time = "%(asctime)s" name = style_open + "%(name)s" level = "%(levelname)s" message = "%(message)s" + style_close format_attrs = [time, name, level, message] log_format = ' - '.join(format_attrs) formatter = logging.Formatter(log_format) return formatter def emit(self, logRecord): """ This method is fired every time that a record is logged by the logging module. This method reimplements logging.Handler.emit that is fired in every logged message. :param logRecord: the record emitted by the logging module. :type logRecord: logging.LogRecord. """ self.setFormatter(self._get_format(logRecord.levelname)) log = self.format(logRecord) log_item = {self.RECORD_KEY: logRecord, self.MESSAGE_KEY: log} self._log_history.append(log_item) self._qtsignal(log_item) class HandlerAdapter(object): """ New style class that accesses all attributes from the LogHandler. Used as a workaround for a problem with multiple inheritance with Pyside that surfaced under OSX with pyside 1.1.0. """ MESSAGE_KEY = 'message' RECORD_KEY = 'record' def __init__(self, qtsignal): self._handler = LogHandler(qtsignal=qtsignal) def setLevel(self, *args, **kwargs): return self._handler.setLevel(*args, **kwargs) def handle(self, *args, **kwargs): return self._handler.handle(*args, **kwargs) @property def level(self): return self._handler.level class LeapLogHandler(QtCore.QObject, HandlerAdapter): """ Custom logging handler. It emits Qt signals so it can be plugged to a gui. Its inner handler also stores an history of logs that can be fetched after having been connected to a gui. """ # All dicts returned are of the form # {'record': LogRecord, 'message': str} new_log = QtCore.Signal(dict) def __init__(self): """ LeapLogHandler initialization. Initializes parent classes. """ QtCore.QObject.__init__(self) HandlerAdapter.__init__(self, qtsignal=self.qtsignal) def qtsignal(self, log_item): # WARNING: the new-style connection does NOT work because PySide # translates the emit method to self.emit, and that collides with # the emit method for logging.Handler # self.new_log.emit(log_item) QtCore.QObject.emit( self, QtCore.SIGNAL('new_log(PyObject)'), log_item) @property def log_history(self): """ Returns the history of the logged messages. """ return self._handler._log_history