Clover coverage report - PLT Utilities Test Coverage (plt-20120304-r5436)
Coverage timestamp: Sat Mar 3 2012 22:01:56 CST
file stats: LOC: 527   Methods: 53
NCLOC: 330   Classes: 12
 
 Source file Conditionals Statements Methods TOTAL
DebugUtil.java 80% 50.8% 11.3% 51.6%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK*
 2   
 3    PLT Utilities BSD License
 4   
 5    Copyright (c) 2007-2010 JavaPLT group at Rice University
 6    All rights reserved.
 7   
 8    Developed by: Java Programming Languages Team
 9    Rice University
 10    http://www.cs.rice.edu/~javaplt/
 11   
 12    Redistribution and use in source and binary forms, with or without modification, are permitted
 13    provided that the following conditions are met:
 14   
 15    - Redistributions of source code must retain the above copyright notice, this list of conditions
 16    and the following disclaimer.
 17    - Redistributions in binary form must reproduce the above copyright notice, this list of
 18    conditions and the following disclaimer in the documentation and/or other materials provided
 19    with the distribution.
 20    - Neither the name of the JavaPLT group, Rice University, nor the names of the library's
 21    contributors may be used to endorse or promote products derived from this software without
 22    specific prior written permission.
 23   
 24    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 25    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 26    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND
 27    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 29    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 30    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 31    OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 32   
 33    *END_COPYRIGHT_BLOCK*/
 34   
 35    package edu.rice.cs.plt.debug;
 36   
 37    import java.io.File;
 38    import java.io.Serializable;
 39    import java.io.UnsupportedEncodingException;
 40    import java.util.ArrayList;
 41    import java.util.List;
 42    import java.util.Timer;
 43    import java.util.TimerTask;
 44   
 45    import edu.rice.cs.plt.iter.IterUtil;
 46    import edu.rice.cs.plt.iter.SizedIterable;
 47    import edu.rice.cs.plt.lambda.*;
 48    import edu.rice.cs.plt.text.Bracket;
 49    import edu.rice.cs.plt.text.TextUtil;
 50    import edu.rice.cs.plt.tuple.Pair;
 51    import edu.rice.cs.plt.reflect.ReflectUtil;
 52    import edu.rice.cs.plt.reflect.ReflectException;
 53   
 54    /** A collection of utility fields and methods to facilitate code-embedded debugging and logging */
 55    public final class DebugUtil {
 56   
 57    /** Prevents instance creation */
 58  0 private DebugUtil() {}
 59   
 60    /**
 61    * A globally-accessible debugging log, declared in the spirit of {@link System#out}. This log is intended to
 62    * record information that would be useful in a debugging session, but that is too detailed for typical program
 63    * executions. By default, its value is a {@link VoidLog}; logging behavior may be changed by setting the system
 64    * property {@code plt.debug.log}, or by directly setting this field (it is declared {@code volatile} so that
 65    * changes take immediate effect in all threads).
 66    *
 67    * @see #initializeLogs
 68    */
 69    public static volatile Log debug;
 70   
 71    /**
 72    * A globally-accessible error log, declared in the spirit of {@link System#out}. This log is intended to
 73    * record errors that should be noted, but that do not require the propagation of an exception. By default,
 74    * its value is a {@link VoidLog}; logging behavior may be changed by setting the system
 75    * property {@code plt.error.log}, or by directly setting this field (it is declared
 76    * {@code volatile} so that changes take immediate effect in all threads).
 77    *
 78    * @see #initializeLogs
 79    */
 80    public static volatile Log error;
 81   
 82  4 static { initializeLogs(); }
 83   
 84    /**
 85    * Initialize {@link #debug} and {@link #error} based on the descriptors appearing in system properties
 86    * {@code plt.debug.log} and {@code plt.error.log}. This method is run automatically when the
 87    * {@code DebugUtil} class is loaded. If desired, it may be re-invoked at any time.
 88    * @see #makeLog
 89    * @see #makeLogSink
 90    */
 91  4 public static void initializeLogs() {
 92  4 String debugProp = System.getProperty("plt.debug.log");
 93  4 debug = (debugProp == null) ? VoidLog.INSTANCE : makeLog(debugProp, "Debug");
 94  4 String errorProp = System.getProperty("plt.error.log");
 95  4 error = (errorProp == null) ? VoidLog.INSTANCE : makeLog(errorProp, "Error");
 96    }
 97   
 98    /**
 99    * Produce a Log corresponding to the given descriptor. If the descriptor is a valid sink descriptor,
 100    * a {@link StandardLog} is returned; otherwise, a {@link VoidLog} is returned. ({@code "void"} is a
 101    * convenient special case of an invalid sink descriptor.)
 102    * @see #makeLogSink
 103    */
 104  8 public static Log makeLog(String descriptor, String defaultName) {
 105  8 LogSink sink = makeLogSink(descriptor, defaultName);
 106  4 if (sink == null) { return VoidLog.INSTANCE; }
 107  4 else { return new StandardLog(sink); }
 108    }
 109   
 110    /**
 111    * <p>Produce a LogSink corresponding to the given descriptor. The descriptor is a sink type, with additional
 112    * support for asynchronous sinks, string parameters, filters, and splitting.
 113    * <ul>
 114    * <li>{@code <descriptor> := <descriptor>, <descriptor> | [~] <single-descriptor> <filter>}</li>
 115    * <li>{@code <single-descriptor> := <type> [:<parameter>] | (<descriptor>)}</li>
 116    * <li>{@code <type> := System.out | stdout | System.err | stderr | file | assert | popup | tree}</li>
 117    * <li>{@code <parameter> := <an arbitrary string parameter for the given sink type>}</li>
 118    * <li>{@code <filter> := ( +<loc-filter> | -<loc-filter> | +'<thread-filter>' | -'<thread-filter>' )* }</li>
 119    * <li>{@code <loc-filter> := <the prefix of fully-qualified method names to include or exclude>}</li>
 120    * <li>{@code <thread-filter> := <a substring of thread names to include or exclude>}</li>
 121    * </ul>
 122    * Examples:
 123    * <ul>
 124    * <li>{@code "stdout, stderr"}: Log to both {@code System.out} and {@code System.err}.</li>
 125    * <li>{@code "tree, ~file"}: Log synchronously to a {@link #remoteTreeLogSink} and
 126    * asynchronously to a file (named after {@code defaultName} and with location determined by the
 127    * {@code plt.log.working.dir} and {@code user.dir} properties).</li>
 128    * <li>{@code "file:pkg1.txt +pkg1, file:pkg2.txt +pkg2"}: Log messages from package {@code pkg1} to file
 129    * {@code pkg1.txt} and messages from package {@code pkg2} to file {@code pkg2.txt}.</li>
 130    * <li>{@code "(stdout:'UTF-8', stderr:'UTF-8') -'Foo'"}: Log all messages <em>except</em> those coming from
 131    * threads with {@code "Foo"} in their names to both {@code System.out} and {@code System.err}, encoded as
 132    * UTF-8 text. Note that apostrophes may be used to prevent interpreting delimiters appearing within
 133    * the sink's argument string.</li>
 134    * <li>{@code "assert +com -com.pkg1 -com.pkg2"}: Assert that no logging messages come from locations in
 135    * subpackages of {@code "com"}, but allowing an exception for messages from {@code "com.pkg1"} and
 136    * {@code "com.pkg2"}.</li>
 137    * </ul></p>
 138    *
 139    * <p>The set of supported types can be extended by defining the property {@code plt.log.factory} to point to
 140    * a static method. The method must have a signature matching the following (any method name can be used):
 141    * {@code LogSink factoryMethod(String type, String arg, String defaultName)}. The {@code arg} parameter
 142    * will be an empty string if none is provided by the descriptor. This factory method is invoked <em>before</em>
 143    * reverting to the default interpretations; to delegate to the default behavior, the method should return
 144    * {@code null}.</p>
 145    */
 146  28 public static LogSink makeLogSink(String descriptor, String defaultName) {
 147  28 String[] split = TextUtil.split(descriptor, ",", Bracket.PARENTHESES, Bracket.APOSTROPHES).array();
 148  28 List<LogSink> sinks = new ArrayList<LogSink>(split.length);
 149  28 for (String s : split) {
 150  33 LogSink sink = makeFilteredLogSink(s.trim(), defaultName);
 151  28 if (sink != null) { sinks.add(sink); }
 152    }
 153  5 if (sinks.isEmpty()) { return null; }
 154  18 else if (sinks.size() == 1) { return sinks.get(0); }
 155  5 else { return new SplitLogSink(sinks); }
 156    }
 157   
 158  33 private static LogSink makeFilteredLogSink(String descriptor, String defaultName) {
 159  33 TextUtil.SplitString split = TextUtil.split(descriptor, "\\+|-", Bracket.PARENTHESES, Bracket.APOSTROPHES);
 160  33 String desc;
 161  33 SizedIterable<Pair<String, String>> filters;
 162  26 if (split.splits().isEmpty()) { desc = descriptor; filters = IterUtil.empty(); }
 163    else {
 164  7 desc = split.splits().get(0).trim();
 165  7 Iterable<String> filterText = IterUtil.compose(IterUtil.skipFirst(split.splits()), split.rest());
 166  7 filters = IterUtil.zip(split.delimiters(), filterText);
 167    }
 168   
 169  33 LogSink result;
 170  33 if (desc.startsWith("(")) {
 171  1 if (desc.endsWith(")")) { result = makeLogSink(desc.substring(1, desc.length()-1), defaultName); }
 172  0 else { return null; } // malformed descriptor
 173    }
 174  32 else if (desc.startsWith("~")) {
 175  2 result = new AsynchronousLogSink(makeAtomicLogSink(desc.substring(1), defaultName));
 176    }
 177    else {
 178  30 result = makeAtomicLogSink(desc, defaultName);
 179    }
 180   
 181  33 if (!filters.isEmpty()) {
 182  7 List<String> whiteListLocs = new ArrayList<String>();
 183  7 List<String> blackListLocs = new ArrayList<String>();
 184  7 List<String> whiteListThreads = new ArrayList<String>();
 185  7 List<String> blackListThreads = new ArrayList<String>();
 186  7 for (Pair<String, String> p : filters) {
 187  11 String text = p.second().trim();
 188  11 boolean thread = text.startsWith("'");
 189  11 if (thread) {
 190  2 if (text.endsWith("'")) { text = text.substring(1, text.length()-1); }
 191  0 else { return null; } // malformed descriptor
 192    }
 193  11 if (p.first().equals("+")) {
 194  6 (thread ? whiteListThreads : whiteListLocs).add(text);
 195    }
 196  5 else if (p.first().equals("-")) {
 197  5 (thread ? blackListThreads : blackListLocs).add(text);
 198    }
 199  0 else { throw new RuntimeException("Bad delimiter from TextUtil.split: " + p.first()); }
 200    }
 201  7 if (!blackListLocs.isEmpty()) {
 202  2 result = FilteredLogSink.byLocationBlackList(result, IterUtil.toArray(blackListLocs, String.class));
 203    }
 204  7 if (!blackListThreads.isEmpty()) {
 205  1 result = FilteredLogSink.byThreadBlackList(result, IterUtil.toArray(blackListThreads, String.class));
 206    }
 207  7 if (!whiteListLocs.isEmpty()) {
 208  5 result = FilteredLogSink.byLocationWhiteList(result, IterUtil.toArray(whiteListLocs, String.class));
 209    }
 210  7 if (!whiteListThreads.isEmpty()) {
 211  1 result = FilteredLogSink.byThreadWhiteList(result, IterUtil.toArray(whiteListThreads, String.class));
 212    }
 213    }
 214  33 return result;
 215    }
 216   
 217  32 private static LogSink makeAtomicLogSink(String descriptor, String defaultName) {
 218  32 String[] split = TextUtil.split(descriptor, ":", 2, Bracket.PARENTHESES, Bracket.APOSTROPHES).array();
 219  32 String name = split[0].trim();
 220  32 String arg = (split.length > 1) ? split[1].trim() : "";
 221  2 if (arg.length() >= 2 && arg.startsWith("'") && arg.endsWith("'")) { arg = arg.substring(1, arg.length()-1); }
 222  32 LogSink result = null;
 223  32 String factoryName = System.getProperty("plt.log.factory");
 224  32 if (factoryName != null) {
 225  0 int dot = factoryName.lastIndexOf('.');
 226  0 if (dot >= 0) {
 227  0 String className = factoryName.substring(0, dot);
 228  0 String methodName = factoryName.substring(dot+1);
 229  0 try {
 230  0 result = (LogSink) ReflectUtil.invokeStaticMethod(className, methodName, name, arg, defaultName);
 231    }
 232    catch (ReflectException e) {
 233  0 System.err.println("Unable to invoke plt.log.factory: " + e.getCause());
 234    }
 235    catch (ClassCastException e) {
 236  0 System.err.println("Unable to invoke plt.log.factory: " + e);
 237    }
 238    }
 239    }
 240  32 if (result == null) {
 241  32 try {
 242  32 if (name.equals("System.out") || name.equals("stdout")) {
 243  4 if (arg.equals("")) { result = new SystemOutLogSink(); }
 244  1 else { result = new SystemOutLogSink(arg); }
 245    }
 246  27 else if (name.equals("System.err") || name.equals("stderr")) {
 247  4 if (arg.equals("")) { result = new SystemErrLogSink(); }
 248  1 else { result = new SystemErrLogSink(arg); }
 249    }
 250  22 else if (name.equals("file")) {
 251  2 if (arg.equals("")) { arg = defaultName.toLowerCase().replace(' ', '-') + "-log.txt"; }
 252  4 String workingDir = System.getProperty("plt.log.working.dir");
 253  4 if (workingDir == null) { result = new FileLogSink(arg); }
 254  0 else { result = new FileLogSink(new File(workingDir, arg)); }
 255    }
 256  18 else if (name.equals("assert")) {
 257  7 result = AssertEmptyLogSink.INSTANCE;
 258    }
 259  11 else if (name.equals("popup")) {
 260  4 if (arg.equals("")) { arg = defaultName; }
 261  4 result = new PopupLogSink(arg);
 262    }
 263  7 else if (name.equals("tree")) {
 264  2 if (arg.equals("")) { arg = defaultName; }
 265  2 result = remoteTreeLogSink(arg);
 266    }
 267    // else result remains null
 268    }
 269    catch (Exception e) { /* ignore; result is null */ }
 270    }
 271  32 return result;
 272    }
 273   
 274  0 public static Log voidLog() { return VoidLog.INSTANCE; }
 275  0 public static Log assertEmptyLog() { return new StandardLog(AssertEmptyLogSink.INSTANCE); }
 276  0 public static Log systemOutLog() { return new StandardLog(new SystemOutLogSink()); }
 277  0 public static Log systemOutLog(String charsetName) throws UnsupportedEncodingException {
 278  0 return new StandardLog(new SystemOutLogSink(charsetName));
 279    }
 280  0 public static Log systemErrLog() { return new StandardLog(new SystemErrLogSink()); }
 281  0 public static Log systemErrLog(String charsetName) throws UnsupportedEncodingException {
 282  0 return new StandardLog(new SystemErrLogSink(charsetName));
 283    }
 284  0 public static Log fileLog(String filename) { return new StandardLog(new FileLogSink(filename)); }
 285  0 public static Log fileLog(File f) { return new StandardLog(new FileLogSink(f)); }
 286  0 public static Log popupLog(String name) { return new StandardLog(new PopupLogSink(name)); }
 287  0 public static Log remoteTreeLog(String name) { return new StandardLog(remoteTreeLogSink(name)); }
 288   
 289    /**
 290    * Create an RMILogSink that passes messages to a remote TreeLogSink with {@code exitOnClose} set
 291    * to {@code true}.
 292    */
 293  2 public static LogSink remoteTreeLogSink(String name) {
 294  2 return new RMILogSink(TreeLogSink.factory(name, true), false);
 295    }
 296   
 297    /**
 298    * An alternative to the built-in {@code assert} statement that treats the assertion as an expression
 299    * rather than a statement. If assertions are enabled and the argument is {@code false}, this method will fail;
 300    * in any case, the value of the argument is returned. This allows code to be conditionally
 301    * executed when assertions are disabled. For example: {@code if (DebugUtil.check(x != null)) x.useMethod()}.
 302    * (Of course, unlike built-in assertions, calls to {@code check()} will <em>always</em> be executed, with
 303    * their associated overhead, whether assertions are enabled or not.)
 304    */
 305  0 public static boolean check(boolean assertion) {
 306  0 assert assertion;
 307  0 return assertion;
 308    }
 309   
 310    /**
 311    * Get the stack trace element representing the method immediately preceding the current method
 312    * on the stack. This gives methods the (dangerous, but useful for debugging) ability to "see"
 313    * who is calling them.
 314    * @throws IllegalStateException If the stack information is not available
 315    */
 316  0 public static StackTraceElement getCaller() {
 317    // Index 0 is this location; index 1 is the caller; index 2 is the caller's caller
 318    // (we avoid Thread.getStackTrace() because it includes itself and unspecified implementation frames
 319    // in the result)
 320  0 try { return new Throwable().getStackTrace()[2]; }
 321    catch (ArrayIndexOutOfBoundsException e) {
 322  0 throw new IllegalStateException("Stack trace information for caller is not available");
 323    }
 324    }
 325   
 326   
 327    /** A lazily-instantiated Timer for scheduling logging events. */
 328    private static final Thunk<Timer> LOG_TIMER = new LazyThunk<Timer>(new Thunk<Timer>() {
 329  0 public Timer value() {
 330  0 return new Timer("Delayed Log Timer", true);
 331    }
 332    });
 333   
 334    /**
 335    * Log the status (name, state, stack, etc.), of the current thread after a delay. Invokes {@link Log#logValues}
 336    * on the given log. Delayed logging events are scheduled on a daemon thread, and so may not run if
 337    * program execution terminates first.
 338    * @param log Log to write to.
 339    * @param delays A set of delays (in milliseconds) after which the thread should be logged. If empty, the
 340    * status is logged immediately. Otherwise, the status is logged after each specified delay.
 341    */
 342  0 public static void logThreadStatus(Log log, long... delays) {
 343  0 logThreadStatus(log, Thread.currentThread(), delays);
 344    }
 345   
 346    /**
 347    * Log the status (name, state, stack, etc.), of the given thread after a delay. Invokes {@link Log#logValues}
 348    * on the given log. Delayed logging events are scheduled on a daemon thread, and so may not run if
 349    * program execution terminates first.
 350    * @param thread The thread to observe.
 351    * @param log Log to write to.
 352    * @param delays A set of delays (in milliseconds) after which the thread should be logged. If empty, the
 353    * status is logged immediately. Otherwise, the status is logged after each specified delay.
 354    */
 355  0 public static void logThreadStatus(final Log log, final Thread thread, long... delays) {
 356    // LogTask can't be a singleton because each scheduled instance has distinct state for scheduling
 357    class LogTask extends TimerTask {
 358  0 public void run() {
 359  0 log.logValues(new String[]{ "thread", "state", "stack" },
 360    thread, thread.getState(), thread.getStackTrace());
 361    }
 362    }
 363  0 if (delays.length == 0) { new LogTask().run(); }
 364  0 for (final long delay : delays) {
 365  0 LOG_TIMER.value().schedule(new LogTask(), delay);
 366    }
 367    }
 368   
 369    /** Wrap a Runnable in a try-catch block that logs all caught {@code RuntimeException}s. */
 370  0 public static Runnable logExceptions(Log l, Runnable r) {
 371  0 return new LogExceptionRunnable(l, r);
 372    }
 373   
 374    private static final class LogExceptionRunnable implements Runnable, Serializable {
 375    private final Log _log;
 376    private final Runnable _r;
 377  0 public LogExceptionRunnable(Log log, Runnable r) { _log = log; _r = r; }
 378  0 public void run() {
 379  0 try { _r.run(); }
 380  0 catch (RuntimeException e) { _log.log(e); }
 381    }
 382    }
 383   
 384    /** Wrap a Runnable in a try-catch block that logs all caught {@code Throwable}s. */
 385  0 public static Runnable logThrowables(Log l, Runnable r) {
 386  0 return new LogThrowableRunnable(l, r);
 387    }
 388   
 389    private static final class LogThrowableRunnable implements Runnable, Serializable {
 390    private final Log _log;
 391    private final Runnable _r;
 392  0 public LogThrowableRunnable(Log log, Runnable r) { _log = log; _r = r; }
 393  0 public void run() {
 394  0 try { _r.run(); }
 395  0 catch (Throwable t) { _log.log(t); }
 396    }
 397    }
 398   
 399    /** Wrap a Runnable1 in a try-catch block that logs all caught {@code RuntimeException}s. */
 400  0 public static <T> Runnable1<T> logExceptions(Log l, Runnable1<? super T> r) {
 401  0 return new LogExceptionRunnable1<T>(l, r);
 402    }
 403   
 404    private static final class LogExceptionRunnable1<T> implements Runnable1<T>, Serializable {
 405    private final Log _log;
 406    private final Runnable1<? super T> _r;
 407  0 public LogExceptionRunnable1(Log log, Runnable1<? super T> r) { _log = log; _r = r; }
 408  0 public void run(T arg) {
 409  0 try { _r.run(arg); }
 410  0 catch (RuntimeException e) { _log.log(e); }
 411    }
 412    }
 413   
 414    /** Wrap a Runnable1 in a try-catch block that logs all caught {@code Throwable}s. */
 415  0 public static <T> Runnable1<T> logThrowables(Log l, Runnable1<? super T> r) {
 416  0 return new LogThrowableRunnable1<T>(l, r);
 417    }
 418   
 419    private static final class LogThrowableRunnable1<T> implements Runnable1<T>, Serializable {
 420    private final Log _log;
 421    private final Runnable1<? super T> _r;
 422  0 public LogThrowableRunnable1(Log log, Runnable1<? super T> r) { _log = log; _r = r; }
 423  0 public void run(T arg) {
 424  0 try { _r.run(arg); }
 425  0 catch (Throwable t) { _log.log(t); }
 426    }
 427    }
 428   
 429    /** Wrap a Runnable2 in a try-catch block that logs all caught {@code RuntimeException}s. */
 430  0 public static <T1, T2> Runnable2<T1, T2> logExceptions(Log l, Runnable2<? super T1, ? super T2> r) {
 431  0 return new LogExceptionRunnable2<T1, T2>(l, r);
 432    }
 433   
 434    private static final class LogExceptionRunnable2<T1, T2> implements Runnable2<T1, T2>, Serializable {
 435    private final Log _log;
 436    private final Runnable2<? super T1, ? super T2> _r;
 437  0 public LogExceptionRunnable2(Log log, Runnable2<? super T1, ? super T2> r) { _log = log; _r = r; }
 438  0 public void run(T1 arg1, T2 arg2) {
 439  0 try { _r.run(arg1, arg2); }
 440  0 catch (RuntimeException e) { _log.log(e); }
 441    }
 442    }
 443   
 444    /** Wrap a Runnable2 in a try-catch block that logs all caught {@code Throwable}s. */
 445  0 public static <T1, T2> Runnable2<T1, T2> logThrowables(Log l, Runnable2<? super T1, ? super T2> r) {
 446  0 return new LogThrowableRunnable2<T1, T2>(l, r);
 447    }
 448   
 449    private static final class LogThrowableRunnable2<T1, T2> implements Runnable2<T1, T2>, Serializable {
 450    private final Log _log;
 451    private final Runnable2<? super T1, ? super T2> _r;
 452  0 public LogThrowableRunnable2(Log log, Runnable2<? super T1, ? super T2> r) { _log = log; _r = r; }
 453  0 public void run(T1 arg1, T2 arg2) {
 454  0 try { _r.run(arg1, arg2); }
 455  0 catch (Throwable t) { _log.log(t); }
 456    }
 457    }
 458   
 459    /** Wrap a Runnable3 in a try-catch block that logs all caught {@code RuntimeException}s. */
 460  0 public static <T1, T2, T3>
 461    Runnable3<T1, T2, T3> logExceptions(Log l, Runnable3<? super T1, ? super T2, ? super T3> r) {
 462  0 return new LogExceptionRunnable3<T1, T2, T3>(l, r);
 463    }
 464   
 465    private static final class LogExceptionRunnable3<T1, T2, T3> implements Runnable3<T1, T2, T3>, Serializable {
 466    private final Log _log;
 467    private final Runnable3<? super T1, ? super T2, ? super T3> _r;
 468  0 public LogExceptionRunnable3(Log log, Runnable3<? super T1, ? super T2, ? super T3> r) { _log = log; _r = r; }
 469  0 public void run(T1 arg1, T2 arg2, T3 arg3) {
 470  0 try { _r.run(arg1, arg2, arg3); }
 471  0 catch (RuntimeException e) { _log.log(e); }
 472    }
 473    }
 474   
 475    /** Wrap a Runnable3 in a try-catch block that logs all caught {@code Throwable}s. */
 476  0 public static <T1, T2, T3>
 477    Runnable3<T1, T2, T3> logThrowables(Log l, Runnable3<? super T1, ? super T2, ? super T3> r) {
 478  0 return new LogThrowableRunnable3<T1, T2, T3>(l, r);
 479    }
 480   
 481    private static final class LogThrowableRunnable3<T1, T2, T3> implements Runnable3<T1, T2, T3>, Serializable {
 482    private final Log _log;
 483    private final Runnable3<? super T1, ? super T2, ? super T3> _r;
 484  0 public LogThrowableRunnable3(Log log, Runnable3<? super T1, ? super T2, ? super T3> r) { _log = log; _r = r; }
 485  0 public void run(T1 arg1, T2 arg2, T3 arg3) {
 486  0 try { _r.run(arg1, arg2, arg3); }
 487  0 catch (Throwable t) { _log.log(t); }
 488    }
 489    }
 490   
 491    /** Wrap a Runnable4 in a try-catch block that logs all caught {@code RuntimeException}s. */
 492  0 public static <T1, T2, T3, T4>
 493    Runnable4<T1, T2, T3, T4> logExceptions(Log l, Runnable4<? super T1, ? super T2, ? super T3, ? super T4> r) {
 494  0 return new LogExceptionRunnable4<T1, T2, T3, T4>(l, r);
 495    }
 496   
 497    private static final class LogExceptionRunnable4<T1, T2, T3, T4> implements Runnable4<T1, T2, T3, T4>, Serializable {
 498    private final Log _log;
 499    private final Runnable4<? super T1, ? super T2, ? super T3, ? super T4> _r;
 500  0 public LogExceptionRunnable4(Log log, Runnable4<? super T1, ? super T2, ? super T3, ? super T4> r) {
 501  0 _log = log; _r = r;
 502    }
 503  0 public void run(T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
 504  0 try { _r.run(arg1, arg2, arg3, arg4); }
 505  0 catch (RuntimeException e) { _log.log(e); }
 506    }
 507    }
 508   
 509    /** Wrap a Runnable4 in a try-catch block that logs all caught {@code Throwable}s. */
 510  0 public static <T1, T2, T3, T4>
 511    Runnable4<T1, T2, T3, T4> logThrowables(Log l, Runnable4<? super T1, ? super T2, ? super T3, ? super T4> r) {
 512  0 return new LogThrowableRunnable4<T1, T2, T3, T4>(l, r);
 513    }
 514   
 515    private static final class LogThrowableRunnable4<T1, T2, T3, T4> implements Runnable4<T1, T2, T3, T4>, Serializable {
 516    private final Log _log;
 517    private final Runnable4<? super T1, ? super T2, ? super T3, ? super T4> _r;
 518  0 public LogThrowableRunnable4(Log log, Runnable4<? super T1, ? super T2, ? super T3, ? super T4> r) {
 519  0 _log = log; _r = r;
 520    }
 521  0 public void run(T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
 522  0 try { _r.run(arg1, arg2, arg3, arg4); }
 523  0 catch (Throwable t) { _log.log(t); }
 524    }
 525    }
 526   
 527    }