exception-tracker: enable send email on exception

This commit is contained in:
Marcin Kuzminski 2020-03-26 14:03:07 +01:00
parent 6a0c148cae
commit d0a117b4ad
7 changed files with 113 additions and 2 deletions

View file

@ -350,6 +350,17 @@ labs_settings_active = true
; This is used to store exception from RhodeCode in shared directory
#exception_tracker.store_path =
; Send email with exception details when it happens
#exception_tracker.send_email = false
; Comma separated list of recipients for exception emails,
; e.g admin@rhodecode.com,devops@rhodecode.com
; Can be left empty, then emails will be sent to ALL super-admins
#exception_tracker.send_email_recipients =
; optional prefix to Add to email Subject
#exception_tracker.email_prefix = [RHODECODE ERROR]
; File store configuration. This is used to store and serve uploaded files
file_store.enabled = true

View file

@ -301,6 +301,17 @@ labs_settings_active = true
; This is used to store exception from RhodeCode in shared directory
#exception_tracker.store_path =
; Send email with exception details when it happens
#exception_tracker.send_email = false
; Comma separated list of recipients for exception emails,
; e.g admin@rhodecode.com,devops@rhodecode.com
; Can be left empty, then emails will be sent to ALL super-admins
#exception_tracker.send_email_recipients =
; optional prefix to Add to email Subject
#exception_tracker.email_prefix = [RHODECODE ERROR]
; File store configuration. This is used to store and serve uploaded files
file_store.enabled = true

View file

