0001-misc-use-only-record_error-to-record-and-notify-5541.patch
tests/form_pages/test_all.py | ||
---|---|---|
5908 | 5908 |
data_source.store() |
5909 | 5909 |
resp2 = app.get(select2_url + '?q=hell') |
5910 | 5910 |
assert emails.count() == 1 |
5911 |
assert ( |
|
5912 |
emails.get_latest('subject') |
|
5913 |
== '[ERROR] [DATASOURCE] Exception: Error loading JSON data source (...)' |
|
5914 |
) |
|
5911 |
assert emails.get_latest('subject') == '[ERROR] [DATASOURCE] Error loading JSON data source (...)' |
|
5915 | 5912 |
if pub.is_using_postgresql(): |
5916 | 5913 |
assert pub.loggederror_class.count() == 1 |
5917 | 5914 |
logged_error = pub.loggederror_class.select()[0] |
5918 | 5915 |
assert logged_error.workflow_id is None |
5919 |
assert logged_error.summary == '[DATASOURCE] Exception: Error loading JSON data source (...)'
|
|
5916 |
assert logged_error.summary == '[DATASOURCE] Error loading JSON data source (...)' |
|
5920 | 5917 | |
5921 | 5918 |
data_source.notify_on_errors = False |
5922 | 5919 |
data_source.store() |
tests/test_datasource.py | ||
---|---|---|
148 | 148 |
assert pub.loggederror_class.count() == 1 |
149 | 149 |
logged_error = pub.loggederror_class.select()[0] |
150 | 150 |
assert logged_error.workflow_id is None |
151 |
assert ( |
|
152 |
logged_error.summary == "[DATASOURCE] Exception: Failed to eval() Python data source ('foobar')" |
|
153 |
) |
|
151 |
assert logged_error.summary == "[DATASOURCE] Failed to eval() Python data source ('foobar')" |
|
154 | 152 | |
155 | 153 |
# expression not iterable |
156 | 154 |
datasource = {'type': 'formula', 'value': '2', 'notify_on_errors': True, 'record_on_errors': True} |
... | ... | |
160 | 158 |
assert pub.loggederror_class.count() == 2 |
161 | 159 |
logged_error = pub.loggederror_class.select()[1] |
162 | 160 |
assert logged_error.workflow_id is None |
163 |
assert ( |
|
164 |
logged_error.summary |
|
165 |
== "[DATASOURCE] Exception: Python data source ('2') gave a non-iterable result" |
|
166 |
) |
|
161 |
assert logged_error.summary == "[DATASOURCE] Python data source ('2') gave a non-iterable result" |
|
167 | 162 | |
168 | 163 | |
169 | 164 |
def test_python_datasource_with_evalutils(pub): |
... | ... | |
407 | 402 |
assert logged_error.workflow_id is None |
408 | 403 |
assert ( |
409 | 404 |
logged_error.summary |
410 |
== "[DATASOURCE] Exception: Error loading JSON data source (error in HTTP request to http://remote.example.net/404 (status: 404))"
|
|
405 |
== "[DATASOURCE] Error loading JSON data source (error in HTTP request to http://remote.example.net/404 (status: 404))" |
|
411 | 406 |
) |
412 | 407 | |
413 | 408 |
datasource = { |
... | ... | |
425 | 420 |
assert logged_error.workflow_id is None |
426 | 421 |
assert ( |
427 | 422 |
logged_error.summary |
428 |
== "[DATASOURCE] Exception: Error reading JSON data source output (Expecting value: line 1 column 1 (char 0))"
|
|
423 |
== "[DATASOURCE] Error reading JSON data source output (Expecting value: line 1 column 1 (char 0))" |
|
429 | 424 |
) |
430 | 425 | |
431 | 426 |
datasource = { |
... | ... | |
440 | 435 |
assert pub.loggederror_class.count() == 3 |
441 | 436 |
logged_error = pub.loggederror_class.select()[2] |
442 | 437 |
assert logged_error.workflow_id is None |
443 |
assert logged_error.summary == "[DATASOURCE] Exception: Error loading JSON data source (error)"
|
|
438 |
assert logged_error.summary == "[DATASOURCE] Error loading JSON data source (error)" |
|
444 | 439 | |
445 | 440 |
datasource = { |
446 | 441 |
'type': 'json', |
... | ... | |
454 | 449 |
assert pub.loggederror_class.count() == 4 |
455 | 450 |
logged_error = pub.loggederror_class.select()[3] |
456 | 451 |
assert logged_error.workflow_id is None |
457 |
assert logged_error.summary == "[DATASOURCE] Exception: Error reading JSON data source output (err 1)"
|
|
452 |
assert logged_error.summary == "[DATASOURCE] Error reading JSON data source output (err 1)" |
|
458 | 453 | |
459 | 454 | |
460 | 455 |
def test_json_datasource_bad_url_scheme(pub, error_email, emails): |
... | ... | |
477 | 472 |
assert logged_error.workflow_id is None |
478 | 473 |
assert ( |
479 | 474 |
logged_error.summary |
480 |
== "[DATASOURCE] Exception: Error loading JSON data source (invalid scheme in URL foo://bar)"
|
|
475 |
== "[DATASOURCE] Error loading JSON data source (invalid scheme in URL foo://bar)" |
|
481 | 476 |
) |
482 | 477 | |
483 | 478 |
datasource = {'type': 'json', 'value': '/bla/blo', 'notify_on_errors': True, 'record_on_errors': True} |
... | ... | |
490 | 485 |
assert logged_error.workflow_id is None |
491 | 486 |
assert ( |
492 | 487 |
logged_error.summary |
493 |
== "[DATASOURCE] Exception: Error loading JSON data source (invalid scheme in URL /bla/blo)"
|
|
488 |
== "[DATASOURCE] Error loading JSON data source (invalid scheme in URL /bla/blo)" |
|
494 | 489 |
) |
495 | 490 | |
496 | 491 | |
... | ... | |
829 | 824 |
assert logged_error.workflow_id is None |
830 | 825 |
assert ( |
831 | 826 |
logged_error.summary |
832 |
== "[DATASOURCE] Exception: Error loading JSON data source (error in HTTP request to http://remote.example.net/404 (status: 404))"
|
|
827 |
== "[DATASOURCE] Error loading JSON data source (error in HTTP request to http://remote.example.net/404 (status: 404))" |
|
833 | 828 |
) |
834 | 829 | |
835 | 830 |
datasource = { |
... | ... | |
847 | 842 |
assert logged_error.workflow_id is None |
848 | 843 |
assert ( |
849 | 844 |
logged_error.summary |
850 |
== "[DATASOURCE] Exception: Error reading JSON data source output (Expecting value: line 1 column 1 (char 0))"
|
|
845 |
== "[DATASOURCE] Error reading JSON data source output (Expecting value: line 1 column 1 (char 0))" |
|
851 | 846 |
) |
852 | 847 | |
853 | 848 |
datasource = { |
... | ... | |
863 | 858 |
assert pub.loggederror_class.count() == 3 |
864 | 859 |
logged_error = pub.loggederror_class.select()[2] |
865 | 860 |
assert logged_error.workflow_id is None |
866 |
assert logged_error.summary == "[DATASOURCE] Exception: Error loading JSON data source (error)"
|
|
861 |
assert logged_error.summary == "[DATASOURCE] Error loading JSON data source (error)" |
|
867 | 862 | |
868 | 863 |
datasource = { |
869 | 864 |
'type': 'geojson', |
... | ... | |
877 | 872 |
assert pub.loggederror_class.count() == 4 |
878 | 873 |
logged_error = pub.loggederror_class.select()[3] |
879 | 874 |
assert logged_error.workflow_id is None |
880 |
assert logged_error.summary == "[DATASOURCE] Exception: Error reading JSON data source output (err 1)"
|
|
875 |
assert logged_error.summary == "[DATASOURCE] Error reading JSON data source output (err 1)" |
|
881 | 876 | |
882 | 877 | |
883 | 878 |
def test_geojson_datasource_bad_url_scheme(pub, error_email, emails): |
... | ... | |
898 | 893 |
assert logged_error.workflow_id is None |
899 | 894 |
assert ( |
900 | 895 |
logged_error.summary |
901 |
== "[DATASOURCE] Exception: Error loading JSON data source (invalid scheme in URL foo://bar)"
|
|
896 |
== "[DATASOURCE] Error loading JSON data source (invalid scheme in URL foo://bar)" |
|
902 | 897 |
) |
903 | 898 | |
904 | 899 |
datasource = {'type': 'geojson', 'value': '/bla/blo', 'notify_on_errors': True, 'record_on_errors': True} |
... | ... | |
911 | 906 |
assert logged_error.workflow_id is None |
912 | 907 |
assert ( |
913 | 908 |
logged_error.summary |
914 |
== "[DATASOURCE] Exception: Error loading JSON data source (invalid scheme in URL /bla/blo)"
|
|
909 |
== "[DATASOURCE] Error loading JSON data source (invalid scheme in URL /bla/blo)" |
|
915 | 910 |
) |
916 | 911 | |
917 | 912 |
tests/test_hobo_notify.py | ||
---|---|---|
664 | 664 |
assert not User.select()[0].email |
665 | 665 | |
666 | 666 | |
667 |
def notify_of_exception(exc_info, context): |
|
668 |
raise Exception(exc_info) |
|
667 |
def record_error(exception=None, *args, **kwargs): |
|
668 |
if exception: |
|
669 |
raise exception |
|
669 | 670 | |
670 | 671 | |
671 | 672 |
def test_process_notification_user_with_errors(pub): |
... | ... | |
711 | 712 | |
712 | 713 |
notification['full'] = False |
713 | 714 | |
714 |
pub.notify_of_exception = notify_of_exception
|
|
715 |
pub.record_error = record_error
|
|
715 | 716 | |
716 | 717 |
for key in ('uuid', 'first_name', 'last_name', 'email'): |
717 | 718 |
backup = notification['objects']['data'][0][key] |
718 | 719 |
del notification['objects']['data'][0][key] |
719 | 720 |
with pytest.raises(Exception) as e: |
720 | 721 |
CmdHoboNotify.process_notification(notification) |
721 |
assert e.value.args[0][0] == ValueError
|
|
722 |
assert e.value.args[0][1].args == ('invalid user',)
|
|
722 |
assert e.type == ValueError
|
|
723 |
assert e.value.args == ('invalid user',) |
|
723 | 724 |
assert User.count() == 0 |
724 | 725 |
notification['objects']['data'][0][key] = backup |
725 | 726 | |
... | ... | |
727 | 728 |
del notification['objects']['data'][0]['uuid'] |
728 | 729 |
with pytest.raises(Exception) as e: |
729 | 730 |
CmdHoboNotify.process_notification(notification) |
730 |
assert e.value.args[0][0] == KeyError
|
|
731 |
assert e.value.args[0][1].args == ('user without uuid',)
|
|
731 |
assert e.type == KeyError
|
|
732 |
assert e.value.args == ('user without uuid',) |
|
732 | 733 | |
733 | 734 | |
734 | 735 |
def test_process_notification_role_with_errors(pub): |
tests/test_workflows.py | ||
---|---|---|
5990 | 5990 |
if two_pubs.is_using_postgresql(): |
5991 | 5991 |
errors = two_pubs.loggederror_class.select() |
5992 | 5992 |
assert len(errors) == 2 |
5993 |
assert any('form_var_toto_string' in (error.exception_message or '') for error in errors) |
|
5994 |
assert any('Missing field' in error.summary for error in errors) |
|
5993 |
assert 'form_var_toto_string' in errors[0].exception_message |
|
5994 |
assert 'Missing field' in errors[1].summary |
|
5995 |
assert errors[0].formdata_id == str(target_formdef.data_class().select()[0].id) |
|
5996 |
assert errors[1].formdata_id == str(target_formdef.data_class().select()[0].id) |
|
5995 | 5997 | |
5996 | 5998 |
# no tracking code has been created |
5997 | 5999 |
created_formdata = target_formdef.data_class().select()[0] |
... | ... | |
6374 | 6376 |
assert two_pubs.loggederror_class.count() == 1 |
6375 | 6377 |
logged_error = two_pubs.loggederror_class.select()[0] |
6376 | 6378 |
assert logged_error.summary == 'Failed to attach user (not found: "zzz")' |
6379 |
assert logged_error.formdata_id == str(carddef.data_class().select()[0].id) |
|
6377 | 6380 | |
6378 | 6381 |
# user association on invalid template |
6379 | 6382 |
carddef.data_class().wipe() |
tests/test_wscall.py | ||
---|---|---|
220 | 220 |
assert 'Foo Bar ' in resp.text |
221 | 221 |
if notify_on_errors: |
222 | 222 |
assert emails.count() == 1 |
223 |
assert "[ERROR] [WSCALL] Exception: %s whatever" % status_code in emails.emails
|
|
223 |
assert "[ERROR] [WSCALL] %s whatever" % status_code in emails.emails |
|
224 | 224 |
emails.empty() |
225 | 225 |
else: |
226 | 226 |
assert emails.count() == 0 |
wcs/api.py | ||
---|---|---|
17 | 17 |
import datetime |
18 | 18 |
import json |
19 | 19 |
import re |
20 |
import sys |
|
21 | 20 |
import time |
22 | 21 |
import urllib.parse |
23 | 22 | |
... | ... | |
1119 | 1118 |
if 'data_source' in info: |
1120 | 1119 |
error_summary = 'Error loading JSON data source (%s)' % str(e) |
1121 | 1120 |
data_source = NamedDataSource.get(info['data_source']) |
1122 |
try: |
|
1123 |
raise Exception(error_summary) from e |
|
1124 |
except Exception: |
|
1125 |
exc_info = sys.exc_info() |
|
1126 |
get_publisher().notify_of_exception( |
|
1127 |
exc_info, |
|
1121 |
get_publisher().record_error( |
|
1122 |
error_summary, |
|
1128 | 1123 |
context='[DATASOURCE]', |
1129 | 1124 |
notify=data_source.notify_on_errors, |
1130 | 1125 |
record=data_source.record_on_errors, |
wcs/ctl/hobo_notify.py | ||
---|---|---|
175 | 175 |
if field.convert_value_from_anything: |
176 | 176 |
try: |
177 | 177 |
field_value = field.convert_value_from_anything(field_value) |
178 |
except ValueError: |
|
179 |
publisher.notify_of_exception(sys.exc_info(), context='[PROVISIONNING]')
|
|
178 |
except ValueError as e:
|
|
179 |
publisher.record_error(exception=e, context='[PROVISIONNING]', notify=True)
|
|
180 | 180 |
continue |
181 | 181 |
user.form_data[field.id] = field_value |
182 | 182 |
user.name_identifiers = [uuid] |
... | ... | |
202 | 202 |
users = User.get_users_with_name_identifier(o['uuid']) |
203 | 203 |
for user in users: |
204 | 204 |
user.set_deleted() |
205 |
except Exception: |
|
206 |
publisher.notify_of_exception(sys.exc_info(), context='[PROVISIONNING]')
|
|
205 |
except Exception as e:
|
|
206 |
publisher.record_error(exception=e, context='[PROVISIONNING]', notify=True)
|
|
207 | 207 | |
208 | 208 | |
209 | 209 |
CmdHoboNotify.register() |
wcs/data_sources.py | ||
---|---|---|
16 | 16 | |
17 | 17 |
import collections |
18 | 18 |
import hashlib |
19 |
import sys |
|
20 | 19 |
import urllib.parse |
21 | 20 |
import xml.etree.ElementTree as ET |
22 | 21 | |
... | ... | |
170 | 169 |
data_key = data_source.get('data_attribute') or 'data' |
171 | 170 |
geojson = data_source.get('type') == 'geojson' |
172 | 171 |
error_summary = None |
173 |
exc = None |
|
174 | 172 | |
175 | 173 |
try: |
176 | 174 |
entries = misc.json_loads(misc.urlopen(url).read()) |
... | ... | |
194 | 192 |
return entries |
195 | 193 |
except misc.ConnectionError as e: |
196 | 194 |
error_summary = 'Error loading %s (%s)' % (log_message_part, str(e)) |
197 |
exc = e |
|
198 | 195 |
except (ValueError, TypeError) as e: |
199 | 196 |
error_summary = 'Error reading %s output (%s)' % (log_message_part, str(e)) |
200 |
exc = e |
|
201 | 197 | |
202 |
if data_source and (data_source.get('record_on_errors') or data_source.get('notify_on_errors')): |
|
203 |
try: |
|
204 |
raise Exception(error_summary) from exc |
|
205 |
except Exception: |
|
206 |
exc_info = sys.exc_info() |
|
207 |
get_publisher().notify_of_exception( |
|
208 |
exc_info, |
|
198 |
if data_source: |
|
199 |
get_publisher().record_error( |
|
200 |
error_summary, |
|
209 | 201 |
context='[DATASOURCE]', |
210 | 202 |
notify=data_source.get('notify_on_errors'), |
211 | 203 |
record=data_source.get('record_on_errors'), |
... | ... | |
312 | 304 |
# noqa pylint: disable=eval-used |
313 | 305 |
value = eval(data_source.get('value'), global_eval_dict, variables) |
314 | 306 |
if not isinstance(value, collections.Iterable): |
315 |
try: |
|
316 |
raise Exception( |
|
317 |
'Python data source (%r) gave a non-iterable result' % data_source.get('value') |
|
318 |
) |
|
319 |
except Exception: |
|
320 |
exc_info = sys.exc_info() |
|
321 |
get_publisher().notify_of_exception( |
|
322 |
exc_info, |
|
307 |
get_publisher().record_error( |
|
308 |
'Python data source (%r) gave a non-iterable result' % data_source.get('value'), |
|
323 | 309 |
context='[DATASOURCE]', |
324 | 310 |
notify=data_source.get('notify_on_errors'), |
325 | 311 |
record=data_source.get('record_on_errors'), |
... | ... | |
340 | 326 |
return [{'id': x, 'text': x} for x in value] |
341 | 327 |
return value |
342 | 328 |
except Exception as exc: |
343 |
try: |
|
344 |
raise Exception( |
|
345 |
'Failed to eval() Python data source (%r)' % data_source.get('value') |
|
346 |
) from exc |
|
347 |
except Exception: |
|
348 |
exc_info = sys.exc_info() |
|
349 |
get_publisher().notify_of_exception( |
|
350 |
exc_info, |
|
329 |
get_publisher().record_error( |
|
330 |
'Failed to eval() Python data source (%r)' % data_source.get('value'), |
|
331 |
exception=exc, |
|
351 | 332 |
context='[DATASOURCE]', |
352 | 333 |
notify=data_source.get('notify_on_errors'), |
353 | 334 |
record=data_source.get('record_on_errors'), |
wcs/logged_errors.py | ||
---|---|---|
94 | 94 |
return error |
95 | 95 | |
96 | 96 |
@classmethod |
97 |
def record_exception(cls, error_summary, plain_error_msg, publisher): |
|
98 |
try: |
|
99 |
context = publisher.substitutions.get_context_variables() |
|
100 |
except Exception: |
|
101 |
return |
|
102 |
formdata_id = context.get('form_number_raw') |
|
103 |
formdef_urlname = context.get('form_slug') |
|
104 |
if formdef_urlname: |
|
105 |
klass = FormDef |
|
106 |
if context.get('form_class_name') == 'CardDef': |
|
107 |
klass = CardDef |
|
108 |
formdef = klass.get_by_urlname(formdef_urlname) |
|
109 |
formdata = formdef.data_class().get(formdata_id, ignore_errors=True) |
|
110 |
workflow = formdef.workflow |
|
111 |
else: |
|
112 |
formdef = formdata = workflow = None |
|
97 |
def record_error(cls, error_summary, plain_error_msg, publisher, *args, **kwargs): |
|
98 |
formdef = kwargs.pop('formdef', None) |
|
99 |
formdata = kwargs.pop('formdata', None) |
|
100 |
workflow = kwargs.pop('workflow', None) |
|
101 |
if not any([formdef, formdata, workflow]): |
|
102 |
try: |
|
103 |
context = publisher.substitutions.get_context_variables() |
|
104 |
except Exception: |
|
105 |
return |
|
106 |
formdata_id = context.get('form_number_raw') |
|
107 |
formdef_urlname = context.get('form_slug') |
|
108 |
if formdef_urlname: |
|
109 |
klass = FormDef |
|
110 |
if context.get('form_class_name') == 'CardDef': |
|
111 |
klass = CardDef |
|
112 |
formdef = klass.get_by_urlname(formdef_urlname) |
|
113 |
formdata = formdef.data_class().get(formdata_id, ignore_errors=True) |
|
114 |
workflow = formdef.workflow |
|
115 |
else: |
|
116 |
formdef = formdata = workflow = None |
|
113 | 117 |
return cls.record( |
114 |
error_summary, plain_error_msg, formdata=formdata, formdef=formdef, workflow=workflow |
|
118 |
error_summary, |
|
119 |
plain_error_msg, |
|
120 |
formdata=formdata, |
|
121 |
formdef=formdef, |
|
122 |
workflow=workflow, |
|
123 |
*args, |
|
124 |
**kwargs, |
|
115 | 125 |
) |
116 | 126 | |
117 | 127 |
def build_tech_id(self): |
wcs/publisher.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU General Public License |
15 | 15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import io |
|
17 | 18 |
import json |
18 | 19 |
import os |
19 | 20 |
import pickle |
... | ... | |
350 | 351 | |
351 | 352 |
conn, cur = sql.get_connection_and_cursor() |
352 | 353 |
sql.drop_views(None, conn, cur) |
353 |
for formdef in FormDef.select() + CardDef.select(): |
|
354 |
sql.do_formdef_tables(formdef) |
|
354 |
for _formdef in FormDef.select() + CardDef.select():
|
|
355 |
sql.do_formdef_tables(_formdef)
|
|
355 | 356 |
sql.migrate_global_views(conn, cur) |
356 | 357 |
conn.commit() |
357 | 358 |
cur.close() |
358 | 359 | |
359 |
def notify_of_exception(self, exc_tuple, context=None, record=True, notify=True): |
|
360 |
exc_type, exc_value, tb = exc_tuple |
|
361 |
error_summary = traceback.format_exception_only(exc_type, exc_value) |
|
362 |
error_summary = error_summary[0][0:-1] # de-listify and strip newline |
|
363 |
if context: |
|
364 |
error_summary = '%s %s' % (context, error_summary) |
|
360 |
def record_error( |
|
361 |
self, error_summary=None, context=None, exception=None, record=True, notify=False, *args, **kwargs |
|
362 |
): |
|
363 |
if not record and not notify: |
|
364 |
return |
|
365 | 365 | |
366 |
plain_error_msg = str(self._generate_plaintext_error(get_request(), self, exc_type, exc_value, tb)) |
|
366 |
if exception is not None: |
|
367 |
exc_type, exc_value, tb = sys.exc_info() |
|
368 |
if not error_summary: |
|
369 |
error_summary = traceback.format_exception_only(exc_type, exc_value) |
|
370 |
error_summary = error_summary[0][0:-1] # de-listify and strip newline |
|
371 |
plain_error_msg = str( |
|
372 |
self._generate_plaintext_error(get_request(), self, exc_type, exc_value, tb) |
|
373 |
) |
|
374 |
else: |
|
375 |
error_file = io.StringIO() |
|
376 |
print('Stack trace (most recent call first):', file=error_file) |
|
377 |
stack_summary = traceback.extract_stack() |
|
378 |
stack_summary.reverse() |
|
379 |
traceback.print_list(stack_summary[1:], file=error_file) |
|
380 |
if get_request(): |
|
381 |
error_file.write('\n') |
|
382 |
error_file.write(get_request().dump()) |
|
383 |
error_file.write('\n') |
|
384 |
plain_error_msg = error_file.getvalue() |
|
367 | 385 | |
368 |
self.log_internal_error(error_summary, plain_error_msg, record=record, notify=notify) |
|
386 |
if context: |
|
387 |
error_summary = '%s %s' % (context, error_summary) |
|
388 |
if error_summary is None: |
|
389 |
return |
|
369 | 390 | |
370 |
def log_internal_error(self, error_summary, plain_error_msg, record=False, notify=True): |
|
371 |
tech_id = None |
|
391 |
logged_exception = None |
|
372 | 392 |
if record and self.loggederror_class: |
373 |
logged_exception = self.loggederror_class.record_exception(
|
|
374 |
error_summary, plain_error_msg, publisher=self |
|
393 |
logged_exception = self.loggederror_class.record_error(
|
|
394 |
error_summary, plain_error_msg, publisher=self, exception=exception, *args, **kwargs
|
|
375 | 395 |
) |
376 |
if logged_exception: |
|
377 |
tech_id = logged_exception.tech_id |
|
378 |
if not notify: |
|
396 |
if not notify or logged_exception and logged_exception.occurences_count > 1: |
|
397 |
# notify only first occurence |
|
379 | 398 |
return |
380 | 399 |
try: |
381 |
self.logger.log_internal_error(error_summary, plain_error_msg, tech_id) |
|
400 |
self.logger.log_internal_error( |
|
401 |
error_summary, plain_error_msg, logged_exception.tech_id if logged_exception else None |
|
402 |
) |
|
382 | 403 |
except OSError: |
383 | 404 |
# Could happen if there is no mail server available and exceptions |
384 | 405 |
# were configured to be mailed. (formerly socket.error) |
385 | 406 |
# Could also could happen on file descriptor exhaustion. |
386 | 407 |
pass |
387 | 408 | |
388 |
def record_error(self, *args, **kwargs): |
|
389 |
if self.loggederror_class: |
|
390 |
self.loggederror_class.record(*args, **kwargs) |
|
391 | ||
392 | 409 |
def apply_global_action_timeouts(self): |
393 | 410 |
from wcs.workflows import Workflow, WorkflowGlobalActionTimeoutTrigger |
394 | 411 |
wcs/qommon/afterjobs.py | ||
---|---|---|
95 | 95 |
self.execute() |
96 | 96 |
else: |
97 | 97 |
self.job_cmd(job=self) |
98 |
except Exception: |
|
99 |
get_publisher().notify_of_exception(sys.exc_info())
|
|
98 |
except Exception as e:
|
|
99 |
get_publisher().record_error(exception=e, notify=True)
|
|
100 | 100 |
self.exception = traceback.format_exc() |
101 | 101 |
self.status = N_('failed') |
102 | 102 |
else: |
wcs/qommon/cron.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU General Public License |
15 | 15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import sys |
|
18 | ||
19 | 17 |
from django.conf import settings |
20 | 18 | |
21 | 19 | |
... | ... | |
69 | 67 |
publisher.substitutions.feed(extra_source(publisher, None)) |
70 | 68 |
try: |
71 | 69 |
job.function(publisher) |
72 |
except Exception: |
|
73 |
publisher.notify_of_exception(sys.exc_info(), context='[CRON]') |
|
70 |
except Exception as e: |
|
71 |
publisher.record_error(exception=e, context='[CRON]', notify=True) |
wcs/qommon/ident/franceconnect.py | ||
---|---|---|
16 | 16 | |
17 | 17 |
import base64 |
18 | 18 |
import hashlib |
19 |
import sys |
|
20 | 19 |
import urllib.parse |
21 | 20 |
import uuid |
22 | 21 | |
... | ... | |
415 | 414 | |
416 | 415 |
try: |
417 | 416 |
value = WorkflowStatusItem.compute(value, context=user_info) |
418 |
except Exception: |
|
419 |
get_publisher().notify_of_exception(sys.exc_info(), context='[FC-user-compute]')
|
|
417 |
except Exception as e:
|
|
418 |
get_publisher().record_error(exception=e, context='[FC-user-compute]', notify=True)
|
|
420 | 419 |
continue |
421 | 420 |
if field_varname == '__name': |
422 | 421 |
user.name = value |
wcs/qommon/saml2.py | ||
---|---|---|
15 | 15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 | 17 |
import os |
18 |
import sys |
|
19 | 18 |
import time |
20 | 19 |
import urllib.parse |
21 | 20 |
from xml.sax.saxutils import escape |
... | ... | |
299 | 298 |
!= get_cfg('sp', {}).get('saml2_base_url') + get_request().get_url()[last_slash:] |
300 | 299 |
): |
301 | 300 |
return error_page('SubjectConfirmation Recipient Mismatch') |
302 |
except Exception: |
|
303 |
get_publisher().notify_of_exception(sys.exc_info(), context='[SAML]')
|
|
301 |
except Exception as e:
|
|
302 |
get_publisher().record_error(exception=e, context='[SAML]', notify=True)
|
|
304 | 303 |
return error_page('Error checking SubjectConfirmation Recipient') |
305 | 304 | |
306 | 305 |
assertions_dir = os.path.join(get_publisher().app_dir, 'assertions') |
... | ... | |
339 | 338 |
return error_page('Assertion received too early') |
340 | 339 |
if not_on_or_after and current_time > not_on_or_after: |
341 | 340 |
return error_page('Assertion expired') |
342 |
except Exception: |
|
343 |
get_publisher().notify_of_exception(sys.exc_info(), context='[SAML]')
|
|
341 |
except Exception as e:
|
|
342 |
get_publisher().record_error(exception=e, context='[SAML]', notify=True)
|
|
344 | 343 |
return error_page('Error checking Assertion Time') |
345 | 344 | |
346 | 345 |
# TODO: check for unknown conditions |
... | ... | |
491 | 490 |
if field and field.convert_value_from_anything: |
492 | 491 |
try: |
493 | 492 |
field_value = field.convert_value_from_anything(field_value) |
494 |
except ValueError: |
|
495 |
get_publisher().notify_of_exception(sys.exc_info(), context='[SAML]')
|
|
493 |
except ValueError as e:
|
|
494 |
get_publisher().record_error(exception=e, context='[SAML]', notify=True)
|
|
496 | 495 |
continue |
497 | 496 |
if user.form_data.get(field_id) != field_value: |
498 | 497 |
user.form_data[field_id] = field_value |
wcs/qommon/storage.py | ||
---|---|---|
686 | 686 |
with locket.lock_file(objects_dir + '.lock.index'): |
687 | 687 |
try: |
688 | 688 |
self.update_indexes(previous_object_value, relative_object_filename) |
689 |
except Exception: |
|
689 |
except Exception as e:
|
|
690 | 690 |
# something failed, we can't keep using possibly broken indexes, so |
691 | 691 |
# we notify of the bug and remove the indexes |
692 |
get_publisher().notify_of_exception(sys.exc_info(), context='[STORAGE]')
|
|
692 |
get_publisher().record_error(exception=e, context='[STORAGE]', notify=True)
|
|
693 | 693 |
self.destroy_indexes() |
694 | 694 | |
695 | 695 |
@classmethod |
wcs/wf/profile.py | ||
---|---|---|
16 | 16 | |
17 | 17 |
import datetime |
18 | 18 |
import json |
19 |
import sys |
|
20 | 19 |
import time |
21 | 20 |
import urllib.parse |
22 | 21 |
import xml.etree.ElementTree as ET |
... | ... | |
158 | 157 |
if field and field.convert_value_from_anything: |
159 | 158 |
try: |
160 | 159 |
field_value = field.convert_value_from_anything(field_value) |
161 |
except ValueError: |
|
162 |
get_publisher().notify_of_exception(sys.exc_info(), context='[PROFILE]')
|
|
160 |
except ValueError as e:
|
|
161 |
get_publisher().record_error(exception=e, context='[PROFILE]', notify=True)
|
|
163 | 162 |
# invalid attribute, do not update it |
164 | 163 |
del new_data[field.varname] |
165 | 164 |
continue |
... | ... | |
188 | 187 |
user_uuid = user.name_identifiers[0] |
189 | 188 |
try: |
190 | 189 |
url = user_ws_url(user_uuid) |
191 |
except MissingSecret: |
|
192 |
get_publisher().notify_of_exception(sys.exc_info(), context='[PROFILE]')
|
|
190 |
except MissingSecret as e:
|
|
191 |
get_publisher().record_error(exception=e, context='[PROFILE]', notify=True)
|
|
193 | 192 |
return |
194 | 193 | |
195 | 194 |
payload = new_data.copy() |
wcs/wf/register_comment.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU General Public License |
15 | 15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import sys |
|
18 | ||
19 | 17 |
from quixote import get_publisher |
20 | 18 |
from quixote.html import htmltext |
21 | 19 | |
... | ... | |
121 | 119 |
# needed by AttachmentEvolutionPart.from_upload() |
122 | 120 |
upload.get_file_pointer() |
123 | 121 |
formdata.evolution[-1].add_part(AttachmentEvolutionPart.from_upload(upload, to=to)) |
124 |
except Exception: |
|
125 |
get_publisher().notify_of_exception(sys.exc_info(), context='[comment/attachments]')
|
|
122 |
except Exception as e:
|
|
123 |
get_publisher().record_error(exception=e, context='[comment/attachments]', notify=True)
|
|
126 | 124 |
continue |
127 | 125 | |
128 | 126 |
def perform(self, formdata): |
wcs/wf/roles.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU General Public License |
15 | 15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import sys |
|
18 | 17 |
import urllib.parse |
19 | 18 | |
20 | 19 |
from quixote import get_publisher, get_request, get_response |
... | ... | |
105 | 104 |
user_uuid = user.name_identifiers[0] |
106 | 105 |
try: |
107 | 106 |
url = roles_ws_url(role_uuid, user_uuid) |
108 |
except MissingSecret: |
|
109 |
get_publisher().notify_of_exception(sys.exc_info(), context='[ROLES]')
|
|
107 |
except MissingSecret as e:
|
|
108 |
get_publisher().record_error(exception=e, context='[ROLES]', notify=True)
|
|
110 | 109 |
return |
111 | 110 | |
112 | 111 |
def after_job(job=None): |
... | ... | |
175 | 174 |
user_uuid = user.name_identifiers[0] |
176 | 175 |
try: |
177 | 176 |
url = roles_ws_url(role_uuid, user_uuid) |
178 |
except MissingSecret: |
|
179 |
get_publisher().notify_of_exception(sys.exc_info(), context='[ROLES]')
|
|
177 |
except MissingSecret as e:
|
|
178 |
get_publisher().record_error(exception=e, context='[ROLES]', notify=True)
|
|
180 | 179 |
return |
181 | 180 | |
182 | 181 |
def after_job(job=None): |
wcs/wf/wscall.py | ||
---|---|---|
392 | 392 |
workflow_data['%s_connection_error' % self.varname] = str(e) |
393 | 393 |
formdata.update_workflow_data(workflow_data) |
394 | 394 |
formdata.store() |
395 |
self.action_on_error(self.action_on_network_errors, formdata, exc_info=sys.exc_info())
|
|
395 |
self.action_on_error(self.action_on_network_errors, formdata, exception=e)
|
|
396 | 396 |
return |
397 | 397 | |
398 | 398 |
app_error_code = get_app_error_code(response, data, self.response_type) |
... | ... | |
452 | 452 |
if self.response_type == 'json': |
453 | 453 |
try: |
454 | 454 |
d = json_loads(force_text(data)) |
455 |
except (ValueError, TypeError): |
|
455 |
except (ValueError, TypeError) as e:
|
|
456 | 456 |
formdata.update_workflow_data(workflow_data) |
457 | 457 |
formdata.store() |
458 |
self.action_on_error( |
|
459 |
self.action_on_bad_data, formdata, response, data=data, exc_info=sys.exc_info() |
|
460 |
) |
|
458 |
self.action_on_error(self.action_on_bad_data, formdata, response, data=data, exception=e) |
|
461 | 459 |
else: |
462 | 460 |
workflow_data['%s_response' % self.varname] = d |
463 | 461 |
if isinstance(d, dict) and self.method == 'POST': |
... | ... | |
480 | 478 |
) |
481 | 479 |
formdata.evolution[-1].add_part(attachment) |
482 | 480 | |
483 |
def action_on_error(self, action, formdata, response=None, data=None, exc_info=None):
|
|
481 |
def action_on_error(self, action, formdata, response=None, data=None, exception=None):
|
|
484 | 482 |
if action in (':pass', ':stop') and ( |
485 | 483 |
self.notify_on_errors or self.record_on_errors or self.record_errors |
486 | 484 |
): |
487 |
if exc_info: |
|
488 |
summary = traceback.format_exception_only(exc_info[0], exc_info[1])[-1] |
|
489 |
else: |
|
485 |
if exception is None: |
|
490 | 486 |
summary = '<no response>' |
491 | 487 |
if response is not None: |
492 | 488 |
summary = '%s %s' % (response.status_code, response.reason) |
493 |
try: |
|
494 |
raise Exception(summary) |
|
495 |
except Exception: |
|
496 |
exc_info = sys.exc_info() |
|
497 | ||
498 |
if self.notify_on_errors or self.record_on_errors: |
|
499 |
get_publisher().notify_of_exception( |
|
500 |
exc_info, context='[WSCALL]', notify=self.notify_on_errors, record=self.record_on_errors |
|
501 |
) |
|
489 |
else: |
|
490 |
exc_type, exc_value = sys.exc_info()[:2] |
|
491 |
summary = traceback.format_exception_only(exc_type, exc_value)[-1] |
|
492 | ||
493 |
get_publisher().record_error( |
|
494 |
error_summary=summary, |
|
495 |
exception=exception, |
|
496 |
context='[WSCALL]', |
|
497 |
notify=self.notify_on_errors, |
|
498 |
record=self.record_on_errors, |
|
499 |
) |
|
502 | 500 |
if self.record_errors and formdata.evolution: |
503 | 501 |
formdata.evolution[-1].add_part(JournalWsCallErrorPart(summary, self.label, data)) |
504 | 502 |
formdata.store() |
... | ... | |
510 | 508 |
# verify that target still exist |
511 | 509 |
try: |
512 | 510 |
self.parent.parent.get_status(action) |
513 |
except KeyError: |
|
514 |
try:
|
|
515 |
raise IndexError(
|
|
516 |
'reference to invalid status %r in workflow %r, status %r'
|
|
517 |
% (action, self.parent.parent.name, self.parent.name)
|
|
518 |
)
|
|
519 |
except IndexError:
|
|
520 |
get_publisher().notify_of_exception(sys.exc_info(), context='[WSCALL]')
|
|
521 |
raise AbortActionException()
|
|
511 |
except KeyError as e:
|
|
512 |
get_publisher().record_error(
|
|
513 |
'reference to invalid status %r in workflow %r, status %r'
|
|
514 |
% (action, self.parent.parent.name, self.parent.name),
|
|
515 |
exception=e,
|
|
516 |
context='[WSCALL]',
|
|
517 |
notify=True,
|
|
518 |
) |
|
519 |
raise AbortActionException() |
|
522 | 520 | |
523 | 521 |
formdata.status = 'wf-%s' % action |
524 | 522 |
formdata.store() |
wcs/workflows.py | ||
---|---|---|
21 | 21 |
import itertools |
22 | 22 |
import os |
23 | 23 |
import random |
24 |
import sys |
|
25 | 24 |
import time |
26 | 25 |
import uuid |
27 | 26 |
import xml.etree.ElementTree as ET |
... | ... | |
1354 | 1353 |
try: |
1355 | 1354 |
# noqa pylint: disable=eval-used |
1356 | 1355 |
anchor_date = eval(self.anchor_expression, get_publisher().get_global_eval_dict(), variables) |
1357 |
except Exception: |
|
1356 |
except Exception as e:
|
|
1358 | 1357 |
# get the variables in the locals() namespace so they are |
1359 | 1358 |
# displayed within the trace. |
1360 | 1359 |
expression = self.anchor_expression # noqa pylint: disable=unused-variable |
1361 | 1360 |
# noqa pylint: disable=unused-variable |
1362 | 1361 |
global_variables = get_publisher().get_global_eval_dict() |
1363 |
get_publisher().notify_of_exception(sys.exc_info(), context='[TIMEOUTS]')
|
|
1362 |
get_publisher().record_error(exception=e, context='[TIMEOUTS]', notify=True)
|
|
1364 | 1363 | |
1365 | 1364 |
# convert anchor_date to datetime.datetime() |
1366 | 1365 |
if isinstance(anchor_date, datetime.datetime): |
... | ... | |
1374 | 1373 |
elif isinstance(anchor_date, str) and anchor_date: |
1375 | 1374 |
try: |
1376 | 1375 |
anchor_date = get_as_datetime(anchor_date) |
1377 |
except ValueError: |
|
1378 |
get_publisher().notify_of_exception(sys.exc_info(), context='[TIMEOUTS]')
|
|
1376 |
except ValueError as e:
|
|
1377 |
get_publisher().record_error(exception=e, context='[TIMEOUTS]', notify=True)
|
|
1379 | 1378 |
anchor_date = None |
1380 | 1379 |
elif anchor_date: |
1381 | 1380 |
# timestamp |
1382 | 1381 |
try: |
1383 | 1382 |
anchor_date = datetime.datetime.fromtimestamp(anchor_date) |
1384 |
except TypeError: |
|
1385 |
get_publisher().notify_of_exception(sys.exc_info(), context='[TIMEOUTS]')
|
|
1383 |
except TypeError as e:
|
|
1384 |
get_publisher().record_error(exception=e, context='[TIMEOUTS]', notify=True)
|
|
1386 | 1385 |
anchor_date = None |
1387 | 1386 | |
1388 | 1387 |
if not anchor_date: |
... | ... | |
2365 | 2364 | |
2366 | 2365 |
try: |
2367 | 2366 |
attachment = WorkflowStatusItem.compute(attachment, allow_complex=True, raises=True) |
2368 |
except Exception: |
|
2369 |
get_publisher().notify_of_exception(sys.exc_info(), context='[workflow/attachments]')
|
|
2367 |
except Exception as e:
|
|
2368 |
get_publisher().record_error(exception=e, context='[workflow/attachments]', notify=True)
|
|
2370 | 2369 |
else: |
2371 | 2370 |
if attachment: |
2372 | 2371 |
complex_value = get_publisher().get_cached_complex_data(attachment) |
... | ... | |
2387 | 2386 |
# and magically convert string like 'form_var_*_raw' to a PicklableUpload |
2388 | 2387 |
# noqa pylint: disable=eval-used |
2389 | 2388 |
picklableupload = eval(attachment, global_eval_dict, local_eval_dict) |
2390 |
except Exception: |
|
2391 |
get_publisher().notify_of_exception(sys.exc_info(), context='[workflow/attachments]')
|
|
2389 |
except Exception as e:
|
|
2390 |
get_publisher().record_error(exception=e, context='[workflow/attachments]', notify=True)
|
|
2392 | 2391 |
continue |
2393 | 2392 | |
2394 | 2393 |
if not picklableupload: |
... | ... | |
2402 | 2401 |
if not isinstance(upload, PicklableUpload): |
2403 | 2402 |
try: |
2404 | 2403 |
upload = FileField.convert_value_from_anything(upload) |
2405 |
except ValueError: |
|
2406 |
get_publisher().notify_of_exception(sys.exc_info(), context='[workflow/attachments]')
|
|
2404 |
except ValueError as e:
|
|
2405 |
get_publisher().record_error(exception=e, context='[workflow/attachments]', notify=True)
|
|
2407 | 2406 |
continue |
2408 | 2407 | |
2409 | 2408 |
yield upload |
wcs/wscalls.py | ||
---|---|---|
16 | 16 | |
17 | 17 |
import collections |
18 | 18 |
import json |
19 |
import sys |
|
20 | 19 |
import urllib.parse |
21 | 20 |
import xml.etree.ElementTree as ET |
22 | 21 | |
... | ... | |
99 | 98 |
try: |
100 | 99 |
value = WorkflowStatusItem.compute(value, raises=True) |
101 | 100 |
value = str(value) |
102 |
except Exception: |
|
103 |
get_publisher().notify_of_exception(sys.exc_info())
|
|
101 |
except Exception as e:
|
|
102 |
get_publisher().record_error(exception=e, notify=True)
|
|
104 | 103 |
else: |
105 | 104 |
key = force_str(key) |
106 | 105 |
value = force_str(value) |
... | ... | |
130 | 129 |
for (key, value) in post_data.items(): |
131 | 130 |
try: |
132 | 131 |
payload[key] = WorkflowStatusItem.compute(value, allow_complex=True, raises=True) |
133 |
except Exception: |
|
134 |
get_publisher().notify_of_exception(sys.exc_info())
|
|
132 |
except Exception as e:
|
|
133 |
get_publisher().record_error(exception=e, notify=True)
|
|
135 | 134 |
else: |
136 | 135 |
if payload[key]: |
137 | 136 |
payload[key] = get_publisher().get_cached_complex_data(payload[key]) |
... | ... | |
163 | 162 |
except ConnectionError as e: |
164 | 163 |
if not handle_connection_errors: |
165 | 164 |
raise e |
166 |
if notify_on_errors or record_on_errors: |
|
167 |
exc_info = sys.exc_info() |
|
168 |
get_publisher().notify_of_exception( |
|
169 |
exc_info, context='[WSCALL]', notify=notify_on_errors, record=record_on_errors |
|
170 |
) |
|
165 |
get_publisher().record_error( |
|
166 |
exception=e, context='[WSCALL]', notify=notify_on_errors, record=record_on_errors |
|
167 |
) |
|
171 | 168 |
return (None, None, None) |
172 | 169 | |
173 | 170 |
app_error_code = get_app_error_code(response, data, 'json') |
... | ... | |
176 | 173 |
summary = '<no response>' |
177 | 174 |
if response is not None: |
178 | 175 |
summary = '%s %s' % (status, response.reason) |
179 |
try: |
|
180 |
raise Exception(summary) |
|
181 |
except Exception: |
|
182 |
exc_info = sys.exc_info() |
|
183 |
get_publisher().notify_of_exception( |
|
184 |
exc_info, context='[WSCALL]', notify=notify_on_errors, record=record_on_errors |
|
176 |
get_publisher().record_error( |
|
177 |
summary, context='[WSCALL]', notify=notify_on_errors, record=record_on_errors |
|
185 | 178 |
) |
186 | 179 | |
187 | 180 |
return (response, status, data) |
188 |
- |