Commits

Miki Tebeka committed d39d0da

Starting

  • Participants
  • Tags v0.0.1

Comments (0)

Files changed (7)

+syntax: glob
+
+*.py[co]
+README.html
+__pycache__
+build
+crashlog.egg-info
+dist
+include test_crashlog.py README.md
+# crashlog 0.1.0
+
+Send email and log to file when there's an uncaught exception in your code
+
+
+# Install
+
+    pip install crashlog
+
+
+# Usage
+    
+    import crashlog
+
+    crashlog.install(email=['bugs@looney.org'], logfile='/path/to/log.txt')
+
+Contact
+=======
+Miki Tebeka <miki.tebeka@gmail.com>
+
+Bug reports go [here][bugs].
+
+
+[bugs]: https://bitbucket.org/tebeka/crashlog/issues
+'''Send email and log when there is an uncaught exception in your code
+Use in your code:
+
+    import crashlog
+    crashlog.install(emails=['daffy@duck.com'])
+'''
+
+__version__ = '0.0.1'
+
+import sys
+from smtplib import SMTP
+from email.mime.text import MIMEText
+from os import environ
+from traceback import format_exception
+if sys.version_info[0] >= 3:
+    from io import StringIO
+else:
+    from cStringIO import StringIO
+from time import ctime
+
+_EMAILS = []
+_LOGFILE = None
+_PREV_EXCEPTHOOK = None
+
+
+def send_email(emails, program, message):
+    message = MIMEText(message)
+    message['Subject'] = '{} crashed'.format(program)
+    crashlog_email = 'noreply@somewhere.com'
+    message['From'] = 'Crashlog <{}>'.format(crashlog_email)
+
+    smtp = SMTP('mailhost.somewhere.com')
+    smtp.sendmail(crashlog_email, _EMAILS, message.as_string())
+
+
+def format_message(type, value, traceback):
+    message = StringIO()
+    out = lambda m: message.write(u'{}\n'.format(m))
+
+    out(ctime())
+    out('== Traceback ==')
+    out(''.join(format_exception(type, value, traceback)))
+    out('\n== Command line ==')
+    out(' '.join(sys.argv))
+    out('\n== Environment ==')
+    for key, value in environ.items():
+        out('{} = {}'.format(key, value))
+
+    return message.getvalue()
+
+
+def excepthook(type, value, traceback):
+    try:
+        if not (_EMAILS or _LOGFILE):
+            return
+
+        message = format_message(type, value, traceback)
+        if _EMAILS:
+            send_email(_EMAILS, sys.argv[0], message)
+
+        if _LOGFILE:
+            with open(_LOGFILE, 'at') as fo:
+                fo.write('{}\n'.format(message))
+
+    finally:
+        if _PREV_EXCEPTHOOK:
+            _PREV_EXCEPTHOOK(type, value, traceback)
+
+
+def install(emails=None, logfile=None):
+    global _EMAILS, _PREV_EXCEPTHOOK, _LOGFILE
+
+    _EMAILS = emails or _EMAILS
+    _LOGFILE = logfile
+    _PREV_EXCEPTHOOK = sys.excepthook
+    sys.excepthook = excepthook

File run-tests.sh

+#!/bin/bash
+
+nosetests -vd .
+try:
+    from setuptools import setup
+except ImportError:
+    from distutils.core import setup
+
+import crashlog
+
+with open('README.md') as fo:
+    long_description=fo.read()
+
+setup(
+    name='crashlog',
+    version=crashlog.__version__,
+    description='Log & email uncaught exceptions',
+    long_description=long_description,
+    author='Miki Tebeka',
+    author_email='miki.tebeka@gmail.com',
+    license='MIT',
+    url='https://bitbucket.org/tebeka/crashlog',
+    py_modules=['crashlog'],
+    classifiers=[
+        'Development Status :: 4 - Beta',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: MIT License',
+        'Programming Language :: Python',
+        'Programming Language :: Python :: 2',
+        'Programming Language :: Python :: 3',
+        'Topic :: Software Development :: Libraries',
+        'Topic :: Software Development :: Libraries :: Python Modules',
+    ],
+    tests_require=['nose'],
+    test_suite = 'nose.collector',
+)

File test_crashlog.py

+import crashlog
+import sys
+
+
+def test_install():
+    emails = ['daffy@looney.org', 'bugs@looney.org']
+    logfile = '/path/to/logfile'
+
+    orig_emails = crashlog._EMAILS
+    orig_logfile = crashlog._LOGFILE
+
+    try:
+        crashlog.install(emails, logfile)
+        assert crashlog._EMAILS == emails, 'bad emails'
+        assert crashlog._LOGFILE == logfile, 'bad logfile'
+        assert sys.excepthook == crashlog.excepthook
+    finally:
+        crashlog._EMAILS = orig_emails
+        crashlog._LOGFILE = orig_logfile
+        sys.excepthook = crashlog._PREV_EXCEPTHOOK
+
+