##// END OF EJS Templates
cache exc store, and use glob.glob for scaning exc store
marcink -
r3975:6411e6b8 default
parent child Browse files
Show More
@@ -1,166 +1,172 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
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 import glob
28
29
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 vcsserver.lib.exc_tracking
33 # NOTE: Any changes should be synced with exc_tracking at vcsserver.lib.exc_tracking
33 global_prefix = 'rhodecode'
34 global_prefix = 'rhodecode'
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
54 _exc_store = None
55
53
56
54 def get_exc_store():
57 def get_exc_store():
55 """
58 """
56 Get and create exception store if it's not existing
59 Get and create exception store if it's not existing
57 """
60 """
61 global _exc_store
58 import rhodecode as app
62 import rhodecode as app
59
63
64 if _exc_store is not None:
65 # quick global cache
66 return _exc_store
67
60 exc_store_dir = app.CONFIG.get('exception_tracker.store_path', '') or tempfile.gettempdir()
68 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)
69 _exc_store_path = os.path.join(exc_store_dir, exc_store_dir_name)
62
70
63 _exc_store_path = os.path.abspath(_exc_store_path)
71 _exc_store_path = os.path.abspath(_exc_store_path)
64 if not os.path.isdir(_exc_store_path):
72 if not os.path.isdir(_exc_store_path):
65 os.makedirs(_exc_store_path)
73 os.makedirs(_exc_store_path)
66 log.debug('Initializing exceptions store at %s', _exc_store_path)
74 log.debug('Initializing exceptions store at %s', _exc_store_path)
75 _exc_store = _exc_store_path
76
67 return _exc_store_path
77 return _exc_store_path
68
78
69
79
70 def _store_exception(exc_id, exc_type_name, exc_traceback, prefix):
80 def _store_exception(exc_id, exc_type_name, exc_traceback, prefix):
71 """
81 """
72 Low level function to store exception in the exception tracker
82 Low level function to store exception in the exception tracker
73 """
83 """
74
84
75 exc_store_path = get_exc_store()
85 exc_store_path = get_exc_store()
76 exc_data, org_data = exc_serialize(exc_id, exc_traceback, exc_type_name)
86 exc_data, org_data = exc_serialize(exc_id, exc_traceback, exc_type_name)
77 exc_pref_id = '{}_{}_{}'.format(exc_id, prefix, org_data['exc_timestamp'])
87 exc_pref_id = '{}_{}_{}'.format(exc_id, prefix, org_data['exc_timestamp'])
78 if not os.path.isdir(exc_store_path):
88 if not os.path.isdir(exc_store_path):
79 os.makedirs(exc_store_path)
89 os.makedirs(exc_store_path)
80 stored_exc_path = os.path.join(exc_store_path, exc_pref_id)
90 stored_exc_path = os.path.join(exc_store_path, exc_pref_id)
81 with open(stored_exc_path, 'wb') as f:
91 with open(stored_exc_path, 'wb') as f:
82 f.write(exc_data)
92 f.write(exc_data)
83 log.debug('Stored generated exception %s as: %s', exc_id, stored_exc_path)
93 log.debug('Stored generated exception %s as: %s', exc_id, stored_exc_path)
84
94
85
95
86 def _prepare_exception(exc_info):
96 def _prepare_exception(exc_info):
87 exc_type, exc_value, exc_traceback = exc_info
97 exc_type, exc_value, exc_traceback = exc_info
88 exc_type_name = exc_type.__name__
98 exc_type_name = exc_type.__name__
89
99
90 tb = ''.join(traceback.format_exception(
100 tb = ''.join(traceback.format_exception(
91 exc_type, exc_value, exc_traceback, None))
101 exc_type, exc_value, exc_traceback, None))
92
102
93 return exc_type_name, tb
103 return exc_type_name, tb
94
104
95
105
96 def store_exception(exc_id, exc_info, prefix=global_prefix):
106 def store_exception(exc_id, exc_info, prefix=global_prefix):
97 """
107 """
98 Example usage::
108 Example usage::
99
109
100 exc_info = sys.exc_info()
110 exc_info = sys.exc_info()
101 store_exception(id(exc_info), exc_info)
111 store_exception(id(exc_info), exc_info)
102 """
112 """
103
113
104 try:
114 try:
105 exc_type_name, exc_traceback = _prepare_exception(exc_info)
115 exc_type_name, exc_traceback = _prepare_exception(exc_info)
106 _store_exception(exc_id=exc_id, exc_type_name=exc_type_name,
116 _store_exception(exc_id=exc_id, exc_type_name=exc_type_name,
107 exc_traceback=exc_traceback, prefix=prefix)
117 exc_traceback=exc_traceback, prefix=prefix)
108 except Exception:
118 except Exception:
109 log.exception('Failed to store exception `%s` information', exc_id)
119 log.exception('Failed to store exception `%s` information', exc_id)
110 # there's no way this can fail, it will crash server badly if it does.
120 # there's no way this can fail, it will crash server badly if it does.
111 pass
121 pass
112
122
113
123
114 def _find_exc_file(exc_id, prefix=global_prefix):
124 def _find_exc_file(exc_id, prefix=global_prefix):
115 exc_store_path = get_exc_store()
125 exc_store_path = get_exc_store()
116 if prefix:
126 if prefix:
117 exc_id = '{}_{}'.format(exc_id, prefix)
127 exc_id = '{}_{}'.format(exc_id, prefix)
118 else:
128 else:
119 # search without a prefix
129 # search without a prefix
120 exc_id = '{}'.format(exc_id)
130 exc_id = '{}'.format(exc_id)
121
131
122 # we need to search the store for such start pattern as above
132 found_exc_id = None
123 for fname in os.listdir(exc_store_path):
133 matches = glob.glob(os.path.join(exc_store_path, exc_id) + '*')
124 if fname.startswith(exc_id):
134 if matches:
125 exc_id = os.path.join(exc_store_path, fname)
135 found_exc_id = matches[0]
126 break
127 continue
128 else:
129 exc_id = None
130
136
131 return exc_id
137 return found_exc_id
132
138
133
139
134 def _read_exception(exc_id, prefix):
140 def _read_exception(exc_id, prefix):
135 exc_id_file_path = _find_exc_file(exc_id=exc_id, prefix=prefix)
141 exc_id_file_path = _find_exc_file(exc_id=exc_id, prefix=prefix)
136 if exc_id_file_path:
142 if exc_id_file_path:
137 with open(exc_id_file_path, 'rb') as f:
143 with open(exc_id_file_path, 'rb') as f:
138 return exc_unserialize(f.read())
144 return exc_unserialize(f.read())
139 else:
145 else:
140 log.debug('Exception File `%s` not found', exc_id_file_path)
146 log.debug('Exception File `%s` not found', exc_id_file_path)
141 return None
147 return None
142
148
143
149
144 def read_exception(exc_id, prefix=global_prefix):
150 def read_exception(exc_id, prefix=global_prefix):
145 try:
151 try:
146 return _read_exception(exc_id=exc_id, prefix=prefix)
152 return _read_exception(exc_id=exc_id, prefix=prefix)
147 except Exception:
153 except Exception:
148 log.exception('Failed to read exception `%s` information', exc_id)
154 log.exception('Failed to read exception `%s` information', exc_id)
149 # there's no way this can fail, it will crash server badly if it does.
155 # there's no way this can fail, it will crash server badly if it does.
150 return None
156 return None
151
157
152
158
153 def delete_exception(exc_id, prefix=global_prefix):
159 def delete_exception(exc_id, prefix=global_prefix):
154 try:
160 try:
155 exc_id_file_path = _find_exc_file(exc_id, prefix=prefix)
161 exc_id_file_path = _find_exc_file(exc_id, prefix=prefix)
156 if exc_id_file_path:
162 if exc_id_file_path:
157 os.remove(exc_id_file_path)
163 os.remove(exc_id_file_path)
158
164
159 except Exception:
165 except Exception:
160 log.exception('Failed to remove exception `%s` information', exc_id)
166 log.exception('Failed to remove exception `%s` information', exc_id)
161 # there's no way this can fail, it will crash server badly if it does.
167 # there's no way this can fail, it will crash server badly if it does.
162 pass
168 pass
163
169
164
170
165 def generate_id():
171 def generate_id():
166 return id(object())
172 return id(object())
General Comments 0
You need to be logged in to leave comments. Login now