diff --git a/MANIFEST.in b/MANIFEST.in index 4943929..acc928b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -19,3 +19,5 @@ recursive-include edbob/pyramid/templates *.mako recursive-include edbob/scaffolds/edbob *.py recursive-include edbob/scaffolds/edbob *_tmpl recursive-include edbob/scaffolds/edbob/+package+/pyramid/templates *.mako + +recursive-include edbob/templates *.mako diff --git a/edbob/errors.py b/edbob/errors.py index 4151350..5b74b12 100644 --- a/edbob/errors.py +++ b/edbob/errors.py @@ -26,6 +26,7 @@ ``edbob.errors`` -- Error Alert Emails """ +import os.path import sys import socket import logging @@ -33,6 +34,7 @@ from traceback import format_exception from cStringIO import StringIO import edbob +from edbob.files import resource_path from edbob.mail import sendmail_with_config @@ -60,15 +62,51 @@ def email_exception(type=None, value=None, traceback=None): if not (type and value and traceback): type, value, traceback = sys.exc_info() - body = StringIO() - hostname = socket.gethostname() - body.write("An exception occurred.\n") - body.write("\n") - body.write("Machine Name: %s (%s)\n" % (hostname, socket.gethostbyname(hostname))) - body.write("Local Time: %s\n" % (edbob.local_time().strftime('%Y-%m-%d %H:%M:%S %Z%z'))) - body.write("\n") - body.write("%s\n" % ''.join(format_exception(type, value, traceback))) + traceback = ''.join(format_exception(type, value, traceback)) + traceback = traceback.strip() + data = { + 'host_name': hostname, + 'host_ip': socket.gethostbyname(hostname), + 'host_time': edbob.local_time(), + 'traceback': traceback, + } - sendmail_with_config('errors', body.getvalue()) + body, ctype = render_exception(data) + sendmail_with_config('errors', body, content_type=ctype) + + +def render_exception(data): + """ + Renders the exception data using a Mako template if one is configured; + otherwise as a simple string. + """ + + template = edbob.config.get('edbob.errors', 'template') + if template: + template = resource_path(template) + if os.path.exists(template): + + # Assume Mako template; render and return. + from mako.template import Template + template = Template(filename=template) + return template.render(**data), 'text/plain' + + # If not a Mako template, return regular text with substitutions. + body = StringIO() + data['host_time'] = data['host_time'].strftime('%Y-%m-%d %H:%M:%S %Z%z') + + body.write("""\ +An unhandled exception occurred. + +Machine Name: %(host_name)s (%(host_ip)s) + +Machine Time: %(host_time)s + +%(traceback)s +""" % data) + + b = body.getvalue() body.close() + + return b, 'text/plain' diff --git a/edbob/files.py b/edbob/files.py index e50a8af..3cab3b4 100644 --- a/edbob/files.py +++ b/edbob/files.py @@ -33,6 +33,8 @@ import shutil import tempfile import lockfile +import pkg_resources + __all__ = ['temp_path'] @@ -98,6 +100,18 @@ def count_lines(path): return lines +def resource_path(path): + """ + Returns a resource file path. ``path`` is assumed either to be a package + resource, or a regular file path. In the latter case it is returned + unchanged. + """ + + if not os.path.isabs(path) and ':' in path: + return pkg_resources.resource_filename(*path.split(':')) + return path + + def temp_path(suffix='.tmp', prefix='edbob.'): """ Convenience function to return a temporary file path. The arguments' diff --git a/edbob/templates/errors/redmine.mako b/edbob/templates/errors/redmine.mako new file mode 100644 index 0000000..0188bd5 --- /dev/null +++ b/edbob/templates/errors/redmine.mako @@ -0,0 +1,9 @@ +An unhandled exception occurred. + +*Machine Name:* ${host_name} (${host_ip}) + +*Machine Time:* ${host_time.strftime('%Y-%m-%d %H:%M:%S %Z%z')} + +
+${traceback}
+