2 // ========================================================================
3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.util.log;
21 import java.io.PrintStream;
22 import java.security.AccessControlException;
23 import java.util.Properties;
25 import org.eclipse.jetty.util.DateCache;
26 import org.eclipse.jetty.util.annotation.ManagedAttribute;
27 import org.eclipse.jetty.util.annotation.ManagedObject;
30 * StdErr Logging implementation.
32 * A Jetty {@link Logger} that sends all logs to STDERR ({@link System#err}) with basic formatting.
34 * Supports named loggers, and properties based configuration.
36 * Configuration Properties:
38 * <dt>${name|hierarchy}.LEVEL=(ALL|DEBUG|INFO|WARN|OFF)</dt>
40 * Sets the level that the Logger should log at.<br/>
41 * Names can be a package name, or a fully qualified class name.<br/>
46 * <dt>org.eclipse.jetty.LEVEL=WARN</dt>
47 * <dd>indicates that all of the jetty specific classes, in any package that
48 * starts with <code>org.eclipse.jetty</code> should log at level WARN.</dd>
49 * <dt>org.eclipse.jetty.io.ChannelEndPoint.LEVEL=ALL</dt>
50 * <dd>indicates that the specific class, ChannelEndPoint, should log all
51 * logging events that it can generate, including DEBUG, INFO, WARN (and even special
52 * internally ignored exception cases).</dd>
56 * <dt>${name}.SOURCE=(true|false)</dt>
58 * Logger specific, attempt to print the java source file name and line number
59 * where the logging event originated from.<br/>
60 * Name must be a fully qualified class name (package name hierarchy is not supported
61 * by this configurable)<br/>
62 * Warning: this is a slow operation and will have an impact on performance!<br/>
66 * <dt>${name}.STACKS=(true|false)</dt>
68 * Logger specific, control the display of stacktraces.<br/>
69 * Name must be a fully qualified class name (package name hierarchy is not supported
70 * by this configurable)<br/>
74 * <dt>org.eclipse.jetty.util.log.stderr.SOURCE=(true|false)</dt>
75 * <dd>Special Global Configuration, attempt to print the java source file name and line number
76 * where the logging event originated from.<br/>
80 * <dt>org.eclipse.jetty.util.log.stderr.LONG=(true|false)</dt>
81 * <dd>Special Global Configuration, when true, output logging events to STDERR using
82 * long form, fully qualified class names. when false, use abbreviated package names<br/>
85 * <dt>org.eclipse.jetty.util.log.stderr.ESCAPE=(true|false)</dt>
86 * <dd>Global Configuration, when true output logging events to STDERR are always
87 * escaped so that control characters are replaced with '?"; '\r' with '<' and '\n' replaced '|'<br/>
92 @ManagedObject("Jetty StdErr Logging Implementation")
93 public class StdErrLog extends AbstractLogger
95 private static final String EOL = System.getProperty("line.separator");
96 private static DateCache _dateCache;
97 private static final Properties __props = new Properties();
99 private final static boolean __source = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.SOURCE",
100 Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.SOURCE","false")));
101 private final static boolean __long = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.LONG","false"));
102 private final static boolean __escape = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.ESCAPE","true"));
106 __props.putAll(Log.__props);
108 String deprecatedProperties[] =
109 { "DEBUG", "org.eclipse.jetty.util.log.DEBUG", "org.eclipse.jetty.util.log.stderr.DEBUG" };
111 // Toss a message to users about deprecated system properties
112 for (String deprecatedProp : deprecatedProperties)
114 if (System.getProperty(deprecatedProp) != null)
116 System.err.printf("System Property [%s] has been deprecated! (Use org.eclipse.jetty.LEVEL=DEBUG instead)%n",deprecatedProp);
122 _dateCache = new DateCache("yyyy-MM-dd HH:mm:ss");
126 x.printStackTrace(System.err);
130 public static final int LEVEL_ALL = 0;
131 public static final int LEVEL_DEBUG = 1;
132 public static final int LEVEL_INFO = 2;
133 public static final int LEVEL_WARN = 3;
134 public static final int LEVEL_OFF = 10;
136 private int _level = LEVEL_INFO;
137 // Level that this Logger was configured as (remembered in special case of .setDebugEnabled())
138 private int _configuredLevel;
139 private PrintStream _stderr = null;
140 private boolean _source = __source;
141 // Print the long form names, otherwise use abbreviated
142 private boolean _printLongNames = __long;
143 // The full log name, as provided by the system.
144 private final String _name;
145 // The abbreviated log name (used by default, unless _long is specified)
146 private final String _abbrevname;
147 private boolean _hideStacks = false;
150 * Obtain a StdErrLog reference for the specified class, a convenience method used most often during testing to allow for control over a specific logger.
152 * Must be actively using StdErrLog as the Logger implementation.
155 * the Class reference for the logger to use.
156 * @return the StdErrLog logger
157 * @throws RuntimeException
158 * if StdErrLog is not the active Logger implementation.
160 public static StdErrLog getLogger(Class<?> clazz)
162 Logger log = Log.getLogger(clazz);
163 if (log instanceof StdErrLog)
165 return (StdErrLog)log;
167 throw new RuntimeException("Logger for " + clazz + " is not of type StdErrLog");
171 * Construct an anonymous StdErrLog (no name).
173 * NOTE: Discouraged usage!
181 * Construct a named StdErrLog using the {@link Log} defined properties
184 * the name of the logger
186 public StdErrLog(String name)
192 * Construct a named Logger using the provided properties to configure logger.
195 * the name of the logger
197 * the configuration properties
199 public StdErrLog(String name, Properties props)
201 if (props!=null && props!=__props)
202 __props.putAll(props);
203 this._name = name == null?"":name;
204 this._abbrevname = condensePackageString(this._name);
205 this._level = getLoggingLevel(props,this._name);
206 this._configuredLevel = this._level;
210 String source = getLoggingProperty(props,_name,"SOURCE");
211 _source = source==null?__source:Boolean.parseBoolean(source);
213 catch (AccessControlException ace)
220 // allow stacktrace display to be controlled by properties as well
221 String stacks = getLoggingProperty(props,_name,"STACKS");
222 _hideStacks = stacks==null?false:!Boolean.parseBoolean(stacks);
224 catch (AccessControlException ignore)
231 * Get the Logging Level for the provided log name. Using the FQCN first, then each package segment from longest to
235 * the properties to check
237 * the name to get log for
238 * @return the logging level
240 public static int getLoggingLevel(Properties props, final String name)
242 // Calculate the level this named logger should operate under.
243 // Checking with FQCN first, then each package segment from longest to shortest.
244 String nameSegment = name;
246 while ((nameSegment != null) && (nameSegment.length() > 0))
248 String levelStr = props.getProperty(nameSegment + ".LEVEL");
249 // System.err.printf("[StdErrLog.CONFIG] Checking for property [%s.LEVEL] = %s%n",nameSegment,levelStr);
250 int level = getLevelId(nameSegment + ".LEVEL",levelStr);
256 // Trim and try again.
257 int idx = nameSegment.lastIndexOf('.');
260 nameSegment = nameSegment.substring(0,idx);
268 // Default Logging Level
269 return getLevelId("log.LEVEL",props.getProperty("log.LEVEL","INFO"));
272 public static String getLoggingProperty(Properties props, String name, String property)
274 // Calculate the level this named logger should operate under.
275 // Checking with FQCN first, then each package segment from longest to shortest.
276 String nameSegment = name;
278 while ((nameSegment != null) && (nameSegment.length() > 0))
280 String s = props.getProperty(nameSegment+"."+property);
284 // Trim and try again.
285 int idx = nameSegment.lastIndexOf('.');
286 nameSegment = (idx >= 0)?nameSegment.substring(0,idx):null;
292 protected static int getLevelId(String levelSegment, String levelName)
294 if (levelName == null)
298 String levelStr = levelName.trim();
299 if ("ALL".equalsIgnoreCase(levelStr))
303 else if ("DEBUG".equalsIgnoreCase(levelStr))
307 else if ("INFO".equalsIgnoreCase(levelStr))
311 else if ("WARN".equalsIgnoreCase(levelStr))
315 else if ("OFF".equalsIgnoreCase(levelStr))
320 System.err.println("Unknown StdErrLog level [" + levelSegment + "]=[" + levelStr + "], expecting only [ALL, DEBUG, INFO, WARN, OFF] as values.");
325 * Condenses a classname by stripping down the package name to just the first character of each package name
331 * "org.eclipse.jetty.test.FooTest" = "oejt.FooTest"
332 * "org.eclipse.jetty.server.logging.LogTest" = "orjsl.LogTest"
336 * the fully qualified class name
337 * @return the condensed name
339 protected static String condensePackageString(String classname)
341 String parts[] = classname.split("\\.");
342 StringBuilder dense = new StringBuilder();
343 for (int i = 0; i < (parts.length - 1); i++)
345 dense.append(parts[i].charAt(0));
347 if (dense.length() > 0)
351 dense.append(parts[parts.length - 1]);
352 return dense.toString();
355 public String getName()
360 public void setPrintLongNames(boolean printLongNames)
362 this._printLongNames = printLongNames;
365 public boolean isPrintLongNames()
367 return this._printLongNames;
370 public boolean isHideStacks()
375 public void setHideStacks(boolean hideStacks)
377 _hideStacks = hideStacks;
380 /* ------------------------------------------------------------ */
382 * Is the source of a log, logged
384 * @return true if the class, method, file and line number of a log is logged.
386 public boolean isSource()
391 /* ------------------------------------------------------------ */
393 * Set if a log source is logged.
396 * true if the class, method, file and line number of a log is logged.
398 public void setSource(boolean source)
403 public void warn(String msg, Object... args)
405 if (_level <= LEVEL_WARN)
407 StringBuilder buffer = new StringBuilder(64);
408 format(buffer,":WARN:",msg,args);
409 (_stderr==null?System.err:_stderr).println(buffer);
413 public void warn(Throwable thrown)
418 public void warn(String msg, Throwable thrown)
420 if (_level <= LEVEL_WARN)
422 StringBuilder buffer = new StringBuilder(64);
423 format(buffer,":WARN:",msg,thrown);
424 (_stderr==null?System.err:_stderr).println(buffer);
428 public void info(String msg, Object... args)
430 if (_level <= LEVEL_INFO)
432 StringBuilder buffer = new StringBuilder(64);
433 format(buffer,":INFO:",msg,args);
434 (_stderr==null?System.err:_stderr).println(buffer);
438 public void info(Throwable thrown)
443 public void info(String msg, Throwable thrown)
445 if (_level <= LEVEL_INFO)
447 StringBuilder buffer = new StringBuilder(64);
448 format(buffer,":INFO:",msg,thrown);
449 (_stderr==null?System.err:_stderr).println(buffer);
453 @ManagedAttribute("is debug enabled for root logger Log.LOG")
454 public boolean isDebugEnabled()
456 return (_level <= LEVEL_DEBUG);
460 * Legacy interface where a programmatic configuration of the logger level
461 * is done as a wholesale approach.
464 public void setDebugEnabled(boolean enabled)
468 this._level = LEVEL_DEBUG;
470 for (Logger log : Log.getLoggers().values())
472 if (log.getName().startsWith(getName()) && log instanceof StdErrLog)
473 ((StdErrLog)log).setLevel(LEVEL_DEBUG);
478 this._level = this._configuredLevel;
480 for (Logger log : Log.getLoggers().values())
482 if (log.getName().startsWith(getName()) && log instanceof StdErrLog)
483 ((StdErrLog)log).setLevel(((StdErrLog)log)._configuredLevel);
488 public int getLevel()
494 * Set the level for this logger.
496 * Available values ({@link StdErrLog#LEVEL_ALL}, {@link StdErrLog#LEVEL_DEBUG}, {@link StdErrLog#LEVEL_INFO},
497 * {@link StdErrLog#LEVEL_WARN})
500 * the level to set the logger to
502 public void setLevel(int level)
507 public void setStdErrStream(PrintStream stream)
509 this._stderr = stream==System.err?null:stream;
512 public void debug(String msg, Object... args)
514 if (_level <= LEVEL_DEBUG)
516 StringBuilder buffer = new StringBuilder(64);
517 format(buffer,":DBUG:",msg,args);
518 (_stderr==null?System.err:_stderr).println(buffer);
522 public void debug(String msg, long arg)
524 if (isDebugEnabled())
526 StringBuilder buffer = new StringBuilder(64);
527 format(buffer,":DBUG:",msg,arg);
528 (_stderr==null?System.err:_stderr).println(buffer);
532 public void debug(Throwable thrown)
537 public void debug(String msg, Throwable thrown)
539 if (_level <= LEVEL_DEBUG)
541 StringBuilder buffer = new StringBuilder(64);
542 format(buffer,":DBUG:",msg,thrown);
543 (_stderr==null?System.err:_stderr).println(buffer);
547 private void format(StringBuilder buffer, String level, String msg, Object... args)
549 long now = System.currentTimeMillis();
550 int ms=(int)(now%1000);
551 String d = _dateCache.formatNow(now);
552 tag(buffer,d,ms,level);
553 format(buffer,msg,args);
556 private void format(StringBuilder buffer, String level, String msg, Throwable thrown)
558 format(buffer,level,msg);
561 format(buffer,": "+String.valueOf(thrown));
565 format(buffer,thrown);
569 private void tag(StringBuilder buffer, String d, int ms, String tag)
583 buffer.append(".00");
585 buffer.append(ms).append(tag);
588 buffer.append(_name);
592 buffer.append(_abbrevname);
595 buffer.append(Thread.currentThread().getName()).append(": ");
598 Throwable source = new Throwable();
599 StackTraceElement[] frames = source.getStackTrace();
600 for (int i = 0; i < frames.length; i++)
602 final StackTraceElement frame = frames[i];
603 String clazz = frame.getClassName();
604 if (clazz.equals(StdErrLog.class.getName()) || clazz.equals(Log.class.getName()))
608 if (!_printLongNames && clazz.startsWith("org.eclipse.jetty."))
610 buffer.append(condensePackageString(clazz));
614 buffer.append(clazz);
616 buffer.append('#').append(frame.getMethodName());
617 if (frame.getFileName() != null)
619 buffer.append('(').append(frame.getFileName()).append(':').append(frame.getLineNumber()).append(')');
627 private void format(StringBuilder builder, String msg, Object... args)
632 for (int i = 0; i < args.length; i++)
637 String braces = "{}";
639 for (Object arg : args)
641 int bracesIndex = msg.indexOf(braces,start);
644 escape(builder,msg.substring(start));
647 start = msg.length();
651 escape(builder,msg.substring(start,bracesIndex));
652 builder.append(String.valueOf(arg));
653 start = bracesIndex + braces.length();
656 escape(builder,msg.substring(start));
659 private void escape(StringBuilder builder, String string)
663 for (int i = 0; i < string.length(); ++i)
665 char c = string.charAt(i);
666 if (Character.isISOControl(c))
688 builder.append(string);
691 private void format(StringBuilder buffer, Throwable thrown)
695 buffer.append("null");
700 format(buffer,thrown.toString());
701 StackTraceElement[] elements = thrown.getStackTrace();
702 for (int i = 0; elements != null && i < elements.length; i++)
704 buffer.append(EOL).append("\tat ");
705 format(buffer,elements[i].toString());
708 Throwable cause = thrown.getCause();
709 if (cause != null && cause != thrown)
711 buffer.append(EOL).append("Caused by: ");
712 format(buffer,cause);
719 * Create a Child Logger of this Logger.
722 protected Logger newLogger(String fullname)
724 StdErrLog logger = new StdErrLog(fullname);
725 // Preserve configuration for new loggers configuration
726 logger.setPrintLongNames(_printLongNames);
727 logger._stderr = this._stderr;
729 // Force the child to have any programmatic configuration
730 if (_level!=_configuredLevel)
731 logger._level=_level;
737 public String toString()
739 StringBuilder s = new StringBuilder();
740 s.append("StdErrLog:");
764 public static void setProperties(Properties props)
767 __props.putAll(props);
770 public void ignore(Throwable ignored)
772 if (_level <= LEVEL_ALL)
774 StringBuilder buffer = new StringBuilder(64);
775 format(buffer,":IGNORED:","",ignored);
776 (_stderr==null?System.err:_stderr).println(buffer);