changeset 11382:d7d12ef788ba

add logging with indentation in graal.Debug
author Erik Eckstein <erik.eckstein@oracle.com>
date Tue, 20 Aug 2013 17:33:04 +0200
parents 1a110b7c03e1
children 423c53e2fa7e
files graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Indent.java graal/com.oracle.graal.debug/src/com/oracle/graal/debug/internal/DebugScope.java
diffstat 3 files changed, 293 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java	Tue Aug 20 15:39:58 2013 +0200
+++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java	Tue Aug 20 17:33:04 2013 +0200
@@ -36,6 +36,10 @@
         ENABLED = true;
     }
 
+    public static void disable() {
+        ENABLED = false;
+    }
+
     public static boolean isEnabled() {
         return ENABLED;
     }
@@ -158,12 +162,22 @@
         }
     }
 
+    /**
+     * Prints an indented message to the current DebugLevel's logging stream if logging is enabled.
+     * 
+     * @param msg The format string of the log message
+     * @param args The arguments referenced by the log message string
+     * @see Indent#log
+     */
     public static void log(String msg, Object... args) {
-        if (ENABLED && DebugScope.getInstance().isLogEnabled()) {
+        if (ENABLED) {
             DebugScope.getInstance().log(msg, args);
         }
     }
 
+    /**
+     * The same as {@link #log}, but without line termination and without indentation.
+     */
     public static void printf(String msg, Object... args) {
         if (ENABLED && DebugScope.getInstance().isLogEnabled()) {
             DebugScope.getInstance().printf(msg, args);
@@ -176,6 +190,105 @@
         }
     }
 
+    private static final class NoLogger implements Indent {
+
+        @Override
+        public void log(String msg, Object... args) {
+        }
+
+        @Override
+        public void setEnabled(boolean enabled) {
+        }
+
+        @Override
+        public Indent indent() {
+            return this;
+        }
+
+        @Override
+        public Indent logIndent(String msg, Object... args) {
+            return this;
+        }
+
+        @Override
+        public Indent outdent() {
+            return this;
+        }
+
+    }
+
+    private static final NoLogger noLoggerInstance = new NoLogger();
+
+    /**
+     * Creates a new indentation level (by adding some spaces) based on the last used Indent of the
+     * current DebugScope.
+     * 
+     * @return The new indentation level
+     * @see Indent#indent
+     */
+    public static Indent indent() {
+        if (ENABLED) {
+            DebugScope scope = DebugScope.getInstance();
+            return scope.pushIndentLogger();
+        }
+        return noLoggerInstance;
+    }
+
+    /**
+     * Creates a new indentation level based on the last used Indent of the current DebugScope and
+     * turns on/off logging.
+     * 
+     * @param enabled If true, logging is enabled, otherwise disabled
+     * @return The new indentation level
+     */
+    public static Indent indent(boolean enabled) {
+        if (ENABLED) {
+            Indent logger = DebugScope.getInstance().pushIndentLogger();
+            logger.setEnabled(enabled);
+            return logger;
+        }
+        return noLoggerInstance;
+    }
+
+    /**
+     * A convenience function which combines {@link #log} and {@link #indent()}.
+     * 
+     * @param msg The format string of the log message
+     * @param args The arguments referenced by the log message string
+     * @return The new indentation level
+     * @see Indent#logIndent
+     */
+    public static Indent logIndent(String msg, Object... args) {
+        if (ENABLED) {
+            DebugScope scope = DebugScope.getInstance();
+            scope.log(msg, args);
+            return scope.pushIndentLogger();
+        }
+        return noLoggerInstance;
+    }
+
+    /**
+     * A convenience function which combines {@link #log} and {@link #indent(boolean)}.
+     * 
+     * @param enabled If true, logging is enabled, otherwise disabled
+     * @param msg The format string of the log message
+     * @param args The arguments referenced by the log message string
+     * @return The new indentation level
+     */
+    public static Indent logIndent(boolean enabled, String msg, Object... args) {
+        if (ENABLED) {
+            DebugScope scope = DebugScope.getInstance();
+            boolean saveLogEnabled = scope.isLogEnabled();
+            scope.setLogEnabled(enabled);
+            scope.log(msg, args);
+            scope.setLogEnabled(saveLogEnabled);
+            Indent indent = scope.pushIndentLogger();
+            indent.setEnabled(enabled);
+            return indent;
+        }
+        return noLoggerInstance;
+    }
+
     public static Iterable<Object> context() {
         if (ENABLED) {
             return DebugScope.getInstance().getCurrentContext();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Indent.java	Tue Aug 20 17:33:04 2013 +0200
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.debug;
+
+/**
+ * Represents an indentation level for logging.
+ * <p>
+ * Note that calling the logging/indent/outdent methods of this interface updates the last used
+ * Indent of the current DebugScope. If no instance of Indent is available (e.g. at the beginning of
+ * a method), then the corresponding static methods of the Debug class should be used. They perform
+ * logging and indentation based on the last used Indent of the current DebugScope.
+ * <p>
+ * Example usage:
+ * 
+ * <pre>
+ *      void method() {
+ *      
+ *          Indent in = Debug.logIndent("header message");
+ *          ...
+ *          in.log("message");
+ *          ...
+ *          Indent in2 = in.logIndent("sub-header message");
+ *          ...
+ *          {
+ *              in2.log("inner message");
+ *          }
+ *          ...
+ *          in.outdent();
+ *      }
+ * </pre>
+ */
+public interface Indent {
+
+    /**
+     * Prints an indented message to the DebugLevel's logging stream if logging is enabled.
+     * 
+     * @param msg The format string of the log message
+     * @param args The arguments referenced by the log message string
+     * @see Debug#log
+     */
+    void log(String msg, Object... args);
+
+    /**
+     * Turns on/off logging for this indentation level.
+     * 
+     * @param enabled If true, logging is enabled, otherwise disabled
+     */
+    void setEnabled(boolean enabled);
+
+    /**
+     * Creates a new indentation level (by adding some spaces).
+     * 
+     * @return The new indentation level
+     * @see Debug#indent
+     */
+    Indent indent();
+
+    /**
+     * A convenience function which combines {@link #log} and {@link #indent}.
+     * 
+     * @param msg The format string of the log message
+     * @param args The arguments referenced by the log message string
+     * @return The new indentation level
+     * @see Debug#logIndent
+     */
+    Indent logIndent(String msg, Object... args);
+
+    /**
+     * Restores the previous indent level. Calling this method is important to restore the correct
+     * last used Indent in the current DebugScope.
+     * 
+     * @return The indent level from which this Indent was created.
+     */
+    Indent outdent();
+}
--- a/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/internal/DebugScope.java	Tue Aug 20 15:39:58 2013 +0200
+++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/internal/DebugScope.java	Tue Aug 20 17:33:04 2013 +0200
@@ -30,13 +30,64 @@
 
 public final class DebugScope {
 
+    private final class IndentImpl implements Indent {
+
+        final String indent;
+        boolean enabled;
+        final IndentImpl parentIndent;
+
+        IndentImpl(IndentImpl parentIndent, boolean enabled) {
+            this.parentIndent = parentIndent;
+            this.indent = (parentIndent == null ? "" : parentIndent.indent + "    ");
+            this.enabled = enabled;
+        }
+
+        @Override
+        public void log(String msg, Object... args) {
+            if (enabled) {
+                if (logScopeName) {
+                    output.println(indent + "scope: " + qualifiedName);
+                    logScopeName = false;
+                }
+                output.println(indent + String.format(msg, args));
+                lastUsedIndent = this;
+            }
+        }
+
+        @Override
+        public void setEnabled(boolean enabled) {
+            this.enabled = enabled;
+        }
+
+        @Override
+        public Indent indent() {
+            lastUsedIndent = new IndentImpl(this, enabled);
+            return lastUsedIndent;
+        }
+
+        @Override
+        public Indent logIndent(String msg, Object... args) {
+            log(msg, args);
+            return indent();
+        }
+
+        @Override
+        public Indent outdent() {
+            if (parentIndent != null) {
+                lastUsedIndent = parentIndent;
+            }
+            return lastUsedIndent;
+        }
+    }
+
     private static ThreadLocal<DebugScope> instanceTL = new ThreadLocal<>();
     private static ThreadLocal<DebugConfig> configTL = new ThreadLocal<>();
     private static ThreadLocal<Throwable> lastExceptionThrownTL = new ThreadLocal<>();
-    private static ThreadLocal<DebugScope> lastLogScope = new ThreadLocal<>();
     private static DebugTimer scopeTime = Debug.timer("ScopeTime");
 
     private final DebugScope parent;
+    private IndentImpl lastUsedIndent = null;
+    private boolean logScopeName = false;
 
     private Object[] context;
 
@@ -45,7 +96,6 @@
 
     private static final char SCOPE_SEP = '.';
 
-    private boolean logEnabled;
     private boolean meterEnabled;
     private boolean timeEnabled;
     private boolean dumpEnabled;
@@ -72,6 +122,17 @@
         this.parent = parent;
         this.context = context;
         this.qualifiedName = qualifiedName;
+        if (parent != null) {
+            this.lastUsedIndent = new IndentImpl(parent.lastUsedIndent, parent.isLogEnabled());
+            logScopeName = !parent.qualifiedName.equals(qualifiedName);
+        } else {
+            this.lastUsedIndent = new IndentImpl(null, false);
+            logScopeName = true;
+        }
+
+        // Be pragmatic: provide a default log stream to prevent a crash if the stream is not
+        // set while logging
+        this.output = System.out;
         assert context != null;
 
         if (parent != null) {
@@ -93,7 +154,11 @@
     }
 
     public boolean isLogEnabled() {
-        return logEnabled;
+        return lastUsedIndent.enabled;
+    }
+
+    public void setLogEnabled(boolean enabled) {
+        lastUsedIndent.setEnabled(enabled);
     }
 
     public boolean isMeterEnabled() {
@@ -105,20 +170,14 @@
     }
 
     public void log(String msg, Object... args) {
-        if (isLogEnabled()) {
-            if (lastLogScope.get() == null || !lastLogScope.get().qualifiedName.equals(this.qualifiedName)) {
-                output.println("scope: " + qualifiedName);
-                lastLogScope.set(this);
-            }
-            output.println(String.format(msg, args));
-        }
+        lastUsedIndent.log(msg, args);
     }
 
     public void printf(String msg, Object... args) {
         if (isLogEnabled()) {
-            if (lastLogScope.get() == null || !lastLogScope.get().qualifiedName.equals(this.qualifiedName)) {
+            if (logScopeName) {
                 output.println("scope: " + qualifiedName);
-                lastLogScope.set(this);
+                logScopeName = false;
             }
             output.printf(msg, args);
         }
@@ -164,6 +223,7 @@
     public <T> T scope(String newName, Runnable runnable, Callable<T> callable, boolean sandbox, DebugConfig sandboxConfig, Object[] newContext) {
         DebugScope oldContext = getInstance();
         DebugConfig oldConfig = getConfig();
+        boolean oldLogEnabled = oldContext.isLogEnabled();
         DebugScope newChild = null;
         if (sandbox) {
             newChild = new DebugScope(newName, newName, null, newContext);
@@ -173,12 +233,14 @@
         }
         instanceTL.set(newChild);
         newChild.updateFlags();
+        newChild.setLogEnabled(oldContext.isLogEnabled());
         try (TimerCloseable a = scopeTime.start()) {
             return executeScope(runnable, callable);
         } finally {
             newChild.context = null;
             instanceTL.set(oldContext);
             setConfig(oldConfig);
+            setLogEnabled(oldLogEnabled);
         }
     }
 
@@ -211,13 +273,14 @@
     private void updateFlags() {
         DebugConfig config = getConfig();
         if (config == null) {
-            logEnabled = false;
             meterEnabled = false;
             timeEnabled = false;
             dumpEnabled = false;
-            output = null;
+
+            // Be pragmatic: provide a default log stream to prevent a crash if the stream is not
+            // set while logging
+            output = System.out;
         } else {
-            logEnabled = config.isLogEnabled();
             meterEnabled = config.isMeterEnabled();
             timeEnabled = config.isTimeEnabled();
             dumpEnabled = config.isDumpEnabled();
@@ -321,9 +384,15 @@
     public void setConfig(DebugConfig newConfig) {
         configTL.set(newConfig);
         updateFlags();
+        setLogEnabled(newConfig != null && newConfig.isLogEnabled());
     }
 
     public String getQualifiedName() {
         return qualifiedName;
     }
+
+    public Indent pushIndentLogger() {
+        lastUsedIndent = (IndentImpl) lastUsedIndent.indent();
+        return lastUsedIndent;
+    }
 }