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