diff --git a/doc/BUGS b/doc/BUGS
index 8de1c58d3..5725f5e0b 100644
--- a/doc/BUGS
+++ b/doc/BUGS
@@ -1,6 +1,8 @@
Bugs
====
+- download-page on qute:htmllog is broken
+
- Downloads: "connection closed" if user takes too long
- Download focus rectangle on windows
diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py
index 8a4547b9b..e8b669452 100644
--- a/qutebrowser/browser/downloads.py
+++ b/qutebrowser/browser/downloads.py
@@ -208,6 +208,7 @@ class DownloadItem(QObject):
filename: The full filename to save the download to.
None: special value to stop the download.
"""
+ logger.debug("Setting filename to {}".format(filename))
if self.filename is not None:
raise ValueError("Filename was already set! filename: {}, "
"existing: {}".format(filename, self.filename))
diff --git a/qutebrowser/network/qutescheme.py b/qutebrowser/network/qutescheme.py
index 7e6cd86a6..a3bc688f9 100644
--- a/qutebrowser/network/qutescheme.py
+++ b/qutebrowser/network/qutescheme.py
@@ -43,6 +43,7 @@ _HTML_TEMPLATE = """
{title}
+ {head}
{body}
@@ -54,18 +55,22 @@ _HTML_TEMPLATE = """
pyeval_output = ":pyeval was never called"
-def _get_html(title, snippet):
+def _get_html(title, snippet, head=None):
"""Add HTML boilerplate to a html snippet.
Args:
title: The title the page should have.
snippet: The html snippet.
+ head: Additional stuff to put in
Return:
HTML content as bytes.
"""
- return _HTML_TEMPLATE.format(title=title, body=snippet).encode(
+ if head is None:
+ head = ""
+ html = _HTML_TEMPLATE.format(title=title, body=snippet, head=head).encode(
'UTF-8', errors='xmlcharrefreplace')
+ return html
class QuteSchemeHandler(SchemeHandler):
@@ -122,7 +127,7 @@ class QuteHandlers:
return _get_html('Version', html)
@classmethod
- def log(cls):
+ def plainlog(cls):
"""Handler for qute:log. Return HTML content as bytes."""
if logutils.ram_handler is None:
text = "Log output was disabled."
@@ -130,6 +135,34 @@ class QuteHandlers:
text = cgi.escape(logutils.ram_handler.dump_log())
return _get_html('log', '{}
'.format(text))
+ @classmethod
+ def log(cls):
+ """Handler for qute:log. Return HTML content as bytes."""
+ style = """
+
+ """
+ if logutils.ram_handler is None:
+ html = "Log output was disabled.
"
+ else:
+ html = logutils.ram_handler.dump_log(html=True)
+ return _get_html('log', html, head=style)
+
@classmethod
def gpl(cls):
"""Handler for qute:gpl. Return HTML content as bytes."""
diff --git a/qutebrowser/utils/log.py b/qutebrowser/utils/log.py
index 6bbf943ba..41738397c 100644
--- a/qutebrowser/utils/log.py
+++ b/qutebrowser/utils/log.py
@@ -22,6 +22,7 @@
import re
import os
import sys
+import cgi
import logging
from logging import getLogger
from collections import deque
@@ -45,11 +46,23 @@ SIMPLE_FMT = '{levelname}: {message}'
EXTENDED_FMT = ('{asctime:8} {levelname:8} {name:10} {module}:{funcName}:'
'{lineno} {message}')
SIMPLE_FMT_COLORED = '%(log_color)s%(levelname)s%(reset)s: %(message)s'
-EXTENDED_FMT_COLORED = ('%(green)s%(asctime)-8s%(reset)s %(log_color)'
- 's%(levelname)-8s%(reset)s %(yellow)s%(name)-10s '
+EXTENDED_FMT_COLORED = ('%(green)s%(asctime)-8s%(reset)s %(log_color)s'
+ '%(levelname)-8s%(reset)s %(yellow)s%(name)-10s '
'%(module)s:%(funcName)s:%(lineno)s%(reset)s '
'%(message)s')
+EXTENDED_FMT_HTML = ('%(green)s%(asctime)-8s%(reset)s | '
+ '%(log_color)s%(levelname)-8s%(reset)s '
+ ' | %(yellow)s%(name)-10s | '
+ '%(module)s:%(funcName)s:%(lineno)s%(reset)s'
+ ' | %(message)s |
')
DATEFMT = '%H:%M:%S'
+LOG_COLORS = {
+ 'DEBUG': 'cyan',
+ 'INFO': 'green',
+ 'WARNING': 'yellow',
+ 'ERROR': 'red',
+ 'CRITICAL': 'red',
+}
# The different loggers used.
@@ -126,7 +139,7 @@ def _init_handlers(level, color, ram_capacity):
color: Whether to use color if available.
"""
global ram_handler
- console_formatter, ram_formatter, use_colorama = _init_formatters(
+ console_fmt, ram_fmt, html_fmt, use_colorama = _init_formatters(
level, color)
if sys.stderr is None:
@@ -138,14 +151,15 @@ def _init_handlers(level, color, ram_capacity):
stream = sys.stderr
console_handler = logging.StreamHandler(stream)
console_handler.setLevel(level)
- console_handler.setFormatter(console_formatter)
+ console_handler.setFormatter(console_fmt)
if ram_capacity == 0:
ram_handler = None
else:
ram_handler = RAMHandler(capacity=ram_capacity)
ram_handler.setLevel(logging.NOTSET)
- ram_handler.setFormatter(ram_formatter)
+ ram_handler.setFormatter(ram_fmt)
+ ram_handler.html_formatter = html_fmt
return console_handler, ram_handler
@@ -169,26 +183,21 @@ def _init_formatters(level, color):
console_fmt = SIMPLE_FMT
console_fmt_colored = SIMPLE_FMT_COLORED
ram_formatter = logging.Formatter(EXTENDED_FMT, DATEFMT, '{')
+ html_formatter = HTMLFormatter(EXTENDED_FMT_HTML, DATEFMT,
+ log_colors=LOG_COLORS)
if sys.stderr is None:
- return None, ram_formatter, False
+ return None, ram_formatter, html_formatter, False
use_colorama = False
if (ColoredFormatter is not None and (os.name == 'posix' or colorama) and
sys.stderr.isatty() and color):
- console_formatter = ColoredFormatter(
- console_fmt_colored, DATEFMT, log_colors={
- 'DEBUG': 'cyan',
- 'INFO': 'green',
- 'WARNING': 'yellow',
- 'ERROR': 'red',
- 'CRITICAL': 'red',
- }
- )
+ console_formatter = ColoredFormatter(console_fmt_colored, DATEFMT,
+ log_colors=LOG_COLORS)
if colorama:
colorama.init()
use_colorama = True
else:
console_formatter = logging.Formatter(console_fmt, DATEFMT, '{')
- return console_formatter, ram_formatter, use_colorama
+ return console_formatter, ram_formatter, html_formatter, use_colorama
def qt_message_handler(msg_type, context, msg):
@@ -304,6 +313,7 @@ class RAMHandler(logging.Handler):
def __init__(self, capacity):
super().__init__()
+ self.html_formatter = None
if capacity != -1:
self.data = deque(maxlen=capacity)
else:
@@ -312,9 +322,55 @@ class RAMHandler(logging.Handler):
def emit(self, record):
self.data.append(record)
- def dump_log(self):
+ def dump_log(self, html=False):
"""Dump the complete formatted log data as as string."""
- lines = []
+ if html:
+ fmt = self.html_formatter.format
+ lines = ['']
+ else:
+ fmt = self.format
+ lines = []
for record in self.data:
- lines.append(self.format(record))
+ lines.append(fmt(record))
+ if html:
+ lines.append('
')
return '\n'.join(lines)
+
+
+class HTMLFormatter(logging.Formatter):
+
+ """Formatter for HTML-colored log messages, similiar to colorlog."""
+
+ def __init__(self, fmt, datefmt, log_colors):
+ """Constructor.
+
+ Args:
+ fmt: The format string to use.
+ datefmt: The date format to use.
+ log_colors: The colors to use for logging levels.
+ """
+ super().__init__(fmt, datefmt)
+ self._log_colors = log_colors
+ self._colordict = {}
+ # We could solve this nicer by using CSS, but for this simple case this
+ # works.
+ for color in ['black', 'red', 'green', 'yellow', 'blue', 'purple',
+ 'cyan', 'white']:
+ self._colordict[color] = ''.format(color)
+ self._colordict['reset'] = ''
+
+ def format(self, record):
+ record.__dict__.update(self._colordict)
+ if record.levelname in self._log_colors:
+ color = self._log_colors[record.levelname]
+ record.log_color = self._colordict[color]
+ else:
+ record.log_color = ''
+ for field in ['asctime', 'filename', 'funcName', 'levelname',
+ 'module', 'message', 'name', 'pathname', 'processName',
+ 'threadName']:
+ setattr(record, field, cgi.escape(getattr(record, field)))
+ message = super().format(record)
+ if not message.endswith(self._colordict['reset']):
+ message += self._colordict['reset']
+ return message