##// END OF EJS Templates
vcs-server: expose remote tracebacks from http backend using the Pyro4AwareFormatter.
marcink -
r1257:edb7f6bf default
parent child Browse files
Show More
@@ -18,6 +18,7 b''
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 import sys
21 22 import logging
22 23
23 24
@@ -73,16 +74,35 b' class Pyro4AwareFormatter(logging.Format'
73 74
74 75 def formatException(self, ei):
75 76 ex_type, ex_value, ex_tb = ei
76 if hasattr(ex_value, '_pyroTraceback'):
77 # johbo: Avoiding to import pyro4 until we get an exception
78 # which actually has a remote traceback. This avoids issues
79 # when gunicorn is used with gevent, since the logging would
80 # trigger an import of Pyro4 before the patches of gevent
81 # are applied.
82 import Pyro4.util
83 return ''.join(
84 Pyro4.util.getPyroTraceback(ex_type, ex_value, ex_tb))
85 return logging.Formatter.formatException(self, ei)
77
78 local_tb = logging.Formatter.formatException(self, ei)
79 if hasattr(ex_value, '_vcs_server_traceback'):
80
81 def formatRemoteTraceback(remote_tb_lines):
82 result = ["\n +--- This exception occured remotely on VCSServer - Remote traceback:\n\n"]
83 result.append(remote_tb_lines)
84 result.append("\n +--- End of remote traceback\n")
85 return result
86
87 try:
88 if ex_type is not None and ex_value is None and ex_tb is None:
89 # possible old (3.x) call syntax where caller is only providing exception object
90 if type(ex_type) is not type:
91 raise TypeError(
92 "invalid argument: ex_type should be an exception type, or just supply no arguments at all")
93 if ex_type is None and ex_tb is None:
94 ex_type, ex_value, ex_tb = sys.exc_info()
95
96 remote_tb = getattr(ex_value, "_vcs_server_traceback", None)
97
98 if remote_tb:
99 remote_tb = formatRemoteTraceback(remote_tb)
100 return local_tb + ''.join(remote_tb)
101 finally:
102 # clean up cycle to traceback, to allow proper GC
103 del ex_type, ex_value, ex_tb
104
105 return local_tb
86 106
87 107
88 108 class ColorFormatter(Pyro4AwareFormatter):
@@ -75,7 +75,7 b' def wrap_in_appenlight_if_enabled(app, s'
75 75
76 76 class RemoteTracebackTracker(object):
77 77 """
78 Utility middleware which forwards Pyro4 remote traceback information.
78 Utility middleware which forwards VCSServer remote traceback information.
79 79 """
80 80
81 81 def __init__(self, app):
@@ -85,7 +85,7 b' class RemoteTracebackTracker(object):'
85 85 try:
86 86 return self.application(environ, start_response)
87 87 except Exception as e:
88 if hasattr(e, '_pyroTraceback'):
88 if hasattr(e, '_vcs_server_traceback'):
89 89 track_extra_information(
90 environ, 'remote_traceback', ''.join(e._pyroTraceback))
90 environ, 'remote_traceback', e._vcs_server_traceback)
91 91 raise
@@ -218,6 +218,12 b' def _remote_call(url, payload, exception'
218 218 exc._vcs_kind = error['_vcs_kind']
219 219 except KeyError:
220 220 pass
221
222 try:
223 exc._vcs_server_traceback = error['traceback']
224 except KeyError:
225 pass
226
221 227 raise exc
222 228 return response.get('result')
223 229
@@ -185,10 +185,10 b' def map_vcs_exceptions(func):'
185 185 # The error middleware adds information if it finds
186 186 # __traceback_info__ in a frame object. This way the remote
187 187 # traceback information is made available in error reports.
188 remote_tb = getattr(e, '_pyroTraceback', None)
188 remote_tb = getattr(e, '_vcs_server_traceback', None)
189 189 if remote_tb:
190 190 __traceback_info__ = (
191 'Found Pyro4 remote traceback information:\n\n' +
191 'Found VCSServer remote traceback information:\n\n' +
192 192 '\n'.join(remote_tb))
193 193
194 194 # Avoid that remote_tb also appears in the frame
@@ -46,6 +46,7 b' function registerRCRoutes() {'
46 46 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
47 47 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
48 48 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
49 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
49 50 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
50 51 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
51 52 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 b' def pytest_runtest_makereport(item, call'
1331 1331 """
1332 1332 Adding the remote traceback if the exception has this information.
1333 1333
1334 Pyro4 attaches this information as the attribute `_pyroTraceback`
1334 Pyro4 attaches this information as the attribute `_vcs_server_traceback`
1335 1335 to the exception instance.
1336 1336 """
1337 1337 outcome = yield
1338 1338 report = outcome.get_result()
1339 1339 if call.excinfo:
1340 _add_pyro_remote_traceback(report, call.excinfo.value)
1340 _add_vcsserver_remote_traceback(report, call.excinfo.value)
1341 1341
1342 1342
1343 def _add_pyro_remote_traceback(report, exc):
1344 pyro_traceback = getattr(exc, '_pyroTraceback', None)
1343 def _add_vcsserver_remote_traceback(report, exc):
1344 vcsserver_traceback = getattr(exc, '_vcs_server_traceback', None)
1345 1345
1346 if pyro_traceback:
1347 traceback = ''.join(pyro_traceback)
1348 section = 'Pyro4 remote traceback ' + report.when
1349 report.sections.append((section, traceback))
1346 if vcsserver_traceback:
1347 section = 'VCSServer remote traceback ' + report.when
1348 report.sections.append((section, vcsserver_traceback))
1350 1349
1351 1350
1352 1351 @pytest.fixture(scope='session')
General Comments 0
You need to be logged in to leave comments. Login now