I've attached a patch that adds a logException method to loggers, and a
couple other simple methods.  I've included tests, but I've only tested
on Firefox.  The names aren't particularly thought out.  A better
textForException function would also be nice, especially one that
formatted lines more nicely and gave relative path names for the
Javascript files (I find the full path names a bit tedious).
Index: tests/test_MochiKit-Logging.html
===================================================================
--- tests/test_MochiKit-Logging.html	(revision 459)
+++ tests/test_MochiKit-Logging.html	(working copy)
@@ -12,7 +12,7 @@
 try {
         
     // Counting the number of tests is really lame
-    plan({'tests': 27});
+    plan({'tests': 35});
    
     // just in case
     logger.clear();
@@ -83,7 +83,43 @@
 
     is( compare(firstTwo, logger.getMessages().slice(0, 2)), 0, "firstTwo" );
 
+    textForException = MochiKit.Logging.textForException;
+    try {
+        asdf;
+    } catch (e) {
+        msg = textForException(e);
+        ok( msg.indexOf('asdf') >= 0, "contains message");
+        ok( msg.indexOf('ReferenceError') >= 0, "contains exception");
+        ok( msg.indexOf('test_MochiKit-Logging.html') >= 0, "contains filename");
+    }
+
+    function badFunction() {
+        return 1+foo;
+    }
+    failed = false;
+    try {
+        badFunction();
+    } catch (e) {
+        failed = true;
+        msg = textForException(e);
+        ok( msg.indexOf('test_MochiKit-Logging.html') >= 0, "contains filename");
+        /* more? */
+    }
+    ok( failed, "badFunction failed");
     
+    try {
+        badFunction();
+    } catch (e) {
+        /* Just exercising */
+        logger.logException(e);
+    }
+
+    caughtFunction = MochiKit.Logging.catchException(badFunction);
+    ok( caughtFunction() === false, "catchException");
+    msgs = logger.getMessages();
+    msg = msgs.pop();
+    is( msg.level, "ERROR", "level ERROR");
+    ok( msg.info[0].indexOf('badFunction') >= 0, "logged exception");
    
     // Done!
 
Index: doc/rst/MochiKit/Logging.rst
===================================================================
--- doc/rst/MochiKit/Logging.rst	(revision 459)
+++ doc/rst/MochiKit/Logging.rst	(working copy)
@@ -206,7 +206,10 @@
 
     Remove a listener using the ident given to :mochiref:`Logger.prototype.addListener`
 
+:mochidef:`Logger.prototype.logException(exc)`:
 
+    Log the exception at level ERROR
+
 Functions
 ---------
 
@@ -240,7 +243,18 @@
 
     Log a WARNING message to the default logger
 
+:mochidef:`logException(exc)`:
 
+    Log the exception at level ERROR
+
+:mochidef:`textForException(exc)`:
+
+    Format the exception as a string
+
+:mochidef:`catchException(func)`:
+
+    Return a wrapped version of the function; if the function raises any exception, log that exception and return ``false``.  The return value is helpful when wrapping an event handler, e.g., ``<button onclick="return catchException(func)(...)">`` will always suppress the button submit
+
 See Also
 ========
 
Index: MochiKit/Logging.js
===================================================================
--- MochiKit/Logging.js	(revision 459)
+++ MochiKit/Logging.js	(working copy)
@@ -49,14 +49,16 @@
     "logError",
     "logDebug",
     "logFatal",
-    "logWarning"
+    "logWarning",
+    "logException"
 ];
 
 
 MochiKit.Logging.EXPORT_OK = [
     "logLevelAtLeast",
     "isLogMessage",
-    "compareLogMessage"
+    "compareLogMessage",
+    "textForException"
 ];
 
 
@@ -125,6 +127,33 @@
             "\nlevel: " +  msg.level +
             "\ninfo: " + msg.info.join(" ")
         );
+    },
+
+    textForException: function (exc) {
+        var s = exc.name + ': ' + exc.message + '\n';
+        s += exc.stack;
+        return s;
+        for (var i=0; i<exc.stack.length; i++) {
+            var frame = exc.stack[i];
+            s += frame + '\n';
+        }
+        return s;
+    },
+
+    catchException: function (func, /* optional */logger) {
+        function replacement() {
+            try {
+                return func.apply(this, arguments);
+            } catch (e) {
+                if (logger) {
+                    logger.logException(e);
+                } else {
+                    MochiKit.Logging.logException(e);
+                }
+                return false;
+            }
+        }
+        return replacement;
     }
 
 });
@@ -321,6 +350,10 @@
         } else {
             MochiKit.LoggingPane.createLoggingPane(inline || false);
         }
+    },
+
+    logException: function (exc) {
+        this.error(MochiKit.Logging.textForException(exc));
     }
 };
 
@@ -365,6 +398,7 @@
     this.logDebug = connectLog('debug');
     this.logFatal = connectLog('fatal');
     this.logWarning = connectLog('warning');
+    this.logException = connectLog('logException');
     this.logger = new Logger();
 
     this.EXPORT_TAGS = {

Reply via email to