##// END OF EJS Templates
exc-store: add extra details for stored exceptions for vcsserver
dan -
r661:b486404e default
parent child Browse files
Show More
@@ -1,151 +1,169 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # RhodeCode VCSServer provides access to different vcs backends via network.
4 4 # Copyright (C) 2014-2019 RhodeCode GmbH
5 5 #
6 6 # This program is free software; you can redistribute it and/or modify
7 7 # it under the terms of the GNU General Public License as published by
8 8 # the Free Software Foundation; either version 3 of the License, or
9 9 # (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software Foundation,
18 18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 19
20 20
21 21 import os
22 22 import time
23 23 import datetime
24 24 import msgpack
25 25 import logging
26 26 import traceback
27 27 import tempfile
28 28
29 from pyramid import compat
29 30
30 31 log = logging.getLogger(__name__)
31 32
32 33 # NOTE: Any changes should be synced with exc_tracking at rhodecode.lib.exc_tracking
33 34 global_prefix = 'vcsserver'
34 35 exc_store_dir_name = 'rc_exception_store_v1'
35 36
36 37
37 38 def exc_serialize(exc_id, tb, exc_type):
38 39
39 40 data = {
40 41 'version': 'v1',
41 42 'exc_id': exc_id,
42 43 'exc_utc_date': datetime.datetime.utcnow().isoformat(),
43 44 'exc_timestamp': repr(time.time()),
44 45 'exc_message': tb,
45 46 'exc_type': exc_type,
46 47 }
47 48 return msgpack.packb(data), data
48 49
49 50
50 51 def exc_unserialize(tb):
51 52 return msgpack.unpackb(tb)
52 53
53 54
54 55 def get_exc_store():
55 56 """
56 57 Get and create exception store if it's not existing
57 58 """
58 59 import vcsserver as app
59 60
60 61 exc_store_dir = app.CONFIG.get('exception_tracker.store_path', '') or tempfile.gettempdir()
61 62 _exc_store_path = os.path.join(exc_store_dir, exc_store_dir_name)
62 63
63 64 _exc_store_path = os.path.abspath(_exc_store_path)
64 65 if not os.path.isdir(_exc_store_path):
65 66 os.makedirs(_exc_store_path)
66 67 log.debug('Initializing exceptions store at %s', _exc_store_path)
67 68 return _exc_store_path
68 69
69 70
70 71 def _store_exception(exc_id, exc_info, prefix):
71 72 exc_type, exc_value, exc_traceback = exc_info
73
72 74 tb = ''.join(traceback.format_exception(
73 75 exc_type, exc_value, exc_traceback, None))
74 76
77 detailed_tb = getattr(exc_value, '_org_exc_tb', None)
78
79 if detailed_tb:
80 if isinstance(detailed_tb, compat.string_types):
81 remote_tb = [detailed_tb]
82
83 tb += (
84 '\n+++ BEG SOURCE EXCEPTION +++\n\n'
85 '{}\n'
86 '+++ END SOURCE EXCEPTION +++\n'
87 ''.format('\n'.join(remote_tb))
88 )
89
90 # Avoid that remote_tb also appears in the frame
91 del remote_tb
92
75 93 exc_type_name = exc_type.__name__
76 94 exc_store_path = get_exc_store()
77 95 exc_data, org_data = exc_serialize(exc_id, tb, exc_type_name)
78 96 exc_pref_id = '{}_{}_{}'.format(exc_id, prefix, org_data['exc_timestamp'])
79 97 if not os.path.isdir(exc_store_path):
80 98 os.makedirs(exc_store_path)
81 99 stored_exc_path = os.path.join(exc_store_path, exc_pref_id)
82 100 with open(stored_exc_path, 'wb') as f:
83 101 f.write(exc_data)
84 102 log.debug('Stored generated exception %s as: %s', exc_id, stored_exc_path)
85 103
86 104
87 105 def store_exception(exc_id, exc_info, prefix=global_prefix):
88 106 """
89 107 Example usage::
90 108
91 109 exc_info = sys.exc_info()
92 110 store_exception(id(exc_info), exc_info)
93 111 """
94 112
95 113 try:
96 114 _store_exception(exc_id=exc_id, exc_info=exc_info, prefix=prefix)
97 115 except Exception:
98 116 log.exception('Failed to store exception `%s` information', exc_id)
99 117 # there's no way this can fail, it will crash server badly if it does.
100 118 pass
101 119
102 120
103 121 def _find_exc_file(exc_id, prefix=global_prefix):
104 122 exc_store_path = get_exc_store()
105 123 if prefix:
106 124 exc_id = '{}_{}'.format(exc_id, prefix)
107 125 else:
108 126 # search without a prefix
109 127 exc_id = '{}'.format(exc_id)
110 128
111 129 # we need to search the store for such start pattern as above
112 130 for fname in os.listdir(exc_store_path):
113 131 if fname.startswith(exc_id):
114 132 exc_id = os.path.join(exc_store_path, fname)
115 133 break
116 134 continue
117 135 else:
118 136 exc_id = None
119 137
120 138 return exc_id
121 139
122 140
123 141 def _read_exception(exc_id, prefix):
124 142 exc_id_file_path = _find_exc_file(exc_id=exc_id, prefix=prefix)
125 143 if exc_id_file_path:
126 144 with open(exc_id_file_path, 'rb') as f:
127 145 return exc_unserialize(f.read())
128 146 else:
129 147 log.debug('Exception File `%s` not found', exc_id_file_path)
130 148 return None
131 149
132 150
133 151 def read_exception(exc_id, prefix=global_prefix):
134 152 try:
135 153 return _read_exception(exc_id=exc_id, prefix=prefix)
136 154 except Exception:
137 155 log.exception('Failed to read exception `%s` information', exc_id)
138 156 # there's no way this can fail, it will crash server badly if it does.
139 157 return None
140 158
141 159
142 160 def delete_exception(exc_id, prefix=global_prefix):
143 161 try:
144 162 exc_id_file_path = _find_exc_file(exc_id, prefix=prefix)
145 163 if exc_id_file_path:
146 164 os.remove(exc_id_file_path)
147 165
148 166 except Exception:
149 167 log.exception('Failed to remove exception `%s` information', exc_id)
150 168 # there's no way this can fail, it will crash server badly if it does.
151 169 pass
General Comments 0
You need to be logged in to leave comments. Login now