View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.util.log;
20  
21  import java.io.PrintStream;
22  import java.security.AccessControlException;
23  import java.util.Properties;
24  
25  import org.eclipse.jetty.util.DateCache;
26  
27  /**
28   * StdErr Logging. This implementation of the Logging facade sends all logs to StdErr with minimal formatting.
29   * <p>
30   * If the system property "org.eclipse.jetty.LEVEL" is set to one of the following (ALL, DEBUG, INFO, WARN), then set
31   * the eclipse jetty root level logger level to that specified level. (Default level is INFO)
32   * <p>
33   * If the system property "org.eclipse.jetty.util.log.SOURCE" is set, then the source method/file of a log is logged.
34   * For named debuggers, the system property name+".SOURCE" is checked, eg "org.eclipse.jetty.util.log.stderr.SOURCE". 
35   * If it is not not set, then "org.eclipse.jetty.util.log.SOURCE" is used as the default.
36   * <p>
37   * If the system property "org.eclipse.jetty.util.log.stderr.LONG" is set, then the full, unabbreviated name of the logger is
38   * used for logging.
39   */
40  public class StdErrLog extends AbstractLogger
41  {
42      private static final String EOL = System.getProperty("line.separator");
43      private static DateCache _dateCache;
44      private static final Properties __props = new Properties();
45  
46      private final static boolean __source = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.SOURCE",
47              Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.SOURCE","false")));
48      private final static boolean __long = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.LONG","false"));
49  
50      static
51      {
52          __props.putAll(Log.__props);
53          
54          String deprecatedProperties[] =
55          { "DEBUG", "org.eclipse.jetty.util.log.DEBUG", "org.eclipse.jetty.util.log.stderr.DEBUG" };
56  
57          // Toss a message to users about deprecated system properties
58          for (String deprecatedProp : deprecatedProperties)
59          {
60              if (System.getProperty(deprecatedProp) != null)
61              {
62                  System.err.printf("System Property [%s] has been deprecated! (Use org.eclipse.jetty.LEVEL=DEBUG instead)%n",deprecatedProp);
63              }
64          }
65  
66          try
67          {
68              _dateCache = new DateCache("yyyy-MM-dd HH:mm:ss");
69          }
70          catch (Exception x)
71          {
72              x.printStackTrace(System.err);
73          }
74      }
75  
76      public static final int LEVEL_ALL = 0;
77      public static final int LEVEL_DEBUG = 1;
78      public static final int LEVEL_INFO = 2;
79      public static final int LEVEL_WARN = 3;
80  
81      private int _level = LEVEL_INFO;
82      // Level that this Logger was configured as (remembered in special case of .setDebugEnabled())
83      private int _configuredLevel;
84      private PrintStream _stderr = null;
85      private boolean _source = __source;
86      // Print the long form names, otherwise use abbreviated
87      private boolean _printLongNames = __long;
88      // The full log name, as provided by the system.
89      private final String _name;
90      // The abbreviated log name (used by default, unless _long is specified)
91      private final String _abbrevname;
92      private boolean _hideStacks = false;
93  
94      public StdErrLog()
95      {
96          this(null);
97      }
98  
99      public StdErrLog(String name)
100     {
101         this(name,__props);
102     }
103 
104     public StdErrLog(String name, Properties props)
105     {
106         if (props!=null && props!=__props)
107             __props.putAll(props);
108         this._name = name == null?"":name;
109         this._abbrevname = condensePackageString(this._name);
110         this._level = getLoggingLevel(props,this._name);
111         this._configuredLevel = this._level;
112 
113         try
114         {
115             _source = Boolean.parseBoolean(props.getProperty(_name + ".SOURCE",Boolean.toString(_source)));
116         }
117         catch (AccessControlException ace)
118         {
119             _source = __source;
120         }
121     }
122 
123     /**
124      * Get the Logging Level for the provided log name. Using the FQCN first, then each package segment from longest to
125      * shortest.
126      *
127      * @param props
128      *            the properties to check
129      * @param name
130      *            the name to get log for
131      * @return the logging level
132      */
133     public static int getLoggingLevel(Properties props, final String name)
134     {
135         // Calculate the level this named logger should operate under.
136         // Checking with FQCN first, then each package segment from longest to shortest.
137         String nameSegment = name;
138 
139         while ((nameSegment != null) && (nameSegment.length() > 0))
140         {
141             String levelStr = props.getProperty(nameSegment + ".LEVEL");
142             // System.err.printf("[StdErrLog.CONFIG] Checking for property [%s.LEVEL] = %s%n",nameSegment,levelStr);
143             int level = getLevelId(nameSegment + ".LEVEL",levelStr);
144             if (level != (-1))
145             {
146                 return level;
147             }
148 
149             // Trim and try again.
150             int idx = nameSegment.lastIndexOf('.');
151             if (idx >= 0)
152             {
153                 nameSegment = nameSegment.substring(0,idx);
154             }
155             else
156             {
157                 nameSegment = null;
158             }
159         }
160 
161         // Default Logging Level
162         return getLevelId("log.LEVEL",props.getProperty("log.LEVEL","INFO"));
163     }
164 
165     protected static int getLevelId(String levelSegment, String levelName)
166     {
167         if (levelName == null)
168         {
169             return -1;
170         }
171         String levelStr = levelName.trim();
172         if ("ALL".equalsIgnoreCase(levelStr))
173         {
174             return LEVEL_ALL;
175         }
176         else if ("DEBUG".equalsIgnoreCase(levelStr))
177         {
178             return LEVEL_DEBUG;
179         }
180         else if ("INFO".equalsIgnoreCase(levelStr))
181         {
182             return LEVEL_INFO;
183         }
184         else if ("WARN".equalsIgnoreCase(levelStr))
185         {
186             return LEVEL_WARN;
187         }
188 
189         System.err.println("Unknown StdErrLog level [" + levelSegment + "]=[" + levelStr + "], expecting only [ALL, DEBUG, INFO, WARN] as values.");
190         return -1;
191     }
192 
193     /**
194      * Condenses a classname by stripping down the package name to just the first character of each package name
195      * segment.Configured
196      * <p>
197      *
198      * <pre>
199      * Examples:
200      * "org.eclipse.jetty.test.FooTest"           = "oejt.FooTest"
201      * "org.eclipse.jetty.server.logging.LogTest" = "orjsl.LogTest"
202      * </pre>
203      *
204      * @param classname
205      *            the fully qualified class name
206      * @return the condensed name
207      */
208     protected static String condensePackageString(String classname)
209     {
210         String parts[] = classname.split("\\.");
211         StringBuilder dense = new StringBuilder();
212         for (int i = 0; i < (parts.length - 1); i++)
213         {
214             dense.append(parts[i].charAt(0));
215         }
216         if (dense.length() > 0)
217         {
218             dense.append('.');
219         }
220         dense.append(parts[parts.length - 1]);
221         return dense.toString();
222     }
223 
224     public String getName()
225     {
226         return _name;
227     }
228 
229     public void setPrintLongNames(boolean printLongNames)
230     {
231         this._printLongNames = printLongNames;
232     }
233 
234     public boolean isPrintLongNames()
235     {
236         return this._printLongNames;
237     }
238 
239     public boolean isHideStacks()
240     {
241         return _hideStacks;
242     }
243 
244     public void setHideStacks(boolean hideStacks)
245     {
246         _hideStacks = hideStacks;
247     }
248 
249     /* ------------------------------------------------------------ */
250     /**
251      * Is the source of a log, logged
252      *
253      * @return true if the class, method, file and line number of a log is logged.
254      */
255     public boolean isSource()
256     {
257         return _source;
258     }
259 
260     /* ------------------------------------------------------------ */
261     /**
262      * Set if a log source is logged.
263      *
264      * @param source
265      *            true if the class, method, file and line number of a log is logged.
266      */
267     public void setSource(boolean source)
268     {
269         _source = source;
270     }
271 
272     public void warn(String msg, Object... args)
273     {
274         if (_level <= LEVEL_WARN)
275         {
276             StringBuilder buffer = new StringBuilder(64);
277             format(buffer,":WARN:",msg,args);
278             (_stderr==null?System.err:_stderr).println(buffer);
279         }
280     }
281 
282     public void warn(Throwable thrown)
283     {
284         warn("",thrown);
285     }
286 
287     public void warn(String msg, Throwable thrown)
288     {
289         if (_level <= LEVEL_WARN)
290         {
291             StringBuilder buffer = new StringBuilder(64);
292             format(buffer,":WARN:",msg,thrown);
293             (_stderr==null?System.err:_stderr).println(buffer);
294         }
295     }
296 
297     public void info(String msg, Object... args)
298     {
299         if (_level <= LEVEL_INFO)
300         {
301             StringBuilder buffer = new StringBuilder(64);
302             format(buffer,":INFO:",msg,args);
303             (_stderr==null?System.err:_stderr).println(buffer);
304         }
305     }
306 
307     public void info(Throwable thrown)
308     {
309         info("",thrown);
310     }
311 
312     public void info(String msg, Throwable thrown)
313     {
314         if (_level <= LEVEL_INFO)
315         {
316             StringBuilder buffer = new StringBuilder(64);
317             format(buffer,":INFO:",msg,thrown);
318             (_stderr==null?System.err:_stderr).println(buffer);
319         }
320     }
321 
322     public boolean isDebugEnabled()
323     {
324         return (_level <= LEVEL_DEBUG);
325     }
326 
327     /**
328      * Legacy interface where a programmatic configuration of the logger level
329      * is done as a wholesale approach.
330      */
331     public void setDebugEnabled(boolean enabled)
332     {
333         if (enabled)
334         {
335             this._level = LEVEL_DEBUG;
336 
337             for (Logger log : Log.getLoggers().values())
338             {                
339                 if (log.getName().startsWith(getName()) && log instanceof StdErrLog)
340                     ((StdErrLog)log).setLevel(LEVEL_DEBUG);
341             }
342         }
343         else
344         {
345             this._level = this._configuredLevel;
346             
347             for (Logger log : Log.getLoggers().values())
348             {
349                 if (log.getName().startsWith(getName()) && log instanceof StdErrLog)
350                     ((StdErrLog)log).setLevel(((StdErrLog)log)._configuredLevel);
351             }
352         }
353     }
354 
355     public int getLevel()
356     {
357         return _level;
358     }
359 
360     /**
361      * Set the level for this logger.
362      * <p>
363      * Available values ({@link StdErrLog#LEVEL_ALL}, {@link StdErrLog#LEVEL_DEBUG}, {@link StdErrLog#LEVEL_INFO},
364      * {@link StdErrLog#LEVEL_WARN})
365      *
366      * @param level
367      *            the level to set the logger to
368      */
369     public void setLevel(int level)
370     {
371         this._level = level;
372     }
373 
374     public void setStdErrStream(PrintStream stream)
375     {
376         this._stderr = stream==System.err?null:stream;
377     }
378 
379     public void debug(String msg, Object... args)
380     {
381         if (_level <= LEVEL_DEBUG)
382         {
383             StringBuilder buffer = new StringBuilder(64);
384             format(buffer,":DBUG:",msg,args);
385             (_stderr==null?System.err:_stderr).println(buffer);
386         }
387     }
388 
389     public void debug(Throwable thrown)
390     {
391         debug("",thrown);
392     }
393 
394     public void debug(String msg, Throwable thrown)
395     {
396         if (_level <= LEVEL_DEBUG)
397         {
398             StringBuilder buffer = new StringBuilder(64);
399             format(buffer,":DBUG:",msg,thrown);
400             (_stderr==null?System.err:_stderr).println(buffer);
401         }
402     }
403 
404     private void format(StringBuilder buffer, String level, String msg, Object... args)
405     {
406         String d = _dateCache.now();
407         int ms = _dateCache.lastMs();
408         tag(buffer,d,ms,level);
409         format(buffer,msg,args);
410     }
411 
412     private void format(StringBuilder buffer, String level, String msg, Throwable thrown)
413     {
414         format(buffer,level,msg);
415         if (isHideStacks())
416         {
417             format(buffer,String.valueOf(thrown));
418         }
419         else
420         {
421             format(buffer,thrown);
422         }
423     }
424 
425     private void tag(StringBuilder buffer, String d, int ms, String tag)
426     {
427         buffer.setLength(0);
428         buffer.append(d);
429         if (ms > 99)
430         {
431             buffer.append('.');
432         }
433         else if (ms > 9)
434         {
435             buffer.append(".0");
436         }
437         else
438         {
439             buffer.append(".00");
440         }
441         buffer.append(ms).append(tag);
442         if (_printLongNames)
443         {
444             buffer.append(_name);
445         }
446         else
447         {
448             buffer.append(_abbrevname);
449         }
450         buffer.append(':');
451         if (_source)
452         {
453             Throwable source = new Throwable();
454             StackTraceElement[] frames = source.getStackTrace();
455             for (int i = 0; i < frames.length; i++)
456             {
457                 final StackTraceElement frame = frames[i];
458                 String clazz = frame.getClassName();
459                 if (clazz.equals(StdErrLog.class.getName()) || clazz.equals(Log.class.getName()))
460                 {
461                     continue;
462                 }
463                 if (!_printLongNames && clazz.startsWith("org.eclipse.jetty."))
464                 {
465                     buffer.append(condensePackageString(clazz));
466                 }
467                 else
468                 {
469                     buffer.append(clazz);
470                 }
471                 buffer.append('#').append(frame.getMethodName());
472                 if (frame.getFileName() != null)
473                 {
474                     buffer.append('(').append(frame.getFileName()).append(':').append(frame.getLineNumber()).append(')');
475                 }
476                 buffer.append(':');
477                 break;
478             }
479         }
480     }
481 
482     private void format(StringBuilder builder, String msg, Object... args)
483     {
484         if (msg == null)
485         {
486             msg = "";
487             for (int i = 0; i < args.length; i++)
488             {
489                 msg += "{} ";
490             }
491         }
492         String braces = "{}";
493         int start = 0;
494         for (Object arg : args)
495         {
496             int bracesIndex = msg.indexOf(braces,start);
497             if (bracesIndex < 0)
498             {
499                 escape(builder,msg.substring(start));
500                 builder.append(" ");
501                 builder.append(arg);
502                 start = msg.length();
503             }
504             else
505             {
506                 escape(builder,msg.substring(start,bracesIndex));
507                 builder.append(String.valueOf(arg));
508                 start = bracesIndex + braces.length();
509             }
510         }
511         escape(builder,msg.substring(start));
512     }
513 
514     private void escape(StringBuilder builder, String string)
515     {
516         for (int i = 0; i < string.length(); ++i)
517         {
518             char c = string.charAt(i);
519             if (Character.isISOControl(c))
520             {
521                 if (c == '\n')
522                 {
523                     builder.append('|');
524                 }
525                 else if (c == '\r')
526                 {
527                     builder.append('<');
528                 }
529                 else
530                 {
531                     builder.append('?');
532                 }
533             }
534             else
535             {
536                 builder.append(c);
537             }
538         }
539     }
540 
541     private void format(StringBuilder buffer, Throwable thrown)
542     {
543         if (thrown == null)
544         {
545             buffer.append("null");
546         }
547         else
548         {
549             buffer.append(EOL);
550             format(buffer,thrown.toString());
551             StackTraceElement[] elements = thrown.getStackTrace();
552             for (int i = 0; elements != null && i < elements.length; i++)
553             {
554                 buffer.append(EOL).append("\tat ");
555                 format(buffer,elements[i].toString());
556             }
557 
558             Throwable cause = thrown.getCause();
559             if (cause != null && cause != thrown)
560             {
561                 buffer.append(EOL).append("Caused by: ");
562                 format(buffer,cause);
563             }
564         }
565     }
566 
567 
568     /**
569      * Create a Child Logger of this Logger.
570      */
571     protected Logger newLogger(String fullname)
572     {
573         StdErrLog logger = new StdErrLog(fullname);
574         // Preserve configuration for new loggers configuration
575         logger.setPrintLongNames(_printLongNames);
576         // Let Level come from configured Properties instead - sel.setLevel(_level);
577         logger.setSource(_source);
578         logger._stderr = this._stderr;
579         
580         // Force the child to have any programmatic configuration
581         if (_level!=_configuredLevel)
582             logger._level=_level;
583 
584         return logger;
585     }
586 
587     @Override
588     public String toString()
589     {
590         StringBuilder s = new StringBuilder();
591         s.append("StdErrLog:");
592         s.append(_name);
593         s.append(":LEVEL=");
594         switch (_level)
595         {
596             case LEVEL_ALL:
597                 s.append("ALL");
598                 break;
599             case LEVEL_DEBUG:
600                 s.append("DEBUG");
601                 break;
602             case LEVEL_INFO:
603                 s.append("INFO");
604                 break;
605             case LEVEL_WARN:
606                 s.append("WARN");
607                 break;
608             default:
609                 s.append("?");
610                 break;
611         }
612         return s.toString();
613     }
614 
615     public static void setProperties(Properties props)
616     {
617         __props.clear();
618         __props.putAll(props);
619     }
620 
621     public void ignore(Throwable ignored)
622     {
623         if (_level <= LEVEL_ALL)
624         {
625             StringBuilder buffer = new StringBuilder(64);
626             format(buffer,":IGNORED:","",ignored);
627             (_stderr==null?System.err:_stderr).println(buffer);
628         }
629     }
630 }