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