vcs-server: expose remote tracebacks from http backend using the Pyro4AwareFormatter.
This commit is contained in:
parent
4a415909c6
commit
90deb7d32c
6 changed files with 49 additions and 23 deletions
|
|
@ -18,6 +18,7 @@
|
|||
# RhodeCode Enterprise Edition, including its added features, Support services,
|
||||
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
|
||||
|
|
@ -73,16 +74,35 @@ class Pyro4AwareFormatter(logging.Formatter):
|
|||
|
||||
def formatException(self, ei):
|
||||
ex_type, ex_value, ex_tb = ei
|
||||
if hasattr(ex_value, '_pyroTraceback'):
|
||||
# johbo: Avoiding to import pyro4 until we get an exception
|
||||
# which actually has a remote traceback. This avoids issues
|
||||
# when gunicorn is used with gevent, since the logging would
|
||||
# trigger an import of Pyro4 before the patches of gevent
|
||||
# are applied.
|
||||
import Pyro4.util
|
||||
return ''.join(
|
||||
Pyro4.util.getPyroTraceback(ex_type, ex_value, ex_tb))
|
||||
return logging.Formatter.formatException(self, ei)
|
||||
|
||||
local_tb = logging.Formatter.formatException(self, ei)
|
||||
if hasattr(ex_value, '_vcs_server_traceback'):
|
||||
|
||||
def formatRemoteTraceback(remote_tb_lines):
|
||||
result = ["\n +--- This exception occured remotely on VCSServer - Remote traceback:\n\n"]
|
||||
result.append(remote_tb_lines)
|
||||
result.append("\n +--- End of remote traceback\n")
|
||||
return result
|
||||
|
||||
try:
|
||||
if ex_type is not None and ex_value is None and ex_tb is None:
|
||||
# possible old (3.x) call syntax where caller is only providing exception object
|
||||
if type(ex_type) is not type:
|
||||
raise TypeError(
|
||||
"invalid argument: ex_type should be an exception type, or just supply no arguments at all")
|
||||
if ex_type is None and ex_tb is None:
|
||||
ex_type, ex_value, ex_tb = sys.exc_info()
|
||||
|
||||
remote_tb = getattr(ex_value, "_vcs_server_traceback", None)
|
||||
|
||||
if remote_tb:
|
||||
remote_tb = formatRemoteTraceback(remote_tb)
|
||||
return local_tb + ''.join(remote_tb)
|
||||
finally:
|
||||
# clean up cycle to traceback, to allow proper GC
|
||||
del ex_type, ex_value, ex_tb
|
||||
|
||||
return local_tb
|
||||
|
||||
|
||||
class ColorFormatter(Pyro4AwareFormatter):
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ def wrap_in_appenlight_if_enabled(app, settings, appenlight_client=None):
|
|||
|
||||
class RemoteTracebackTracker(object):
|
||||
"""
|
||||
Utility middleware which forwards Pyro4 remote traceback information.
|
||||
Utility middleware which forwards VCSServer remote traceback information.
|
||||
"""
|
||||
|
||||
def __init__(self, app):
|
||||
|
|
@ -85,7 +85,7 @@ class RemoteTracebackTracker(object):
|
|||
try:
|
||||
return self.application(environ, start_response)
|
||||
except Exception as e:
|
||||
if hasattr(e, '_pyroTraceback'):
|
||||
if hasattr(e, '_vcs_server_traceback'):
|
||||
track_extra_information(
|
||||
environ, 'remote_traceback', ''.join(e._pyroTraceback))
|
||||
environ, 'remote_traceback', e._vcs_server_traceback)
|
||||
raise
|
||||
|
|
|
|||
|
|
@ -218,6 +218,12 @@ def _remote_call(url, payload, exceptions_map, session):
|
|||
exc._vcs_kind = error['_vcs_kind']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
exc._vcs_server_traceback = error['traceback']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
raise exc
|
||||
return response.get('result')
|
||||
|
||||
|
|
|
|||
|
|
@ -185,10 +185,10 @@ def map_vcs_exceptions(func):
|
|||
# The error middleware adds information if it finds
|
||||
# __traceback_info__ in a frame object. This way the remote
|
||||
# traceback information is made available in error reports.
|
||||
remote_tb = getattr(e, '_pyroTraceback', None)
|
||||
remote_tb = getattr(e, '_vcs_server_traceback', None)
|
||||
if remote_tb:
|
||||
__traceback_info__ = (
|
||||
'Found Pyro4 remote traceback information:\n\n' +
|
||||
'Found VCSServer remote traceback information:\n\n' +
|
||||
'\n'.join(remote_tb))
|
||||
|
||||
# Avoid that remote_tb also appears in the frame
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ function registerRCRoutes() {
|
|||
pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
|
||||
pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
|
||||
pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
|
||||
pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
|
||||
pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
|
||||
pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
|
||||
pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
|
||||
|
|
|
|||
|
|
@ -1331,22 +1331,21 @@ def pytest_runtest_makereport(item, call):
|
|||
"""
|
||||
Adding the remote traceback if the exception has this information.
|
||||
|
||||
Pyro4 attaches this information as the attribute `_pyroTraceback`
|
||||
Pyro4 attaches this information as the attribute `_vcs_server_traceback`
|
||||
to the exception instance.
|
||||
"""
|
||||
outcome = yield
|
||||
report = outcome.get_result()
|
||||
if call.excinfo:
|
||||
_add_pyro_remote_traceback(report, call.excinfo.value)
|
||||
_add_vcsserver_remote_traceback(report, call.excinfo.value)
|
||||
|
||||
|
||||
def _add_pyro_remote_traceback(report, exc):
|
||||
pyro_traceback = getattr(exc, '_pyroTraceback', None)
|
||||
def _add_vcsserver_remote_traceback(report, exc):
|
||||
vcsserver_traceback = getattr(exc, '_vcs_server_traceback', None)
|
||||
|
||||
if pyro_traceback:
|
||||
traceback = ''.join(pyro_traceback)
|
||||
section = 'Pyro4 remote traceback ' + report.when
|
||||
report.sections.append((section, traceback))
|
||||
if vcsserver_traceback:
|
||||
section = 'VCSServer remote traceback ' + report.when
|
||||
report.sections.append((section, vcsserver_traceback))
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue