##// END OF EJS Templates
Aligned expanded sumarry box content better and moved comments as last column
Aligned expanded sumarry box content better and moved comments as last column

File last commit:

r3363:f08e98b1 default
r3639:7ee2e326 new-ui
Show More
exc_tracking.py
166 lines | 5.0 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
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)
def get_exc_store():
"""
Get and create exception store if it's not existing
"""
exc_tracker: allow setting custom store via .ini file...
r3019 import rhodecode as app
exceptions: added new exception tracking capability....
r2907
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)
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)
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)
# we need to search the store for such start pattern as above
for fname in os.listdir(exc_store_path):
if fname.startswith(exc_id):
exc_id = os.path.join(exc_store_path, fname)
break
continue
else:
exc_id = None
return exc_id
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())