Add :messages command to show past messages.

This adds a 'level' query parameter to qute://log and qute://plainlog.
For example, qute://log?level=warning will show an html page containing
log entries with severity warning or greater.
If the query is omitted, the original behavior of qute://log is
preserved.

:messages [level] is a command that opens qute://log?level=<level>.
By default, level defaults to 'error' as an easy way to see missed
error messages.
This commit is contained in:
Ryan Roden-Corrent 2016-05-11 21:23:38 -04:00
parent 42460c3c04
commit 800c1c3cf8
4 changed files with 74 additions and 8 deletions

View File

@ -1339,6 +1339,22 @@ class CommandDispatcher:
url = QUrl('qute://help/{}'.format(path)) url = QUrl('qute://help/{}'.format(path))
self._open(url, tab, bg, window) self._open(url, tab, bg, window)
@cmdutils.register(instance='command-dispatcher', scope='window')
def messages(self, level='error', plain=False, tab=False, bg=False,
window=False):
"""Show a log of past messages.
Args:
level: Include messages with `level` or higher severity.
Valid values: vdebug, debug, info, warning, error, critical.
plain: Whether to show plaintext (as opposed to html).
"""
if plain:
url = QUrl('qute://plainlog?level={}'.format(level))
else:
url = QUrl('qute://log?level={}'.format(level))
self._open(url, tab, bg, window)
@cmdutils.register(instance='command-dispatcher', @cmdutils.register(instance='command-dispatcher',
modes=[KeyMode.insert], hide=True, scope='window') modes=[KeyMode.insert], hide=True, scope='window')
def open_editor(self): def open_editor(self):

View File

@ -27,7 +27,7 @@ import functools
import configparser import configparser
import mimetypes import mimetypes
from PyQt5.QtCore import pyqtSlot, QObject from PyQt5.QtCore import pyqtSlot, QObject, QUrlQuery
from PyQt5.QtNetwork import QNetworkReply from PyQt5.QtNetwork import QNetworkReply
import qutebrowser import qutebrowser
@ -159,22 +159,37 @@ def qute_version(_win_id, _request):
@add_handler('plainlog') @add_handler('plainlog')
def qute_plainlog(_win_id, _request): def qute_plainlog(_win_id, _request):
"""Handler for qute:plainlog. Return HTML content as bytes.""" """Handler for qute:plainlog. Return HTML content as bytes.
An optional query parameter specifies the min log level to print.
For example, qute://log?level=warning prints warnings and errors.
Level can be one of: vdebug, debug, info, warning, error, critical.
"""
if log.ram_handler is None: if log.ram_handler is None:
text = "Log output was disabled." text = "Log output was disabled."
else: else:
text = log.ram_handler.dump_log() query = QUrlQuery(_request.url().query())
level = query.queryItemValue('level')
text = log.ram_handler.dump_log(html=False, level=level)
html = jinja.render('pre.html', title='log', content=text) html = jinja.render('pre.html', title='log', content=text)
return html.encode('UTF-8', errors='xmlcharrefreplace') return html.encode('UTF-8', errors='xmlcharrefreplace')
@add_handler('log') @add_handler('log')
def qute_log(_win_id, _request): def qute_log(_win_id, _request):
"""Handler for qute:log. Return HTML content as bytes.""" """Handler for qute:log. Return HTML content as bytes.
An optional query parameter specifies the min log level to print.
For example, qute://log?level=warning prints warnings and errors.
Level can be one of: vdebug, debug, info, warning, error, critical.
"""
if log.ram_handler is None: if log.ram_handler is None:
html_log = None html_log = None
else: else:
html_log = log.ram_handler.dump_log(html=True) query = QUrlQuery(_request.url().query())
level = query.queryItemValue('level')
html_log = log.ram_handler.dump_log(html=True, level=level)
html = jinja.render('log.html', title='log', content=html_log) html = jinja.render('log.html', title='log', content=html_log)
return html.encode('UTF-8', errors='xmlcharrefreplace') return html.encode('UTF-8', errors='xmlcharrefreplace')

View File

@ -429,13 +429,15 @@ class RAMHandler(logging.Handler):
# We don't log VDEBUG to RAM. # We don't log VDEBUG to RAM.
self._data.append(record) self._data.append(record)
def dump_log(self, html=False): def dump_log(self, html=False, level='vdebug'):
"""Dump the complete formatted log data as as string. """Dump the complete formatted log data as as string.
FIXME: We should do all the HTML formatter via jinja2. FIXME: We should do all the HTML formatter via jinja2.
(probably obsolete when moving to a widget for logging, (probably obsolete when moving to a widget for logging,
https://github.com/The-Compiler/qutebrowser/issues/34 https://github.com/The-Compiler/qutebrowser/issues/34
""" """
# pylint: disable=protected-access
minlevel = logging._nameToLevel.get(level.upper(), VDEBUG_LEVEL)
lines = [] lines = []
fmt = self.html_formatter.format if html else self.format fmt = self.html_formatter.format if html else self.format
self.acquire() self.acquire()
@ -444,7 +446,8 @@ class RAMHandler(logging.Handler):
finally: finally:
self.release() self.release()
for record in records: for record in records:
lines.append(fmt(record)) if record.levelno >= minlevel:
lines.append(fmt(record))
return '\n'.join(lines) return '\n'.join(lines)

View File

@ -342,7 +342,7 @@ Feature: Various utility commands.
Scenario: Running :pyeval with --quiet Scenario: Running :pyeval with --quiet
When I run :debug-pyeval --quiet 1+1 When I run :debug-pyeval --quiet 1+1
Then "pyeval output: 2" should be logged Then "pyeval output: 2" should be logged
## https://github.com/The-Compiler/qutebrowser/issues/504 ## https://github.com/The-Compiler/qutebrowser/issues/504
Scenario: Focusing download widget via Tab Scenario: Focusing download widget via Tab
@ -367,3 +367,35 @@ Feature: Various utility commands.
When I set network -> custom-headers to {"X-Qute-Test": "testvalue"} When I set network -> custom-headers to {"X-Qute-Test": "testvalue"}
And I open headers And I open headers
Then the header X-Qute-Test should be set to testvalue Then the header X-Qute-Test should be set to testvalue
## :messages
Scenario: Showing error messages
When I run :message-error the-error-message
And I run :message-warning the-warning-message
And I run :message-info the-info-message
And I run :messages
Then the error "the-error-message" should be shown
And the warning "the-warning-message" should be shown
And the page should contain the plaintext "the-error-message"
Scenario: Showing messages of type 'warning' or greater
When I run :message-error the-error-message
And I run :message-warning the-warning-message
And I run :message-info the-info-message
And I run :messages warning
Then the error "the-error-message" should be shown
And the warning "the-warning-message" should be shown
And the page should contain the plaintext "the-error-message"
And the page should contain the plaintext "the-warning-message"
Scenario: Showing messages of type 'info' or greater
When I run :message-error the-error-message
And I run :message-warning the-warning-message
And I run :message-info the-info-message
And I run :messages info
Then the error "the-error-message" should be shown
And the warning "the-warning-message" should be shown
And the page should contain the plaintext "the-error-message"
And the page should contain the plaintext "the-warning-message"
And the page should contain the plaintext "the-info-message"