##// END OF EJS Templates
release: Finish preparation for 4.18.0
release: Finish preparation for 4.18.0

File last commit:

r4112:1f5dcb79 default
r4182:f37a3126 v4.18.0 stable
Show More
exc_tracking.py
173 lines | 5.1 KiB | text/x-python | PythonLexer
exceptions: added new exception tracking capability....
r2907 # -*- coding: utf-8 -*-
docs: updated copyrights to 2019
r3363 # Copyright (C) 2010-2019 RhodeCode GmbH
exceptions: added new exception tracking capability....
r2907 #
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# 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 Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import os
import time
import datetime
import msgpack
import logging
import traceback
import tempfile
cache exc store, and use glob.glob for scaning exc store
r3975 import glob
exceptions: added new exception tracking capability....
r2907
log = logging.getLogger(__name__)
# NOTE: Any changes should be synced with exc_tracking at vcsserver.lib.exc_tracking
global_prefix = 'rhodecode'
exc_tracker: allow setting custom store via .ini file...
r3019 exc_store_dir_name = 'rc_exception_store_v1'
exceptions: added new exception tracking capability....
r2907
def exc_serialize(exc_id, tb, exc_type):
data = {
'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,
}
return msgpack.packb(data), data
def exc_unserialize(tb):
return msgpack.unpackb(tb)
cache exc store, and use glob.glob for scaning exc store
r3975 _exc_store = None
exceptions: added new exception tracking capability....
r2907
def get_exc_store():
"""
Get and create exception store if it's not existing
"""
cache exc store, and use glob.glob for scaning exc store
r3975 global _exc_store
exc_tracker: allow setting custom store via .ini file...
r3019 import rhodecode as app
exceptions: added new exception tracking capability....
r2907
cache exc store, and use glob.glob for scaning exc store
r3975 if _exc_store is not None:
# quick global cache
return _exc_store
exception_store: rename .ini option for future
r3024 exc_store_dir = app.CONFIG.get('exception_tracker.store_path', '') or tempfile.gettempdir()
exc_tracker: allow setting custom store via .ini file...
r3019 _exc_store_path = os.path.join(exc_store_dir, exc_store_dir_name)
exceptions: added new exception tracking capability....
r2907
_exc_store_path = os.path.abspath(_exc_store_path)
if not os.path.isdir(_exc_store_path):
os.makedirs(_exc_store_path)
log.debug('Initializing exceptions store at %s', _exc_store_path)
cache exc store, and use glob.glob for scaning exc store
r3975 _exc_store = _exc_store_path
exceptions: added new exception tracking capability....
r2907 return _exc_store_path
api: added store_exception_api for remote exception storage....
r3317 def _store_exception(exc_id, exc_type_name, exc_traceback, prefix):
"""
Low level function to store exception in the exception tracker
"""
exceptions: added new exception tracking capability....
r2907
exc_store_path = get_exc_store()
api: added store_exception_api for remote exception storage....
r3317 exc_data, org_data = exc_serialize(exc_id, exc_traceback, exc_type_name)
exceptions: added new exception tracking capability....
r2907 exc_pref_id = '{}_{}_{}'.format(exc_id, prefix, org_data['exc_timestamp'])
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)
with open(stored_exc_path, 'wb') as f:
f.write(exc_data)
log.debug('Stored generated exception %s as: %s', exc_id, stored_exc_path)
api: added store_exception_api for remote exception storage....
r3317 def _prepare_exception(exc_info):
exc_type, exc_value, exc_traceback = exc_info
exc_type_name = exc_type.__name__
tb = ''.join(traceback.format_exception(
exc_type, exc_value, exc_traceback, None))
return exc_type_name, tb
exceptions: added new exception tracking capability....
r2907 def store_exception(exc_id, exc_info, prefix=global_prefix):
exception-tracker: store event sending exception for easier event fail debugging.
r3007 """
Example usage::
exc_info = sys.exc_info()
store_exception(id(exc_info), exc_info)
"""
exceptions: added new exception tracking capability....
r2907 try:
api: added store_exception_api for remote exception storage....
r3317 exc_type_name, exc_traceback = _prepare_exception(exc_info)
_store_exception(exc_id=exc_id, exc_type_name=exc_type_name,
exc-tracker: store API based exceptions and fix prefixes to no use _
r3335 exc_traceback=exc_traceback, prefix=prefix)
exception-tracker: send exc id headers on failed API calls for tracking errors that server generated.
r4112 return exc_id, exc_type_name
exceptions: added new exception tracking capability....
r2907 except Exception:
log.exception('Failed to store exception `%s` information', exc_id)
# 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_id = '{}_{}'.format(exc_id, prefix)
else:
# search without a prefix
exc_id = '{}'.format(exc_id)
cache exc store, and use glob.glob for scaning exc store
r3975 found_exc_id = None
matches = glob.glob(os.path.join(exc_store_path, exc_id) + '*')
if matches:
found_exc_id = matches[0]
exceptions: added new exception tracking capability....
r2907
cache exc store, and use glob.glob for scaning exc store
r3975 return found_exc_id
exceptions: added new exception tracking capability....
r2907
def _read_exception(exc_id, prefix):
exc_id_file_path = _find_exc_file(exc_id=exc_id, prefix=prefix)
if exc_id_file_path:
with open(exc_id_file_path, 'rb') as f:
return exc_unserialize(f.read())
else:
log.debug('Exception File `%s` not found', exc_id_file_path)
return None
def read_exception(exc_id, prefix=global_prefix):
try:
return _read_exception(exc_id=exc_id, prefix=prefix)
except Exception:
log.exception('Failed to read exception `%s` information', exc_id)
# 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:
log.exception('Failed to remove exception `%s` information', exc_id)
# there's no way this can fail, it will crash server badly if it does.
pass
api: added store_exception_api for remote exception storage....
r3317
def generate_id():
return id(object())