View Javadoc

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