@ -88,6 +88,15 @@ Check if we should use full-topic or mini-topic.
'modified': ['b/modified_file.rst'],
'removed': ['.idea'],
})
exc_traceback = {
'exc_utc_date': '2020-03-26T12:54:50.683281',
'exc_id': 139638856342656,
'exc_timestamp': '1585227290.683288',
'version': 'v1',
'exc_message': 'Traceback (most recent call last):\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/tweens.py", line 41, in excview_tween\n response = handler(request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/router.py", line 148, in handle_request\n registry, request, context, context_iface, view_name\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/view.py", line 667, in _call_view\n response = view_callable(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/config/views.py", line 188, in attr_view\n return view(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/config/views.py", line 214, in predicate_wrapper\n return view(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/viewderivers.py", line 401, in viewresult_to_response\n result = view(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/viewderivers.py", line 132, in _class_view\n response = getattr(inst, attr)()\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/apps/debug_style/views.py", line 355, in render_email\n template_type, **email_kwargs.get(email_id, {}))\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/model/notification.py", line 402, in render_email\n body = email_template.render(None, **_kwargs)\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/lib/partial_renderer.py", line 95, in render\n return self._render_with_exc(tmpl, args, kwargs)\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/lib/partial_renderer.py", line 79, in _render_with_exc\n return render_func.render(*args, **kwargs)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/template.py", line 476, in render\n return runtime._render(self, self.callable_, args, data)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/runtime.py", line 883, in _render\n **_kwargs_for_callable(callable_, data)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/runtime.py", line 920, in _render_context\n _exec_template(inherit, lclcontext, args=args, kwargs=kwargs)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/runtime.py", line 947, in _exec_template\n callable_(context, *args, **kwargs)\n File "rhodecode_templates_email_templates_base_mako", line 63, in render_body\n File "rhodecode_templates_email_templates_exception_tracker_mako", line 43, in render_body\nAttributeError: \'str\' object has no attribute \'get\'\n',
'exc_type': 'AttributeError'
}
email_kwargs = {
'test': {},
'message': {
@ -97,6 +106,13 @@ Check if we should use full-topic or mini-topic.
'user': user,
'date': datetime.datetime.now(),
},
'exception': {
'email_prefix': '[RHODECODE ERROR]',
'exc_id': exc_traceback['exc_id'],
'exc_url': 'http://server-url/{}'.format(exc_traceback['exc_id']),
'exc_type_name': 'NameError',
'exc_traceback': exc_traceback,
},
'password_reset': {
'password_reset_url': 'http://example.com/reset-rhodecode-password/token',

View file

@ -611,6 +611,14 @@ def _sanitize_cache_settings(settings):
settings,
'exception_tracker.store_path',
temp_store, lower=False, default_when_empty=True)
_bool_setting(
settings,
'exception_tracker.send_email',
'false')
_string_setting(
settings,
'exception_tracker.email_prefix',
'[RHODECODE ERROR]', lower=False, default_when_empty=True)
# cache_perms
_string_setting(

View file

@ -27,7 +27,6 @@ import traceback
import tempfile
import glob
log = logging.getLogger(__name__)
# NOTE: Any changes should be synced with exc_tracking at vcsserver.lib.exc_tracking
@ -77,10 +76,11 @@ def get_exc_store():
return _exc_store_path
def _store_exception(exc_id, exc_type_name, exc_traceback, prefix):
def _store_exception(exc_id, exc_type_name, exc_traceback, prefix, send_email=None):
"""
Low level function to store exception in the exception tracker
"""
import rhodecode as app
exc_store_path = get_exc_store()
exc_data, org_data = exc_serialize(exc_id, exc_traceback, exc_type_name)
@ -92,6 +92,50 @@ def _store_exception(exc_id, exc_type_name, exc_traceback, prefix):
f.write(exc_data)
log.debug('Stored generated exception %s as: %s', exc_id, stored_exc_path)
if send_email is None:
# NOTE(marcink): read app config unless we specify explicitly
send_email = app.CONFIG.get('exception_tracker.send_email', False)
if send_email:
try:
send_exc_email(exc_id, exc_type_name)
except Exception:
log.exception('Failed to send exception email')
pass
def send_exc_email(exc_id, exc_type_name):
import rhodecode as app
from pyramid.threadlocal import get_current_request
from rhodecode.apps._base import TemplateArgs
from rhodecode.lib.utils2 import aslist
from rhodecode.lib.celerylib import run_task, tasks
from rhodecode.lib.base import attach_context_attributes
from rhodecode.model.notification import EmailNotificationModel
request = get_current_request()
recipients = aslist(app.CONFIG.get('exception_tracker.send_email_recipients', ''))
log.debug('Sending Email exception to: `%s`', recipients or 'all super admins')
# NOTE(marcink): needed for email template rendering
attach_context_attributes(TemplateArgs(), request, request.user.user_id)
email_kwargs = {
'email_prefix': app.CONFIG.get('exception_tracker.email_prefix', '') or '[RHODECODE ERROR]',
'exc_url': request.route_url('admin_settings_exception_tracker_show', exception_id=exc_id),
'exc_id': exc_id,
'exc_type_name': exc_type_name,
'exc_traceback': read_exception(exc_id, prefix=None),
}
(subject, headers, email_body,
email_body_plaintext) = EmailNotificationModel().render_email(
EmailNotificationModel.TYPE_EMAIL_EXCEPTION, **email_kwargs)
run_task(tasks.send_email, recipients, subject,
email_body_plaintext, email_body)
def _prepare_exception(exc_info):
exc_type, exc_value, exc_traceback = exc_info

View file

@ -304,6 +304,7 @@ class EmailNotificationModel(BaseModel):
TYPE_PASSWORD_RESET = 'password_reset'
TYPE_PASSWORD_RESET_CONFIRMATION = 'password_reset_confirmation'
TYPE_EMAIL_TEST = 'email_test'
TYPE_EMAIL_EXCEPTION = 'exception'
TYPE_TEST = 'test'
email_types = {
@ -311,6 +312,8 @@ class EmailNotificationModel(BaseModel):
'rhodecode:templates/email_templates/main.mako',
TYPE_TEST:
'rhodecode:templates/email_templates/test.mako',
TYPE_EMAIL_EXCEPTION:
'rhodecode:templates/email_templates/exception_tracker.mako',
TYPE_EMAIL_TEST:
'rhodecode:templates/email_templates/email_test.mako',
TYPE_REGISTRATION:

View file

@ -0,0 +1,18 @@
## -*- coding: utf-8 -*-
<%inherit file="base.mako"/>
<%namespace name="base" file="base.mako"/>
<%def name="subject()" filter="n,trim,whitespace_filter">
${email_prefix} ${exc_type_name} (${exc_id})
</%def>
## plain text version of the email. Empty by default
<%def name="body_plaintext()" filter="n,trim">
NO PLAINTEXT VERSION
</%def>
<h4>${_('Exception `{}` generated on UTC date: {}').format(exc_traceback.get('exc_type', 'NO_TYPE'), exc_traceback.get('exc_utc_date', 'NO_DATE'))}</h4>
<p>
View exception <a href="${exc_url}">${exc_id}</a>
</p>
<pre>${exc_traceback.get('exc_message', 'NO_MESSAGE')}</pre>