001package daikon;
002
003import static java.util.logging.Level.INFO;
004
005import java.util.HashSet;
006import java.util.Set;
007import java.util.logging.ConsoleHandler;
008import java.util.logging.Formatter;
009import java.util.logging.Handler;
010import java.util.logging.Level;
011import java.util.logging.LogRecord;
012import java.util.logging.Logger;
013import java.util.logging.SimpleFormatter;
014
015/**
016 * Standard methods for setting up logging. Allows creation of Console writers using one method.
017 * Logger methods should only be called in a shell class at setup, after which Logger calls should
018 * be used for logging.
019 */
020public final class LogHelper {
021  private LogHelper() {
022    throw new Error("do not instantiate");
023  }
024
025  /**
026   * Sets up global logs with a given priority and logging output pattern. Creates one
027   * ConsoleHandler at root to receive default messages, setting priority to INFO. Removes previous
028   * appenders at root.
029   */
030  public static void setupLogs(Level l, Formatter formatter) {
031    // Send debug and other info messages to System.err
032    Handler app = new ConsoleHandler();
033    app.setLevel(Level.ALL);
034    app.setFormatter(formatter);
035
036    // Logger.global.removeAllAppenders();
037    {
038      // Java 5 version
039      @SuppressWarnings("deprecation")
040      Logger global = Logger.global;
041      // Java 6 version (doesn't work in Java 5)
042      // Logger global = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
043      Handler[] handlers = global.getHandlers();
044      for (Handler handler : handlers) {
045        global.removeHandler(handler);
046      }
047    }
048
049    Logger root = Logger.getLogger("");
050    Handler[] handlers = root.getHandlers();
051    for (Handler handler : handlers) {
052      root.removeHandler(handler);
053    }
054    root.addHandler(app);
055    root.setLevel(l);
056    allLoggers.add(root);
057
058    // Logger.global.addHandler(app);
059    // Logger.global.setLevel(l);
060    // Logger.global.fine ("Installed logger at level " + l);
061  }
062
063  public static class DaikonLogFormatter extends SimpleFormatter {
064    @Override
065    public synchronized String format(LogRecord record) {
066      // // By default, take up 20 spaces min, and 20 spaces max for logger.
067      // // %c = Logger. %m = message, %n = newline
068      // // Example output: "@ daikon.Daikon: This is a message"
069      // setupLogs (l, "@ %20.20c: %m%n");
070
071      String loggerName = record.getLoggerName() + ":";
072
073      // If we aren't generating tracebacks, find the src class/method/line
074      // where the log was called from
075      String src = "";
076      if (!Debug.dkconfig_showTraceback) {
077        Throwable stack = new Throwable("debug traceback");
078        stack.fillInStackTrace();
079        StackTraceElement[] ste_arr = stack.getStackTrace();
080        for (int ii = ste_arr.length - 1; ii >= 0; ii--) {
081          StackTraceElement ste = ste_arr[ii];
082          if (ste.getClassName().startsWith("java")
083              || ste.getClassName().contains("daikon.LogHelper")
084              || ste.getMethodName().equals("log")
085              || ste.getClassName().contains("daikon.Debug")) {
086            continue;
087          }
088          src = ste.getFileName() + " " + ste.getLineNumber() + ": ";
089        }
090      }
091      // src = record.getSourceClassName().replaceAll ("\\w*\\.", "")
092      //        + "." + record.getSourceMethodName() + ": ";
093
094      return "@ " + loggerName + " " + src + record.getMessage() + Global.lineSep;
095    }
096  }
097
098  /** Default method for setting up global logs. */
099  public static void setupLogs() {
100    setupLogs(INFO);
101  }
102
103  /**
104   * Sets up global logs with a given priority. Creates one ConsoleHandler. Removes previous
105   * appenders at root.
106   */
107  public static void setupLogs(Level l) {
108    setupLogs(l, new DaikonLogFormatter());
109  }
110
111  private static final Set<Logger> allLoggers = new HashSet<>();
112
113  /**
114   * Changes the logging priority of a sub category. Also caches the logger to avoid
115   * garbage-collection and recreation with the old level.
116   */
117  public static void setLevel(Logger lg, Level l) {
118    lg.setLevel(l);
119    allLoggers.add(lg); // to prevent garbage-collection and re-initialization
120  }
121
122  /**
123   * Changes the logging priority of a sub category. Also caches the logger to avoid
124   * garbage-collection and recreation with the old level.
125   */
126  public static void setLevel(String s, Level l) {
127    setLevel(Logger.getLogger(s), l);
128  }
129}