diff --git a/jupyterhub/app.py b/jupyterhub/app.py index 090668bf..3b05d6aa 100644 --- a/jupyterhub/app.py +++ b/jupyterhub/app.py @@ -26,7 +26,7 @@ import tornado.httpserver import tornado.options from tornado.httpclient import HTTPError from tornado.ioloop import IOLoop, PeriodicCallback -from tornado.log import LogFormatter, app_log, access_log, gen_log +from tornado.log import app_log, access_log, gen_log from tornado import gen, web import IPython @@ -47,6 +47,7 @@ from .handlers.static import CacheControlStaticFilesHandler from . import orm from ._data import DATA_FILES_PATH +from .log import CoroutineLogFormatter from .traitlets import URLPrefix from .utils import ( url_path_join, @@ -350,7 +351,7 @@ class JupyterHub(Application): handlers = List() - _log_formatter_cls = LogFormatter + _log_formatter_cls = CoroutineLogFormatter http_server = None proxy_process = None io_loop = None diff --git a/jupyterhub/log.py b/jupyterhub/log.py new file mode 100644 index 00000000..87625d5e --- /dev/null +++ b/jupyterhub/log.py @@ -0,0 +1,40 @@ +"""logging utilities""" +# Copyright (c) Jupyter Development Team. +# Distributed under the terms of the Modified BSD License. + +import traceback + +from tornado.log import LogFormatter + +def coroutine_traceback(typ, value, tb): + """Scrub coroutine frames from a traceback + + Coroutine tracebacks have a bunch of identical uninformative frames at each yield point. + This removes those extra frames, so tracebacks should be easier to read. + This might be a horrible idea. + + Returns a list of strings (like traceback.format_tb) + """ + all_frames = traceback.extract_tb(tb) + useful_frames = [] + for frame in all_frames: + if frame[0] == '' and frame[2] == 'raise_exc_info': + continue + # start out conservative with filename + function matching + # maybe just filename matching would be sufficient + elif frame[0].endswith('tornado/gen.py') and frame[2] in {'run', 'wrapper'}: + continue + elif frame[0].endswith('tornado/concurrent.py') and frame[2] == 'result': + continue + useful_frames.append(frame) + tb_list = ['Traceback (most recent call last):\n'] + tb_list.extend(traceback.format_list(useful_frames)) + tb_list.extend(traceback.format_exception_only(typ, value)) + return tb_list + + +class CoroutineLogFormatter(LogFormatter): + """Log formatter that scrubs coroutine frames""" + def formatException(self, exc_info): + return ''.join(coroutine_traceback(*exc_info)) +