##// END OF EJS Templates
fix: lfs chunked uploads....
fix: lfs chunked uploads. When testing large file uploads it's found that gunicorn raises NoMoreData instead of returning value. This fixes the problem and doesn't show excesive exceptions for no reason. Previously file upload still worked but spawned errors in logs

File last commit:

r1166:020860b6 default
r1280:b2259b07 default
Show More
exc_tracking.py
273 lines | 7.8 KiB | text/x-python | PythonLexer
exceptions: allow error tracking and exception storage on vcsserver.
r491 # RhodeCode VCSServer provides access to different vcs backends via network.
source-code: updated copyrights to 2023
r1126 # Copyright (C) 2014-2023 RhodeCode GmbH
exceptions: allow error tracking and exception storage on vcsserver.
r491 #
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
exc-tracking: synced with ce
r1146 import io
exceptions: allow error tracking and exception storage on vcsserver.
r491 import os
import time
exc-tracking: use more rich style tracebacks.
r1144 import sys
exceptions: allow error tracking and exception storage on vcsserver.
r491 import datetime
import msgpack
import logging
import traceback
import tempfile
exc-tracking: use more rich style tracebacks.
r1144 import glob
exceptions: allow error tracking and exception storage on vcsserver.
r491
log = logging.getLogger(__name__)
# NOTE: Any changes should be synced with exc_tracking at rhodecode.lib.exc_tracking
global_prefix = 'vcsserver'
exc_store: allow to specify a custom path for exception store.
r519 exc_store_dir_name = 'rc_exception_store_v1'
exceptions: allow error tracking and exception storage on vcsserver.
r491
exc-tracking: use more rich style tracebacks.
r1144 def exc_serialize(exc_id, tb, exc_type, extra_data=None):
exceptions: allow error tracking and exception storage on vcsserver.
r491 data = {
exc-tracking: synced with ce
r1146 "version": "v1",
"exc_id": exc_id,
"exc_utc_date": datetime.datetime.utcnow().isoformat(),
"exc_timestamp": repr(time.time()),
"exc_message": tb,
"exc_type": exc_type,
exceptions: allow error tracking and exception storage on vcsserver.
r491 }
exc-tracking: use more rich style tracebacks.
r1144 if extra_data:
data.update(extra_data)
exceptions: allow error tracking and exception storage on vcsserver.
r491 return msgpack.packb(data), data
def exc_unserialize(tb):
return msgpack.unpackb(tb)
exc-tracking: use more rich style tracebacks.
r1144 _exc_store = None
exceptions: allow error tracking and exception storage on vcsserver.
r491 def get_exc_store():
"""
Get and create exception store if it's not existing
"""
exc-tracking: use more rich style tracebacks.
r1144 global _exc_store
if _exc_store is not None:
# quick global cache
return _exc_store
exc_store: allow to specify a custom path for exception store.
r519 import vcsserver as app
exceptions: allow error tracking and exception storage on vcsserver.
r491
exc-tracking: synced with ce
r1146 exc_store_dir = (
app.CONFIG.get("exception_tracker.store_path", "") or tempfile.gettempdir()
)
exc_store: allow to specify a custom path for exception store.
r519 _exc_store_path = os.path.join(exc_store_dir, exc_store_dir_name)
exceptions: allow error tracking and exception storage on vcsserver.
r491
_exc_store_path = os.path.abspath(_exc_store_path)
if not os.path.isdir(_exc_store_path):
os.makedirs(_exc_store_path)
exc-tracking: synced with ce
r1146 log.debug("Initializing exceptions store at %s", _exc_store_path)
exc-tracking: use more rich style tracebacks.
r1144 _exc_store = _exc_store_path
exceptions: allow error tracking and exception storage on vcsserver.
r491 return _exc_store_path
exc-tracking: use more rich style tracebacks.
r1144 def get_detailed_tb(exc_info):
try:
exc-tracking: synced with ce
r1146 from pip._vendor.rich import (
traceback as rich_tb,
scope as rich_scope,
console as rich_console,
)
exc-tracking: use more rich style tracebacks.
r1144 except ImportError:
try:
exc-tracking: synced with ce
r1146 from rich import (
traceback as rich_tb,
scope as rich_scope,
console as rich_console,
)
exc-tracking: use more rich style tracebacks.
r1144 except ImportError:
return None
exc-tracking: synced with ce
r1146 console = rich_console.Console(width=160, file=io.StringIO())
exc-tracking: use more rich style tracebacks.
r1144
exc = rich_tb.Traceback.extract(*exc_info, show_locals=True)
tb_rich = rich_tb.Traceback(
trace=exc,
width=160,
extra_lines=3,
theme=None,
word_wrap=False,
show_locals=False,
exc-tracking: synced with ce
r1146 max_frames=100,
exc-tracking: use more rich style tracebacks.
r1144 )
# last_stack = exc.stacks[-1]
# last_frame = last_stack.frames[-1]
# if last_frame and last_frame.locals:
# console.print(
# rich_scope.render_scope(
# last_frame.locals,
# title=f'{last_frame.filename}:{last_frame.lineno}'))
console.print(tb_rich)
formatted_locals = console.file.getvalue()
return formatted_locals
exceptions: allow error tracking and exception storage on vcsserver.
r491
exc-tracking: use more rich style tracebacks.
r1144 def get_request_metadata(request=None) -> dict:
request_metadata = {}
if not request:
from pyramid.threadlocal import get_current_request
exc-tracking: synced with ce
r1146
exc-tracking: use more rich style tracebacks.
r1144 request = get_current_request()
dan
exc-store: add extra details for stored exceptions for vcsserver
r661
exc-tracking: use more rich style tracebacks.
r1144 # NOTE(marcink): store request information into exc_data
if request:
exc-tracking: synced with ce
r1146 request_metadata["client_address"] = getattr(request, "client_addr", "")
request_metadata["user_agent"] = getattr(request, "user_agent", "")
request_metadata["method"] = getattr(request, "method", "")
request_metadata["url"] = getattr(request, "url", "")
exc-tracking: use more rich style tracebacks.
r1144 return request_metadata
exc-tracking: synced with CE code
r1166 def format_exc(exc_info, use_detailed_tb=True):
exc-tracking: use more rich style tracebacks.
r1144 exc_type, exc_value, exc_traceback = exc_info
tb = "++ TRACEBACK ++\n\n"
tb += "".join(traceback.format_exception(exc_type, exc_value, exc_traceback, None))
exc-tracking: synced with CE code
r1166
exc-tracking: use more rich style tracebacks.
r1144 detailed_tb = getattr(exc_value, "_org_exc_tb", None)
exc-tracking: synced with CE code
r1166
dan
exc-store: add extra details for stored exceptions for vcsserver
r661 if detailed_tb:
ruff: code-cleanups
r1100 remote_tb = detailed_tb
py3: remove compat module usage
r1038 if isinstance(detailed_tb, str):
dan
exc-store: add extra details for stored exceptions for vcsserver
r661 remote_tb = [detailed_tb]
exc-tracking: synced with CE code
r1166
dan
exc-store: add extra details for stored exceptions for vcsserver
r661 tb += (
exc-tracking: use more rich style tracebacks.
r1144 "\n+++ BEG SOURCE EXCEPTION +++\n\n"
"{}\n"
"+++ END SOURCE EXCEPTION +++\n"
"".format("\n".join(remote_tb))
dan
exc-store: add extra details for stored exceptions for vcsserver
r661 )
exc-tracking: synced with CE code
r1166
dan
exc-store: add extra details for stored exceptions for vcsserver
r661 # Avoid that remote_tb also appears in the frame
del remote_tb
exc-tracking: synced with CE code
r1166
if use_detailed_tb:
locals_tb = get_detailed_tb(exc_info)
if locals_tb:
tb += f"\n+++ DETAILS +++\n\n{locals_tb}\n" ""
exc-tracking: use more rich style tracebacks.
r1144 return tb
def _store_exception(exc_id, exc_info, prefix, request_path=''):
"""
Low level function to store exception in the exception tracker
"""
extra_data = {}
extra_data.update(get_request_metadata())
exc_type, exc_value, exc_traceback = exc_info
tb = format_exc(exc_info)
dan
exc-store: add extra details for stored exceptions for vcsserver
r661
exceptions: allow error tracking and exception storage on vcsserver.
r491 exc_type_name = exc_type.__name__
exc-tracking: use more rich style tracebacks.
r1144 exc_data, org_data = exc_serialize(exc_id, tb, exc_type_name, extra_data=extra_data)
exc-tracking: synced with ce
r1146 exc_pref_id = f"{exc_id}_{prefix}_{org_data['exc_timestamp']}"
exceptions: allow error tracking and exception storage on vcsserver.
r491 exc_store_path = get_exc_store()
if not os.path.isdir(exc_store_path):
os.makedirs(exc_store_path)
stored_exc_path = os.path.join(exc_store_path, exc_pref_id)
exc-tracking: synced with ce
r1146 with open(stored_exc_path, "wb") as f:
exceptions: allow error tracking and exception storage on vcsserver.
r491 f.write(exc_data)
exc-tracking: synced with ce
r1146 log.debug("Stored generated exception %s as: %s", exc_id, stored_exc_path)
exceptions: allow error tracking and exception storage on vcsserver.
r491
exc-tracking: synced with ce
r1146 if request_path:
log.error(
'error occurred handling this request.\n'
'Path: `%s`, %s',
request_path, tb)
exceptions: allow error tracking and exception storage on vcsserver.
r491
vcsserver: log exceptions into the logs
r939
def store_exception(exc_id, exc_info, prefix=global_prefix, request_path=''):
exc_store: allow to specify a custom path for exception store.
r519 """
Example usage::
exc_info = sys.exc_info()
store_exception(id(exc_info), exc_info)
"""
exceptions: allow error tracking and exception storage on vcsserver.
r491 try:
exc-tracking: use more rich style tracebacks.
r1144 exc_type = exc_info[0]
exc_type_name = exc_type.__name__
exc-tracking: synced with ce
r1146 _store_exception(
exc_id=exc_id, exc_info=exc_info, prefix=prefix, request_path=request_path,
)
exc-tracking: use more rich style tracebacks.
r1144 return exc_id, exc_type_name
exceptions: allow error tracking and exception storage on vcsserver.
r491 except Exception:
exc-tracking: synced with ce
r1146 log.exception("Failed to store exception `%s` information", exc_id)
exceptions: allow error tracking and exception storage on vcsserver.
r491 # there's no way this can fail, it will crash server badly if it does.
pass
def _find_exc_file(exc_id, prefix=global_prefix):
exc_store_path = get_exc_store()
if prefix:
exc-tracking: synced with ce
r1146 exc_id = f"{exc_id}_{prefix}"
exceptions: allow error tracking and exception storage on vcsserver.
r491 else:
# search without a prefix
exc-tracking: synced with ce
r1146 exc_id = f"{exc_id}"
exceptions: allow error tracking and exception storage on vcsserver.
r491
exc-tracking: use more rich style tracebacks.
r1144 found_exc_id = None
exc-tracking: synced with ce
r1146 matches = glob.glob(os.path.join(exc_store_path, exc_id) + "*")
exc-tracking: use more rich style tracebacks.
r1144 if matches:
found_exc_id = matches[0]
exceptions: allow error tracking and exception storage on vcsserver.
r491
exc-tracking: use more rich style tracebacks.
r1144 return found_exc_id
exceptions: allow error tracking and exception storage on vcsserver.
r491
def _read_exception(exc_id, prefix):
exc_id_file_path = _find_exc_file(exc_id=exc_id, prefix=prefix)
if exc_id_file_path:
exc-tracking: synced with ce
r1146 with open(exc_id_file_path, "rb") as f:
exceptions: allow error tracking and exception storage on vcsserver.
r491 return exc_unserialize(f.read())
else:
exc-tracking: synced with ce
r1146 log.debug("Exception File `%s` not found", exc_id_file_path)
exceptions: allow error tracking and exception storage on vcsserver.
r491 return None
def read_exception(exc_id, prefix=global_prefix):
try:
return _read_exception(exc_id=exc_id, prefix=prefix)
except Exception:
exc-tracking: synced with ce
r1146 log.exception("Failed to read exception `%s` information", exc_id)
exceptions: allow error tracking and exception storage on vcsserver.
r491 # there's no way this can fail, it will crash server badly if it does.
return None
def delete_exception(exc_id, prefix=global_prefix):
try:
exc_id_file_path = _find_exc_file(exc_id, prefix=prefix)
if exc_id_file_path:
os.remove(exc_id_file_path)
except Exception:
exc-tracking: synced with ce
r1146 log.exception("Failed to remove exception `%s` information", exc_id)
exceptions: allow error tracking and exception storage on vcsserver.
r491 # there's no way this can fail, it will crash server badly if it does.
pass
exc-tracking: use more rich style tracebacks.
r1144
def generate_id():
return id(object())