]> WPIA git - gigi.git/blobdiff - lib/jetty/org/eclipse/jetty/util/log/StdErrLog.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / util / log / StdErrLog.java
diff --git a/lib/jetty/org/eclipse/jetty/util/log/StdErrLog.java b/lib/jetty/org/eclipse/jetty/util/log/StdErrLog.java
new file mode 100644 (file)
index 0000000..fda1c72
--- /dev/null
@@ -0,0 +1,779 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.util.log;
+
+import java.io.PrintStream;
+import java.security.AccessControlException;
+import java.util.Properties;
+
+import org.eclipse.jetty.util.DateCache;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+
+/**
+ * StdErr Logging implementation. 
+ * <p>
+ * A Jetty {@link Logger} that sends all logs to STDERR ({@link System#err}) with basic formatting.
+ * <p>
+ * Supports named loggers, and properties based configuration. 
+ * <p>
+ * Configuration Properties:
+ * <dl>
+ *   <dt>${name|hierarchy}.LEVEL=(ALL|DEBUG|INFO|WARN|OFF)</dt>
+ *   <dd>
+ *   Sets the level that the Logger should log at.<br/>
+ *   Names can be a package name, or a fully qualified class name.<br/>
+ *   Default: INFO<br/>
+ *   <br/>
+ *   Examples:
+ *   <dl>
+ *   <dt>org.eclipse.jetty.LEVEL=WARN</dt>
+ *   <dd>indicates that all of the jetty specific classes, in any package that 
+ *   starts with <code>org.eclipse.jetty</code> should log at level WARN.</dd>
+ *   <dt>org.eclipse.jetty.io.ChannelEndPoint.LEVEL=ALL</dt>
+ *   <dd>indicates that the specific class, ChannelEndPoint, should log all
+ *   logging events that it can generate, including DEBUG, INFO, WARN (and even special
+ *   internally ignored exception cases).</dd>
+ *   </dl>  
+ *   </dd>
+ *   
+ *   <dt>${name}.SOURCE=(true|false)</dt>
+ *   <dd>
+ *   Logger specific, attempt to print the java source file name and line number
+ *   where the logging event originated from.<br/>
+ *   Name must be a fully qualified class name (package name hierarchy is not supported
+ *   by this configurable)<br/>
+ *   Warning: this is a slow operation and will have an impact on performance!<br/>
+ *   Default: false
+ *   </dd>
+ *   
+ *   <dt>${name}.STACKS=(true|false)</dt>
+ *   <dd>
+ *   Logger specific, control the display of stacktraces.<br/>
+ *   Name must be a fully qualified class name (package name hierarchy is not supported
+ *   by this configurable)<br/>
+ *   Default: true
+ *   </dd>
+ *   
+ *   <dt>org.eclipse.jetty.util.log.stderr.SOURCE=(true|false)</dt>
+ *   <dd>Special Global Configuration, attempt to print the java source file name and line number
+ *   where the logging event originated from.<br/>
+ *   Default: false
+ *   </dd>
+ *   
+ *   <dt>org.eclipse.jetty.util.log.stderr.LONG=(true|false)</dt>
+ *   <dd>Special Global Configuration, when true, output logging events to STDERR using
+ *   long form, fully qualified class names.  when false, use abbreviated package names<br/>
+ *   Default: false
+ *   </dd>
+ *   <dt>org.eclipse.jetty.util.log.stderr.ESCAPE=(true|false)</dt>
+ *   <dd>Global Configuration, when true output logging events to STDERR are always
+ *   escaped so that control characters are replaced with '?";  '\r' with '<' and '\n' replaced '|'<br/>
+ *   Default: true
+ *   </dd>
+ * </dl>
+ */
+@ManagedObject("Jetty StdErr Logging Implementation")
+public class StdErrLog extends AbstractLogger
+{
+    private static final String EOL = System.getProperty("line.separator");
+    private static DateCache _dateCache;
+    private static final Properties __props = new Properties();
+
+    private final static boolean __source = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.SOURCE",
+            Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.SOURCE","false")));
+    private final static boolean __long = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.LONG","false"));
+    private final static boolean __escape = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.ESCAPE","true"));
+
+    static
+    {
+        __props.putAll(Log.__props);
+
+        String deprecatedProperties[] =
+        { "DEBUG", "org.eclipse.jetty.util.log.DEBUG", "org.eclipse.jetty.util.log.stderr.DEBUG" };
+
+        // Toss a message to users about deprecated system properties
+        for (String deprecatedProp : deprecatedProperties)
+        {
+            if (System.getProperty(deprecatedProp) != null)
+            {
+                System.err.printf("System Property [%s] has been deprecated! (Use org.eclipse.jetty.LEVEL=DEBUG instead)%n",deprecatedProp);
+            }
+        }
+
+        try
+        {
+            _dateCache = new DateCache("yyyy-MM-dd HH:mm:ss");
+        }
+        catch (Exception x)
+        {
+            x.printStackTrace(System.err);
+        }
+    }
+
+    public static final int LEVEL_ALL = 0;
+    public static final int LEVEL_DEBUG = 1;
+    public static final int LEVEL_INFO = 2;
+    public static final int LEVEL_WARN = 3;
+    public static final int LEVEL_OFF = 10;
+
+    private int _level = LEVEL_INFO;
+    // Level that this Logger was configured as (remembered in special case of .setDebugEnabled())
+    private int _configuredLevel;
+    private PrintStream _stderr = null;
+    private boolean _source = __source;
+    // Print the long form names, otherwise use abbreviated
+    private boolean _printLongNames = __long;
+    // The full log name, as provided by the system.
+    private final String _name;
+    // The abbreviated log name (used by default, unless _long is specified)
+    private final String _abbrevname;
+    private boolean _hideStacks = false;
+
+    /**
+     * Obtain a StdErrLog reference for the specified class, a convenience method used most often during testing to allow for control over a specific logger.
+     * <p>
+     * Must be actively using StdErrLog as the Logger implementation.
+     * 
+     * @param clazz
+     *            the Class reference for the logger to use.
+     * @return the StdErrLog logger
+     * @throws RuntimeException
+     *             if StdErrLog is not the active Logger implementation.
+     */
+    public static StdErrLog getLogger(Class<?> clazz)
+    {
+        Logger log = Log.getLogger(clazz);
+        if (log instanceof StdErrLog)
+        {
+            return (StdErrLog)log;
+        }
+        throw new RuntimeException("Logger for " + clazz + " is not of type StdErrLog");
+    }
+
+    /**
+     * Construct an anonymous StdErrLog (no name).
+     * <p>
+     * NOTE: Discouraged usage!
+     */
+    public StdErrLog()
+    {
+        this(null);
+    }
+
+    /**
+     * Construct a named StdErrLog using the {@link Log} defined properties
+     * 
+     * @param name
+     *            the name of the logger
+     */
+    public StdErrLog(String name)
+    {
+        this(name,__props);
+    }
+
+    /**
+     * Construct a named Logger using the provided properties to configure logger.
+     * 
+     * @param name
+     *            the name of the logger
+     * @param props
+     *            the configuration properties
+     */
+    public StdErrLog(String name, Properties props)
+    {
+        if (props!=null && props!=__props)
+            __props.putAll(props);
+        this._name = name == null?"":name;
+        this._abbrevname = condensePackageString(this._name);
+        this._level = getLoggingLevel(props,this._name);
+        this._configuredLevel = this._level;
+
+        try
+        {
+            String source = getLoggingProperty(props,_name,"SOURCE");
+            _source = source==null?__source:Boolean.parseBoolean(source);
+        }
+        catch (AccessControlException ace)
+        {
+            _source = __source;
+        }
+
+        try
+        {
+            // allow stacktrace display to be controlled by properties as well
+            String stacks = getLoggingProperty(props,_name,"STACKS");
+            _hideStacks = stacks==null?false:!Boolean.parseBoolean(stacks);
+        }
+        catch (AccessControlException ignore)
+        {
+            /* ignore */
+        }        
+    }
+
+    /**
+     * Get the Logging Level for the provided log name. Using the FQCN first, then each package segment from longest to
+     * shortest.
+     *
+     * @param props
+     *            the properties to check
+     * @param name
+     *            the name to get log for
+     * @return the logging level
+     */
+    public static int getLoggingLevel(Properties props, final String name)
+    {
+        // Calculate the level this named logger should operate under.
+        // Checking with FQCN first, then each package segment from longest to shortest.
+        String nameSegment = name;
+
+        while ((nameSegment != null) && (nameSegment.length() > 0))
+        {
+            String levelStr = props.getProperty(nameSegment + ".LEVEL");
+            // System.err.printf("[StdErrLog.CONFIG] Checking for property [%s.LEVEL] = %s%n",nameSegment,levelStr);
+            int level = getLevelId(nameSegment + ".LEVEL",levelStr);
+            if (level != (-1))
+            {
+                return level;
+            }
+
+            // Trim and try again.
+            int idx = nameSegment.lastIndexOf('.');
+            if (idx >= 0)
+            {
+                nameSegment = nameSegment.substring(0,idx);
+            }
+            else
+            {
+                nameSegment = null;
+            }
+        }
+
+        // Default Logging Level
+        return getLevelId("log.LEVEL",props.getProperty("log.LEVEL","INFO"));
+    }
+    
+    public static String getLoggingProperty(Properties props, String name, String property)
+    {
+        // Calculate the level this named logger should operate under.
+        // Checking with FQCN first, then each package segment from longest to shortest.
+        String nameSegment = name;
+
+        while ((nameSegment != null) && (nameSegment.length() > 0))
+        {
+            String s = props.getProperty(nameSegment+"."+property);
+            if (s!=null)
+                return s;
+
+            // Trim and try again.
+            int idx = nameSegment.lastIndexOf('.');
+            nameSegment = (idx >= 0)?nameSegment.substring(0,idx):null;
+        }
+
+        return null;
+    }
+
+    protected static int getLevelId(String levelSegment, String levelName)
+    {
+        if (levelName == null)
+        {
+            return -1;
+        }
+        String levelStr = levelName.trim();
+        if ("ALL".equalsIgnoreCase(levelStr))
+        {
+            return LEVEL_ALL;
+        }
+        else if ("DEBUG".equalsIgnoreCase(levelStr))
+        {
+            return LEVEL_DEBUG;
+        }
+        else if ("INFO".equalsIgnoreCase(levelStr))
+        {
+            return LEVEL_INFO;
+        }
+        else if ("WARN".equalsIgnoreCase(levelStr))
+        {
+            return LEVEL_WARN;
+        }
+        else if ("OFF".equalsIgnoreCase(levelStr))
+        {
+            return LEVEL_OFF;
+        }
+
+        System.err.println("Unknown StdErrLog level [" + levelSegment + "]=[" + levelStr + "], expecting only [ALL, DEBUG, INFO, WARN, OFF] as values.");
+        return -1;
+    }
+
+    /**
+     * Condenses a classname by stripping down the package name to just the first character of each package name
+     * segment.Configured
+     * <p>
+     *
+     * <pre>
+     * Examples:
+     * "org.eclipse.jetty.test.FooTest"           = "oejt.FooTest"
+     * "org.eclipse.jetty.server.logging.LogTest" = "orjsl.LogTest"
+     * </pre>
+     *
+     * @param classname
+     *            the fully qualified class name
+     * @return the condensed name
+     */
+    protected static String condensePackageString(String classname)
+    {
+        String parts[] = classname.split("\\.");
+        StringBuilder dense = new StringBuilder();
+        for (int i = 0; i < (parts.length - 1); i++)
+        {
+            dense.append(parts[i].charAt(0));
+        }
+        if (dense.length() > 0)
+        {
+            dense.append('.');
+        }
+        dense.append(parts[parts.length - 1]);
+        return dense.toString();
+    }
+
+    public String getName()
+    {
+        return _name;
+    }
+
+    public void setPrintLongNames(boolean printLongNames)
+    {
+        this._printLongNames = printLongNames;
+    }
+
+    public boolean isPrintLongNames()
+    {
+        return this._printLongNames;
+    }
+
+    public boolean isHideStacks()
+    {
+        return _hideStacks;
+    }
+
+    public void setHideStacks(boolean hideStacks)
+    {
+        _hideStacks = hideStacks;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Is the source of a log, logged
+     *
+     * @return true if the class, method, file and line number of a log is logged.
+     */
+    public boolean isSource()
+    {
+        return _source;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set if a log source is logged.
+     *
+     * @param source
+     *            true if the class, method, file and line number of a log is logged.
+     */
+    public void setSource(boolean source)
+    {
+        _source = source;
+    }
+
+    public void warn(String msg, Object... args)
+    {
+        if (_level <= LEVEL_WARN)
+        {
+            StringBuilder buffer = new StringBuilder(64);
+            format(buffer,":WARN:",msg,args);
+            (_stderr==null?System.err:_stderr).println(buffer);
+        }
+    }
+
+    public void warn(Throwable thrown)
+    {
+        warn("",thrown);
+    }
+
+    public void warn(String msg, Throwable thrown)
+    {
+        if (_level <= LEVEL_WARN)
+        {
+            StringBuilder buffer = new StringBuilder(64);
+            format(buffer,":WARN:",msg,thrown);
+            (_stderr==null?System.err:_stderr).println(buffer);
+        }
+    }
+
+    public void info(String msg, Object... args)
+    {
+        if (_level <= LEVEL_INFO)
+        {
+            StringBuilder buffer = new StringBuilder(64);
+            format(buffer,":INFO:",msg,args);
+            (_stderr==null?System.err:_stderr).println(buffer);
+        }
+    }
+
+    public void info(Throwable thrown)
+    {
+        info("",thrown);
+    }
+
+    public void info(String msg, Throwable thrown)
+    {
+        if (_level <= LEVEL_INFO)
+        {
+            StringBuilder buffer = new StringBuilder(64);
+            format(buffer,":INFO:",msg,thrown);
+            (_stderr==null?System.err:_stderr).println(buffer);
+        }
+    }
+
+    @ManagedAttribute("is debug enabled for root logger Log.LOG")
+    public boolean isDebugEnabled()
+    {
+        return (_level <= LEVEL_DEBUG);
+    }
+
+    /**
+     * Legacy interface where a programmatic configuration of the logger level
+     * is done as a wholesale approach.
+     */
+    @Override
+    public void setDebugEnabled(boolean enabled)
+    {
+        if (enabled)
+        {
+            this._level = LEVEL_DEBUG;
+
+            for (Logger log : Log.getLoggers().values())
+            {                
+                if (log.getName().startsWith(getName()) && log instanceof StdErrLog)
+                    ((StdErrLog)log).setLevel(LEVEL_DEBUG);
+            }
+        }
+        else
+        {
+            this._level = this._configuredLevel;
+
+            for (Logger log : Log.getLoggers().values())
+            {
+                if (log.getName().startsWith(getName()) && log instanceof StdErrLog)
+                    ((StdErrLog)log).setLevel(((StdErrLog)log)._configuredLevel);
+            }
+        }
+    }
+
+    public int getLevel()
+    {
+        return _level;
+    }
+
+    /**
+     * Set the level for this logger.
+     * <p>
+     * Available values ({@link StdErrLog#LEVEL_ALL}, {@link StdErrLog#LEVEL_DEBUG}, {@link StdErrLog#LEVEL_INFO},
+     * {@link StdErrLog#LEVEL_WARN})
+     *
+     * @param level
+     *            the level to set the logger to
+     */
+    public void setLevel(int level)
+    {
+        this._level = level;
+    }
+
+    public void setStdErrStream(PrintStream stream)
+    {
+        this._stderr = stream==System.err?null:stream;
+    }
+
+    public void debug(String msg, Object... args)
+    {
+        if (_level <= LEVEL_DEBUG)
+        {
+            StringBuilder buffer = new StringBuilder(64);
+            format(buffer,":DBUG:",msg,args);
+            (_stderr==null?System.err:_stderr).println(buffer);
+        }
+    }
+
+    public void debug(String msg, long arg)
+    {
+        if (isDebugEnabled())
+        {
+            StringBuilder buffer = new StringBuilder(64);
+            format(buffer,":DBUG:",msg,arg);
+            (_stderr==null?System.err:_stderr).println(buffer);
+        }
+    }
+    
+    public void debug(Throwable thrown)
+    {
+        debug("",thrown);
+    }
+
+    public void debug(String msg, Throwable thrown)
+    {
+        if (_level <= LEVEL_DEBUG)
+        {
+            StringBuilder buffer = new StringBuilder(64);
+            format(buffer,":DBUG:",msg,thrown);
+            (_stderr==null?System.err:_stderr).println(buffer);
+        }
+    }
+
+    private void format(StringBuilder buffer, String level, String msg, Object... args)
+    {
+        long now = System.currentTimeMillis();
+        int ms=(int)(now%1000);
+        String d = _dateCache.formatNow(now);
+        tag(buffer,d,ms,level);
+        format(buffer,msg,args);
+    }
+
+    private void format(StringBuilder buffer, String level, String msg, Throwable thrown)
+    {
+        format(buffer,level,msg);
+        if (isHideStacks())
+        {
+            format(buffer,": "+String.valueOf(thrown));
+        }
+        else
+        {
+            format(buffer,thrown);
+        }
+    }
+
+    private void tag(StringBuilder buffer, String d, int ms, String tag)
+    {
+        buffer.setLength(0);
+        buffer.append(d);
+        if (ms > 99)
+        {
+            buffer.append('.');
+        }
+        else if (ms > 9)
+        {
+            buffer.append(".0");
+        }
+        else
+        {
+            buffer.append(".00");
+        }
+        buffer.append(ms).append(tag);
+        if (_printLongNames)
+        {
+            buffer.append(_name);
+        }
+        else
+        {
+            buffer.append(_abbrevname);
+        }
+        buffer.append(':');
+        buffer.append(Thread.currentThread().getName()).append(": ");
+        if (_source)
+        {
+            Throwable source = new Throwable();
+            StackTraceElement[] frames = source.getStackTrace();
+            for (int i = 0; i < frames.length; i++)
+            {
+                final StackTraceElement frame = frames[i];
+                String clazz = frame.getClassName();
+                if (clazz.equals(StdErrLog.class.getName()) || clazz.equals(Log.class.getName()))
+                {
+                    continue;
+                }
+                if (!_printLongNames && clazz.startsWith("org.eclipse.jetty."))
+                {
+                    buffer.append(condensePackageString(clazz));
+                }
+                else
+                {
+                    buffer.append(clazz);
+                }
+                buffer.append('#').append(frame.getMethodName());
+                if (frame.getFileName() != null)
+                {
+                    buffer.append('(').append(frame.getFileName()).append(':').append(frame.getLineNumber()).append(')');
+                }
+                buffer.append(':');
+                break;
+            }
+        }
+    }
+
+    private void format(StringBuilder builder, String msg, Object... args)
+    {
+        if (msg == null)
+        {
+            msg = "";
+            for (int i = 0; i < args.length; i++)
+            {
+                msg += "{} ";
+            }
+        }
+        String braces = "{}";
+        int start = 0;
+        for (Object arg : args)
+        {
+            int bracesIndex = msg.indexOf(braces,start);
+            if (bracesIndex < 0)
+            {
+                escape(builder,msg.substring(start));
+                builder.append(" ");
+                builder.append(arg);
+                start = msg.length();
+            }
+            else
+            {
+                escape(builder,msg.substring(start,bracesIndex));
+                builder.append(String.valueOf(arg));
+                start = bracesIndex + braces.length();
+            }
+        }
+        escape(builder,msg.substring(start));
+    }
+
+    private void escape(StringBuilder builder, String string)
+    {
+        if (__escape)
+        {
+            for (int i = 0; i < string.length(); ++i)
+            {
+                char c = string.charAt(i);
+                if (Character.isISOControl(c))
+                {
+                    if (c == '\n')
+                    {
+                        builder.append('|');
+                    }
+                    else if (c == '\r')
+                    {
+                        builder.append('<');
+                    }
+                    else
+                    {
+                        builder.append('?');
+                    }
+                }
+                else
+                {
+                    builder.append(c);
+                }
+            }
+        }
+        else
+            builder.append(string);
+    }
+
+    private void format(StringBuilder buffer, Throwable thrown)
+    {
+        if (thrown == null)
+        {
+            buffer.append("null");
+        }
+        else
+        {
+            buffer.append(EOL);
+            format(buffer,thrown.toString());
+            StackTraceElement[] elements = thrown.getStackTrace();
+            for (int i = 0; elements != null && i < elements.length; i++)
+            {
+                buffer.append(EOL).append("\tat ");
+                format(buffer,elements[i].toString());
+            }
+
+            Throwable cause = thrown.getCause();
+            if (cause != null && cause != thrown)
+            {
+                buffer.append(EOL).append("Caused by: ");
+                format(buffer,cause);
+            }
+        }
+    }
+
+
+    /**
+     * Create a Child Logger of this Logger.
+     */
+    @Override
+    protected Logger newLogger(String fullname)
+    {
+        StdErrLog logger = new StdErrLog(fullname);
+        // Preserve configuration for new loggers configuration
+        logger.setPrintLongNames(_printLongNames);
+        logger._stderr = this._stderr;
+
+        // Force the child to have any programmatic configuration
+        if (_level!=_configuredLevel)
+            logger._level=_level;
+
+        return logger;
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder s = new StringBuilder();
+        s.append("StdErrLog:");
+        s.append(_name);
+        s.append(":LEVEL=");
+        switch (_level)
+        {
+            case LEVEL_ALL:
+                s.append("ALL");
+                break;
+            case LEVEL_DEBUG:
+                s.append("DEBUG");
+                break;
+            case LEVEL_INFO:
+                s.append("INFO");
+                break;
+            case LEVEL_WARN:
+                s.append("WARN");
+                break;
+            default:
+                s.append("?");
+                break;
+        }
+        return s.toString();
+    }
+
+    public static void setProperties(Properties props)
+    {
+        __props.clear();
+        __props.putAll(props);
+    }
+
+    public void ignore(Throwable ignored)
+    {
+        if (_level <= LEVEL_ALL)
+        {
+            StringBuilder buffer = new StringBuilder(64);
+            format(buffer,":IGNORED:","",ignored);
+            (_stderr==null?System.err:_stderr).println(buffer);
+        }
+    }
+}