changeset 2149:2e7e81a3133f

admin traceback emails
author Ana Balica <ana.balica@gmail.com>
date Sun, 14 Jul 2013 11:43:01 +0300
parents d6d3ad2ee913
children 48b752c4609c
files MoinMoin/log.py MoinMoin/signalling/log.py wikiconfig.py
diffstat 3 files changed, 85 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/log.py	Fri Jul 12 21:09:08 2013 +0300
+++ b/MoinMoin/log.py	Sun Jul 14 11:43:01 2013 +0300
@@ -70,14 +70,14 @@
 keys=root
 
 [handlers]
-keys=stderr
+keys=stderr,email
 
 [formatters]
 keys=default
 
 [logger_root]
 level=%(loglevel)s
-handlers=stderr
+handlers=stderr,email
 
 [handler_stderr]
 class=StreamHandler
@@ -85,6 +85,12 @@
 formatter=default
 args=(sys.stderr, )
 
+[handler_email]
+class=MoinMoin.log.EmailHandler
+level=ERROR
+formatter=default
+args=()
+
 [formatter_default]
 format=%(asctime)s %(levelname)s %(name)s:%(lineno)d %(message)s
 datefmt=
@@ -170,3 +176,69 @@
         if isinstance(levelnumber, int):  # that list has also the reverse mapping...
             setattr(logger, levelname, levelnumber)
     return logger
+
+
+def get_log_level(section_name, conf_fname=None):
+    """ Get from the config the log level of a section
+
+    :param conf_name: configuration filename path
+    :return: handler log level
+    """
+    conf_fname = os.environ.get('MOINLOGGINGCONF', conf_fname)
+    got_config = False
+    if conf_fname:
+        try:
+            conf_fname = os.path.abspath(conf_fname)
+            f = open(conf_fname)
+            got_config = True
+        except IOError as e:
+            logger = getLogger(__name__)
+            logger.warning('load_config for "{0}" failed with "{1}".'.format(conf_fname, str(e)))
+    if not got_config:
+        from StringIO import StringIO
+        f = StringIO(logging_config)
+
+    import ConfigParser
+    cp = ConfigParser.ConfigParser()
+    if hasattr(f, 'readline'):
+        cp.readfp(f)
+    else:
+        cp.read(f)
+    f.close()
+    if cp.has_option(section_name, 'level'):
+        return cp.get(section_name, 'level')
+
+
+class EmailHandler(logging.Handler):
+    """ A custom handler class which sends email for each logging event using
+    wiki mail configuration
+    """
+    def __init__(self, toaddrs=[], subject=''):
+        """ Initialize the handler
+
+        @param toaddrs: address or a list of email addresses whom to send email
+        @param subject: email's subject
+        """
+        logging.Handler.__init__(self)
+        if isinstance(toaddrs, basestring):
+            toaddrs = [toaddrs]
+        self.toaddrs = toaddrs
+        self.subject = subject
+
+    def emit(self, record):
+        """ Emit a record.
+
+        Send the record to the specified addresses
+        """
+        from MoinMoin.mail.sendmail import sendmail
+        from flask import current_app as app
+        cfg = app.cfg
+        # the app config is accessible after logging is initialized, so set the
+        # arguments and make the decision to send mail or not here
+        toaddrs = self.toaddrs if self.toaddrs else cfg.admin_emails
+        log_level = get_log_level('handler_email')
+        subject = self.subject if self.subject else '[{0}][{1}] Log message'.format(
+            cfg.sitename, log_level)
+        msg = self.format(record)
+        if app.cfg.email_tracebacks:
+            sendmail(subject, msg, to=toaddrs)
--- a/MoinMoin/signalling/log.py	Fri Jul 12 21:09:08 2013 +0300
+++ b/MoinMoin/signalling/log.py	Sun Jul 14 11:43:01 2013 +0300
@@ -7,6 +7,7 @@
 
 
 from .signals import *
+from flask import got_request_exception
 
 from MoinMoin import log
 logging = log.getLogger(__name__)
@@ -22,3 +23,8 @@
 def log_item_modified(app, item_name):
     wiki_name = app.cfg.interwikiname
     logging.info(u"item {0}:{1} modified".format(wiki_name, item_name))
+
+
+@got_request_exception.connect_via(ANY)
+def log_exception(sender, exception, **extra):
+    logging.exception(exception)
--- a/wikiconfig.py	Fri Jul 12 21:09:08 2013 +0300
+++ b/wikiconfig.py	Sun Jul 14 11:43:01 2013 +0300
@@ -77,6 +77,11 @@
         xs = XStatic(mod, root_url='/static', provider='local', protocol='http')
         serve_files.update([(xs.name, xs.base_dir)])
 
+    # list of admin emails
+    admin_emails = []
+    # send tracebacks to admins
+    email_tracebacks = False
+
 
 MOINCFG = Config  # Flask only likes uppercase stuff
 # Flask settings - see the flask documentation about their meaning