diff --git a/rhodecode/lib/logging_formatter.py b/rhodecode/lib/logging_formatter.py --- a/rhodecode/lib/logging_formatter.py +++ b/rhodecode/lib/logging_formatter.py @@ -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.Format 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): diff --git a/rhodecode/lib/middleware/appenlight.py b/rhodecode/lib/middleware/appenlight.py --- a/rhodecode/lib/middleware/appenlight.py +++ b/rhodecode/lib/middleware/appenlight.py @@ -75,7 +75,7 @@ def wrap_in_appenlight_if_enabled(app, s 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 diff --git a/rhodecode/lib/vcs/client_http.py b/rhodecode/lib/vcs/client_http.py --- a/rhodecode/lib/vcs/client_http.py +++ b/rhodecode/lib/vcs/client_http.py @@ -218,6 +218,12 @@ def _remote_call(url, payload, exception 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') diff --git a/rhodecode/lib/vcs/exceptions.py b/rhodecode/lib/vcs/exceptions.py --- a/rhodecode/lib/vcs/exceptions.py +++ b/rhodecode/lib/vcs/exceptions.py @@ -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 diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js --- a/rhodecode/public/js/rhodecode/routes.js +++ b/rhodecode/public/js/rhodecode/routes.js @@ -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']); diff --git a/rhodecode/tests/plugin.py b/rhodecode/tests/plugin.py --- a/rhodecode/tests/plugin.py +++ b/rhodecode/tests/plugin.py @@ -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')