diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/leap/app.py | 15 | ||||
-rw-r--r-- | src/leap/crypto/srpauth.py | 9 | ||||
-rw-r--r-- | src/leap/gui/__init__.py | 21 | ||||
-rw-r--r-- | src/leap/gui/loggerwindow.py | 124 | ||||
-rw-r--r-- | src/leap/gui/mainwindow.py | 37 | ||||
-rw-r--r-- | src/leap/gui/ui/loggerwindow.ui | 155 | ||||
-rw-r--r-- | src/leap/gui/ui/mainwindow.ui | 10 | ||||
-rw-r--r-- | src/leap/services/eip/eipspec.py | 4 | ||||
-rw-r--r-- | src/leap/services/eip/providerbootstrapper.py | 10 | ||||
-rw-r--r-- | src/leap/services/eip/tests/test_eipconfig.py | 25 | ||||
-rw-r--r-- | src/leap/services/mail/smtpbootstrapper.py | 8 | ||||
-rw-r--r-- | src/leap/services/mail/smtpconfig.py | 1 | ||||
-rw-r--r-- | src/leap/services/soledad/soledadbootstrapper.py | 8 | ||||
-rw-r--r-- | src/leap/services/soledad/soledadconfig.py | 1 | ||||
-rw-r--r-- | src/leap/util/leap_log_handler.py | 99 |
15 files changed, 496 insertions, 31 deletions
diff --git a/src/leap/app.py b/src/leap/app.py index 797cea8a..03552edb 100644 --- a/src/leap/app.py +++ b/src/leap/app.py @@ -26,6 +26,7 @@ from PySide import QtCore, QtGui from leap.common.events import server as event_server from leap.util import __version__ as VERSION from leap.util import leap_argparse +from leap.util.leap_log_handler import LeapLogHandler from leap.gui import locale_rc from leap.gui import twisted_main from leap.gui.mainwindow import MainWindow @@ -33,6 +34,7 @@ from leap.platform_init import IS_MAC from leap.platform_init.locks import we_are_the_one_and_only from leap.services.tx import leap_services + import codecs codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None) @@ -75,16 +77,23 @@ def main(): else: level = logging.WARNING + # Console logger logger = logging.getLogger(name='leap') logger.setLevel(level) console = logging.StreamHandler() console.setLevel(level) - formatter = logging.Formatter( - '%(asctime)s ' - '- %(name)s - %(levelname)s - %(message)s') + log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' + formatter = logging.Formatter(log_format) console.setFormatter(formatter) logger.addHandler(console) + # LEAP custom handler + leap_handler = LeapLogHandler() + leap_handler.setLevel(level) + logger.addHandler(leap_handler) + + logger.debug('Leap handler plugged!') + if not we_are_the_one_and_only(): # leap-client is already running logger.warning("Tried to launch more than one instance " diff --git a/src/leap/crypto/srpauth.py b/src/leap/crypto/srpauth.py index 2f3cbd1c..f1897e1d 100644 --- a/src/leap/crypto/srpauth.py +++ b/src/leap/crypto/srpauth.py @@ -22,6 +22,9 @@ import requests import srp import json +#this error is raised from requests +from simplejson.decoder import JSONDecodeError + from PySide import QtCore, QtGui from leap.common.check import leap_assert @@ -232,7 +235,10 @@ class SRPAuth(QtCore.QObject): raise SRPAuthenticationError(self.tr("Could not connect to " "the server")) - content, mtime = get_content(auth_result) + try: + content, mtime = get_content(auth_result) + except JSONDecodeError: + raise SRPAuthenticationError("Bad JSON content in auth result") if auth_result.status_code == 422: logger.error("[%s] Wrong password (HAMK): [%s]" % @@ -319,6 +325,7 @@ class SRPAuth(QtCore.QObject): self._authentication_preprocessing(username, password) salt, B = self._start_authentication(username, password) M2 = self._process_challenge(salt, B, username) + self._verify_session(M2) leap_assert(self.get_session_id(), "Something went wrong because" diff --git a/src/leap/gui/__init__.py b/src/leap/gui/__init__.py index e69de29b..d31dac64 100644 --- a/src/leap/gui/__init__.py +++ b/src/leap/gui/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# __init__.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 <http://www.gnu.org/licenses/>. +""" +init file for leap.gui +""" +from .. import app +__all__ = [app] diff --git a/src/leap/gui/loggerwindow.py b/src/leap/gui/loggerwindow.py new file mode 100644 index 00000000..dd724ac7 --- /dev/null +++ b/src/leap/gui/loggerwindow.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- +# loggerwindow.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 <http://www.gnu.org/licenses/>. + +""" +History log window +""" +import logging + +from PySide import QtGui +from ui_loggerwindow import Ui_LoggerWindow +from leap.common.check import leap_assert, leap_assert_type +from leap.util.leap_log_handler import LeapLogHandler + +logger = logging.getLogger(__name__) + + +class LoggerWindow(QtGui.QWidget): + """ + Window that displays a history of the logged messages in the app. + """ + def __init__(self, handler): + """ + Initialize the widget with the custom handler. + + :param handler: Custom handler that supports history and signal. + :type handler: LeapLogHandler. + """ + QtGui.QWidget.__init__(self) + leap_assert(handler, "We need a handler for the logger window") + leap_assert_type(handler, LeapLogHandler) + + # Load UI + self.ui = Ui_LoggerWindow() + self.ui.setupUi(self) + + # Make connections + self.ui.btnSave.clicked.connect(self._save_log_to_file) + self.ui.btnDebug.toggled.connect(self._load_history), + self.ui.btnInfo.toggled.connect(self._load_history), + self.ui.btnWarning.toggled.connect(self._load_history), + self.ui.btnError.toggled.connect(self._load_history), + self.ui.btnCritical.toggled.connect(self._load_history) + + # Load logging history and connect logger with the widget + self._logging_handler = handler + self._connect_to_handler() + self._load_history() + + def _connect_to_handler(self): + """ + This method connects the loggerwindow with the handler through a + signal communicate the logger events. + """ + self._logging_handler.new_log.connect(self._add_log_line) + + def _add_log_line(self, log): + """ + Adds a line to the history, only if it's in the desired levels to show. + + :param log: a log record to be inserted in the widget + :type log: a dict with RECORD_KEY and MESSAGE_KEY. + the record contains the LogRecord of the logging module, + the message contains the formatted message for the log. + """ + level = log[LeapLogHandler.RECORD_KEY].levelname + message = log[LeapLogHandler.MESSAGE_KEY] + + if self._logs_to_display[level]: + self.ui.txtLogHistory.append(message) + + def _load_history(self): + """ + Load the previous logged messages in the widget. + They are stored in the custom handler. + """ + self._set_logs_to_display() + self.ui.txtLogHistory.clear() + history = self._logging_handler.log_history + for line in history: + self._add_log_line(line) + + def _set_logs_to_display(self): + """ + Sets the logs_to_display dict getting the toggled options from the ui + """ + self._logs_to_display = { + 'DEBUG': self.ui.btnDebug.isChecked(), + 'INFO': self.ui.btnInfo.isChecked(), + 'WARNING': self.ui.btnWarning.isChecked(), + 'ERROR': self.ui.btnError.isChecked(), + 'CRITICAL': self.ui.btnCritical.isChecked() + } + + def _save_log_to_file(self): + """ + Lets the user save the current log to a file + """ + fileName, filtr = QtGui.QFileDialog.getSaveFileName( + self, self.tr("Save As")) + + if fileName: + try: + with open(fileName, 'w') as output: + output.write(self.ui.txtLogHistory.toPlainText()) + output.write('\n') + logger.debug('Log saved in %s' % (fileName, )) + except IOError, e: + logger.error("Error saving log file: %r" % (e, )) + else: + logger.debug('Log not saved!') diff --git a/src/leap/gui/mainwindow.py b/src/leap/gui/mainwindow.py index a8aa1b1c..12187f51 100644 --- a/src/leap/gui/mainwindow.py +++ b/src/leap/gui/mainwindow.py @@ -35,6 +35,7 @@ from leap.common.events import events_pb2 as proto from leap.config.leapsettings import LeapSettings from leap.config.providerconfig import ProviderConfig from leap.crypto.srpauth import SRPAuth +from leap.gui.loggerwindow import LoggerWindow from leap.gui.wizard import Wizard from leap.services.eip.eipbootstrapper import EIPBootstrapper from leap.services.eip.eipconfig import EIPConfig @@ -208,6 +209,7 @@ class MainWindow(QtGui.QMainWindow): self.ui.action_about_leap.triggered.connect(self._about) self.ui.action_quit.triggered.connect(self.quit) self.ui.action_wizard.triggered.connect(self._launch_wizard) + self.ui.action_show_logs.triggered.connect(self._show_logger_window) self.raise_window.connect(self._do_raise_mainwindow) # Used to differentiate between real quits and close to tray @@ -249,6 +251,8 @@ class MainWindow(QtGui.QMainWindow): self._wizard = None self._wizard_firstrun = False + self._logger_window = None + self._bypass_checks = bypass_checks self._soledad = None @@ -282,6 +286,35 @@ class MainWindow(QtGui.QMainWindow): self._wizard.exec_() self._wizard = None + def _get_leap_logging_handler(self): + """ + Gets the leap handler from the top level logger + + :return: a logging handler or None + :rtype: LeapLogHandler or None + """ + from leap.util.leap_log_handler import LeapLogHandler + leap_logger = logging.getLogger('leap') + for h in leap_logger.handlers: + if isinstance(h, LeapLogHandler): + return h + return None + + def _show_logger_window(self): + """ + Displays the window with the history of messages logged until now + and displays the new ones on arrival. + """ + if self._logger_window is None: + leap_log_handler = self._get_leap_logging_handler() + if leap_log_handler is None: + logger.error('Leap logger handler not found') + else: + self._logger_window = LoggerWindow(handler=leap_log_handler) + self._logger_window.show() + else: + self._logger_window.show() + def _remember_state_changed(self, state): enable = True if state == QtCore.Qt.Checked else False self.ui.chkAutoLogin.setEnabled(enable) @@ -1101,6 +1134,10 @@ class MainWindow(QtGui.QMainWindow): self._really_quit = True if self._wizard: self._wizard.close() + + if self._logger_window: + self._logger_window.close() + self.close() if self._quit_callback: diff --git a/src/leap/gui/ui/loggerwindow.ui b/src/leap/gui/ui/loggerwindow.ui new file mode 100644 index 00000000..28325cdf --- /dev/null +++ b/src/leap/gui/ui/loggerwindow.ui @@ -0,0 +1,155 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>LoggerWindow</class> + <widget class="QWidget" name="LoggerWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>648</width> + <height>469</height> + </rect> + </property> + <property name="windowTitle"> + <string>Logs</string> + </property> + <property name="windowIcon"> + <iconset resource="../../../../data/resources/mainwindow.qrc"> + <normaloff>:/images/leap-color-big.png</normaloff>:/images/leap-color-big.png</iconset> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="2" column="0" colspan="2"> + <widget class="QTextBrowser" name="txtLogHistory"/> + </item> + <item row="0" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QPushButton" name="btnDebug"> + <property name="text"> + <string>Debug</string> + </property> + <property name="icon"> + <iconset resource="../../../../data/resources/loggerwindow.qrc"> + <normaloff>:/images/oxygen-icons/script-error.png</normaloff>:/images/oxygen-icons/script-error.png</iconset> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnInfo"> + <property name="text"> + <string>Info</string> + </property> + <property name="icon"> + <iconset resource="../../../../data/resources/loggerwindow.qrc"> + <normaloff>:/images/oxygen-icons/dialog-information.png</normaloff>:/images/oxygen-icons/dialog-information.png</iconset> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnWarning"> + <property name="text"> + <string>Warning</string> + </property> + <property name="icon"> + <iconset resource="../../../../data/resources/loggerwindow.qrc"> + <normaloff>:/images/oxygen-icons/dialog-warning.png</normaloff>:/images/oxygen-icons/dialog-warning.png</iconset> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnError"> + <property name="text"> + <string>Error</string> + </property> + <property name="icon"> + <iconset resource="../../../../data/resources/loggerwindow.qrc"> + <normaloff>:/images/oxygen-icons/dialog-error.png</normaloff>:/images/oxygen-icons/dialog-error.png</iconset> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnCritical"> + <property name="text"> + <string>Critical</string> + </property> + <property name="icon"> + <iconset resource="../../../../data/resources/loggerwindow.qrc"> + <normaloff>:/images/oxygen-icons/edit-bomb.png</normaloff>:/images/oxygen-icons/edit-bomb.png</iconset> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnSave"> + <property name="text"> + <string>Save to file</string> + </property> + <property name="icon"> + <iconset resource="../../../../data/resources/loggerwindow.qrc"> + <normaloff>:/images/oxygen-icons/document-save-as.png</normaloff>:/images/oxygen-icons/document-save-as.png</iconset> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <tabstops> + <tabstop>btnDebug</tabstop> + <tabstop>btnInfo</tabstop> + <tabstop>btnWarning</tabstop> + <tabstop>btnError</tabstop> + <tabstop>btnCritical</tabstop> + <tabstop>btnSave</tabstop> + <tabstop>txtLogHistory</tabstop> + </tabstops> + <resources> + <include location="../../../../data/resources/loggerwindow.qrc"/> + <include location="../../../../data/resources/mainwindow.qrc"/> + </resources> + <connections/> +</ui> diff --git a/src/leap/gui/ui/mainwindow.ui b/src/leap/gui/ui/mainwindow.ui index b42ff180..fdf5c167 100644 --- a/src/leap/gui/ui/mainwindow.ui +++ b/src/leap/gui/ui/mainwindow.ui @@ -396,7 +396,7 @@ <x>0</x> <y>0</y> <width>415</width> - <height>25</height> + <height>21</height> </rect> </property> <widget class="QMenu" name="menuSession"> @@ -417,9 +417,10 @@ </widget> <widget class="QMenu" name="menuSettings"> <property name="title"> - <string>S&ettings</string> + <string>&Utils</string> </property> <addaction name="action_wizard"/> + <addaction name="action_show_logs"/> </widget> <addaction name="menuSession"/> <addaction name="menuSettings"/> @@ -451,6 +452,11 @@ <string>&Wizard</string> </property> </action> + <action name="action_show_logs"> + <property name="text"> + <string>Show &logs</string> + </property> + </action> </widget> <tabstops> <tabstop>lnUser</tabstop> diff --git a/src/leap/services/eip/eipspec.py b/src/leap/services/eip/eipspec.py index 7fa782c7..94ba674f 100644 --- a/src/leap/services/eip/eipspec.py +++ b/src/leap/services/eip/eipspec.py @@ -22,12 +22,12 @@ eipservice_config_spec = { 'serial': { 'type': int, 'default': 1, - 'required': True + 'required': ["True"] }, 'version': { 'type': int, 'default': 1, - 'required': True + 'required': ["True"] }, 'clusters': { 'type': list, diff --git a/src/leap/services/eip/providerbootstrapper.py b/src/leap/services/eip/providerbootstrapper.py index 734d3867..289d212b 100644 --- a/src/leap/services/eip/providerbootstrapper.py +++ b/src/leap/services/eip/providerbootstrapper.py @@ -209,13 +209,16 @@ class ProviderBootstrapper(QtCore.QObject): def run_provider_select_checks(self, checker, domain, download_if_needed=False): """ - Populates the check queue + Populates the check queue. :param checker: checker thread to be used to run this check :type checker: CheckerThread + :param domain: domain to check :type domain: str - :param download_if_needed: if True, makes the checks do not overwrite already downloaded data + + :param download_if_needed: if True, makes the checks do not + overwrite already downloaded data :type download_if_needed: bool :return: True if the checks passed, False otherwise @@ -407,7 +410,8 @@ class ProviderBootstrapper(QtCore.QObject): :param provider_config: Provider configuration :type provider_config: ProviderConfig - :param download_if_needed: if True, makes the checks do not overwrite already downloaded data. + :param download_if_needed: if True, makes the checks do not + overwrite already downloaded data. :type download_if_needed: bool """ leap_assert(provider_config, "We need a provider config!") diff --git a/src/leap/services/eip/tests/test_eipconfig.py b/src/leap/services/eip/tests/test_eipconfig.py index ce04c2fc..0bd19d5e 100644 --- a/src/leap/services/eip/tests/test_eipconfig.py +++ b/src/leap/services/eip/tests/test_eipconfig.py @@ -97,10 +97,12 @@ class EIPConfigTest(BaseLeapTest): """ self.write_config(sample_config) config = EIPConfig() - self.assertRaises( - AssertionError, - config.get_clusters) - self.assertTrue(config.load(self.configfile)) + #self.assertRaises( + #AssertionError, + #config.get_clusters) + + self.assertTrue(config.load( + self.configfile, relative=False)) self.assertEqual( config.get_openvpn_configuration(), sample_config["openvpn_configuration"]) @@ -123,7 +125,8 @@ class EIPConfigTest(BaseLeapTest): data['openvpn_configuration']["extra_param"] = "FOO" self.write_config(data) config = EIPConfig() - config.load(self.configfile) + config.load( + self.configfile, relative=False) self.assertEqual( config.get_openvpn_configuration(), sample_config["openvpn_configuration"]) @@ -133,7 +136,7 @@ class EIPConfigTest(BaseLeapTest): data['openvpn_configuration']["auth"] = "SHA1;" self.write_config(data) config = EIPConfig() - config.load(self.configfile) + config.load(self.configfile, relative=False) self.assertEqual( config.get_openvpn_configuration(), sample_config["openvpn_configuration"]) @@ -143,7 +146,7 @@ class EIPConfigTest(BaseLeapTest): data['openvpn_configuration']["auth"] = "SHA1>`&|" self.write_config(data) config = EIPConfig() - config.load(self.configfile) + config.load(self.configfile, relative=False) self.assertEqual( config.get_openvpn_configuration(), sample_config["openvpn_configuration"]) @@ -153,7 +156,7 @@ class EIPConfigTest(BaseLeapTest): data['openvpn_configuration']["auth"] = "shaSHA1" self.write_config(data) config = EIPConfig() - config.load(self.configfile) + config.load(self.configfile, relative=False) self.assertEqual( config.get_openvpn_configuration(), sample_config["openvpn_configuration"]) @@ -163,7 +166,7 @@ class EIPConfigTest(BaseLeapTest): data['openvpn_configuration']["auth"] = "sha&*!@#;" self.write_config(data) config = EIPConfig() - config.load(self.configfile) + config.load(self.configfile, relative=False) self.assertEqual( config.get_openvpn_configuration(), {'cipher': 'AES-128-CBC', @@ -174,7 +177,7 @@ class EIPConfigTest(BaseLeapTest): data['gateways'][0]["ip_address"] = "11.22.33.44;" self.write_config(data) config = EIPConfig() - config.load(self.configfile) + config.load(self.configfile, relative=False) self.assertEqual( config.get_gateway_ip(), None) @@ -183,7 +186,7 @@ class EIPConfigTest(BaseLeapTest): data['gateways'][0]["ip_address"] = "11.22.33.44`" self.write_config(data) config = EIPConfig() - config.load(self.configfile) + config.load(self.configfile, relative=False) self.assertEqual( config.get_gateway_ip(), None) diff --git a/src/leap/services/mail/smtpbootstrapper.py b/src/leap/services/mail/smtpbootstrapper.py index 7e0f10de..6e0a0a47 100644 --- a/src/leap/services/mail/smtpbootstrapper.py +++ b/src/leap/services/mail/smtpbootstrapper.py @@ -116,10 +116,10 @@ class SMTPBootstrapper(QtCore.QObject): # Not modified if res.status_code == 304: logger.debug("SMTP definition has not been modified") - self._smtp_config.load(os.path.join("leap", - "providers", - self._provider_config.get_domain(), - "smtp-service.json")) + self._smtp_config.load(os.path.join( + "leap", "providers", + self._provider_config.get_domain(), + "smtp-service.json")) else: smtp_definition, mtime = get_content(res) diff --git a/src/leap/services/mail/smtpconfig.py b/src/leap/services/mail/smtpconfig.py index e7e2895a..30371005 100644 --- a/src/leap/services/mail/smtpconfig.py +++ b/src/leap/services/mail/smtpconfig.py @@ -45,4 +45,3 @@ class SMTPConfig(BaseConfig): def get_locations(self): return self._safe_get_value("locations") - diff --git a/src/leap/services/soledad/soledadbootstrapper.py b/src/leap/services/soledad/soledadbootstrapper.py index 2869e098..eea9b0d5 100644 --- a/src/leap/services/soledad/soledadbootstrapper.py +++ b/src/leap/services/soledad/soledadbootstrapper.py @@ -218,7 +218,8 @@ class SoledadBootstrapper(QtCore.QObject): self._keymanager.get_key(address, openpgp.OpenPGPKey, private=True, fetch_remote=False) except KeyNotFound: - logger.debug("Key not found. Generating key for %s" % (address,)) + logger.debug( + "Key not found. Generating key for %s" % (address,)) self._keymanager.gen_key(openpgp.OpenPGPKey) logger.debug("Key generated successfully.") @@ -235,8 +236,9 @@ class SoledadBootstrapper(QtCore.QObject): logger.debug("Uploading public key to %s" % (key_uri,)) - pubkey = self._keymanager.get_key(address, openpgp.OpenPGPKey, - private=False, fetch_remote=False) + pubkey = self._keymanager.get_key( + address, openpgp.OpenPGPKey, + private=False, fetch_remote=False) key_data = { self.PUBKEY_KEY: pubkey.key_data, } diff --git a/src/leap/services/soledad/soledadconfig.py b/src/leap/services/soledad/soledadconfig.py index 836265f3..80a82d11 100644 --- a/src/leap/services/soledad/soledadconfig.py +++ b/src/leap/services/soledad/soledadconfig.py @@ -45,4 +45,3 @@ class SoledadConfig(BaseConfig): def get_locations(self): return self._safe_get_value("locations") - diff --git a/src/leap/util/leap_log_handler.py b/src/leap/util/leap_log_handler.py new file mode 100644 index 00000000..0e598032 --- /dev/null +++ b/src/leap/util/leap_log_handler.py @@ -0,0 +1,99 @@ +# -*- 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 <http://www.gnu.org/licenses/>. + +""" +Custom handler for the logger window. +""" +import logging + +from PySide import QtCore + + +class LeapLogHandler(logging.Handler, QtCore.QObject): + """ + Custom logging handler. It emits Qt signals so it can be plugged to a gui. + Also stores an history of logs that can be fetched after connect to a gui. + """ + # All dicts returned are of the form + # {'record': LogRecord, 'message': str} + new_log = QtCore.Signal(dict) + + MESSAGE_KEY = 'message' + RECORD_KEY = 'record' + + def __init__(self): + logging.Handler.__init__(self) + QtCore.QObject.__init__(self) + + self._log_history = [] + + def _set_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 = "<span style='" + html_style[logging_level] + "'>" + style_close = "</span>" + 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) + self.setFormatter(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. + QObject.emit gets in the way on the PySide signal model but we + workarouded that issue. + + :param logRecord: the record emitted by the logging module. + :type logRecord: logging.LogRecord. + """ + self._set_format(logRecord.levelname) + log = self.format(logRecord) + log_item = {self.RECORD_KEY: logRecord, self.MESSAGE_KEY: log} + self._log_history.append(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._log_history |