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                 int level = getLevelId(levelStr);
145                 if (level != (-1))
146                 {
147                     return level;
148                 }
149             }
150         }
151 
152         // Default Logging Level
153         return getLevelId(props.getProperty("log.LEVEL", "INFO"));
154     }
155     
156     protected static int getLevelId(String levelName)
157     {
158         if (levelName == null)
159         {
160             return -1;
161         }
162         String levelStr = levelName.trim();
163         if ("ALL".equalsIgnoreCase(levelStr))
164         {
165             return LEVEL_ALL;
166         }
167         else if ("DEBUG".equalsIgnoreCase(levelStr))
168         {
169             return LEVEL_DEBUG;
170         }
171         else if ("INFO".equalsIgnoreCase(levelStr))
172         {
173             return LEVEL_INFO;
174         }
175         else if ("WARN".equalsIgnoreCase(levelStr))
176         {
177             return LEVEL_WARN;
178         }
179 
180         System.err.println("Unknown StdErrLog level [" + levelStr + "], expecting only [ALL, DEBUG, INFO, WARN] as values.");
181         return -1;
182     }
183 
184     /**
185      * Condenses a classname by stripping down the package name to just the first character of each package name
186      * segment.Configured
187      * <p>
188      * 
189      * <pre>
190      * Examples:
191      * "org.eclipse.jetty.test.FooTest"           = "oejt.FooTest"
192      * "org.eclipse.jetty.server.logging.LogTest" = "orjsl.LogTest"
193      * </pre>
194      * 
195      * @param classname
196      *            the fully qualified class name
197      * @return the condensed name
198      */
199     protected static String condensePackageString(String classname)
200     {
201         String parts[] = classname.split("\\.");
202         StringBuilder dense = new StringBuilder();
203         for (int i = 0; i < (parts.length - 1); i++)
204         {
205             dense.append(parts[i].charAt(0));
206         }
207         if (dense.length() > 0)
208         {
209             dense.append('.');
210         }
211         dense.append(parts[parts.length - 1]);
212         return dense.toString();
213     }
214 
215     public String getName()
216     {
217         return _name;
218     }
219 
220     public void setPrintLongNames(boolean printLongNames)
221     {
222         this._printLongNames = printLongNames;
223     }
224 
225     public boolean isPrintLongNames()
226     {
227         return this._printLongNames;
228     }
229 
230     public boolean isHideStacks()
231     {
232         return _hideStacks;
233     }
234 
235     public void setHideStacks(boolean hideStacks)
236     {
237         _hideStacks = hideStacks;
238     }
239 
240     /* ------------------------------------------------------------ */
241     /**
242      * Is the source of a log, logged
243      * 
244      * @return true if the class, method, file and line number of a log is logged.
245      */
246     public boolean isSource()
247     {
248         return _source;
249     }
250 
251     /* ------------------------------------------------------------ */
252     /**
253      * Set if a log source is logged.
254      * 
255      * @param source
256      *            true if the class, method, file and line number of a log is logged.
257      */
258     public void setSource(boolean source)
259     {
260         _source = source;
261     }
262 
263     public void warn(String msg, Object... args)
264     {
265         if (_level <= LEVEL_WARN)
266         {
267             StringBuilder buffer = new StringBuilder(64);
268             format(buffer,":WARN:",msg,args);
269             _stderr.println(buffer);
270         }
271     }
272 
273     public void warn(Throwable thrown)
274     {
275         warn("",thrown);
276     }
277 
278     public void warn(String msg, Throwable thrown)
279     {
280         if (_level <= LEVEL_WARN)
281         {
282             StringBuilder buffer = new StringBuilder(64);
283             format(buffer,":WARN:",msg,thrown);
284             _stderr.println(buffer);
285         }
286     }
287 
288     public void info(String msg, Object... args)
289     {
290         if (_level <= LEVEL_INFO)
291         {
292             StringBuilder buffer = new StringBuilder(64);
293             format(buffer,":INFO:",msg,args);
294             _stderr.println(buffer);
295         }
296     }
297 
298     public void info(Throwable thrown)
299     {
300         info("",thrown);
301     }
302 
303     public void info(String msg, Throwable thrown)
304     {
305         if (_level <= LEVEL_INFO)
306         {
307             StringBuilder buffer = new StringBuilder(64);
308             format(buffer,":INFO:",msg,thrown);
309             _stderr.println(buffer);
310         }
311     }
312 
313     public boolean isDebugEnabled()
314     {
315         return (_level >= LEVEL_DEBUG);
316     }
317 
318     /**
319      * @deprecated use {@link #setLevel(int)} instead.
320      */
321     @Deprecated
322     public void setDebugEnabled(boolean enabled)
323     {
324         if (enabled)
325         {
326             _level = LEVEL_DEBUG;
327         }
328         else
329         {
330             _level = LEVEL_INFO;
331         }
332     }
333 
334     public int getLevel()
335     {
336         return _level;
337     }
338 
339     /**
340      * Set the level for this logger.
341      * <p>
342      * Available values ({@link StdErrLog#LEVEL_ALL}, {@link StdErrLog#LEVEL_DEBUG}, {@link StdErrLog#LEVEL_INFO},
343      * {@link StdErrLog#LEVEL_WARN})
344      * 
345      * @param level
346      *            the level to set the logger to
347      */
348     public void setLevel(int level)
349     {
350         this._level = level;
351     }
352 
353     public void setStdErrStream(PrintStream stream)
354     {
355         this._stderr = stream;
356     }
357 
358     public void debug(String msg, Object... args)
359     {
360         if (_level <= LEVEL_DEBUG)
361         {
362             StringBuilder buffer = new StringBuilder(64);
363             format(buffer,":DBUG:",msg,args);
364             _stderr.println(buffer);
365         }
366     }
367 
368     public void debug(Throwable thrown)
369     {
370         debug("",thrown);
371     }
372 
373     public void debug(String msg, Throwable thrown)
374     {
375         if (_level <= LEVEL_DEBUG)
376         {
377             StringBuilder buffer = new StringBuilder(64);
378             format(buffer,":DBUG:",msg,thrown);
379             _stderr.println(buffer);
380         }
381     }
382 
383     private void format(StringBuilder buffer, String level, String msg, Object... args)
384     {
385         String d = _dateCache.now();
386         int ms = _dateCache.lastMs();
387         tag(buffer,d,ms,level);
388         format(buffer,msg,args);
389     }
390 
391     private void format(StringBuilder buffer, String level, String msg, Throwable thrown)
392     {
393         format(buffer,level,msg);
394         if (isHideStacks())
395         {
396             format(buffer,String.valueOf(thrown));
397         }
398         else
399         {
400             format(buffer,thrown);
401         }
402     }
403 
404     private void tag(StringBuilder buffer, String d, int ms, String tag)
405     {
406         buffer.setLength(0);
407         buffer.append(d);
408         if (ms > 99)
409         {
410             buffer.append('.');
411         }
412         else if (ms > 9)
413         {
414             buffer.append(".0");
415         }
416         else
417         {
418             buffer.append(".00");
419         }
420         buffer.append(ms).append(tag);
421         if (_printLongNames)
422         {
423             buffer.append(_name);
424         }
425         else
426         {
427             buffer.append(_abbrevname);
428         }
429         buffer.append(':');
430         if (_source)
431         {
432             Throwable source = new Throwable();
433             StackTraceElement[] frames = source.getStackTrace();
434             for (int i = 0; i < frames.length; i++)
435             {
436                 final StackTraceElement frame = frames[i];
437                 String clazz = frame.getClassName();
438                 if (clazz.equals(StdErrLog.class.getName()) || clazz.equals(Log.class.getName()))
439                 {
440                     continue;
441                 }
442                 if (!_printLongNames && clazz.startsWith("org.eclipse.jetty."))
443                 {
444                     buffer.append(condensePackageString(clazz));
445                 }
446                 else
447                 {
448                     buffer.append(clazz);
449                 }
450                 buffer.append('#').append(frame.getMethodName());
451                 if (frame.getFileName() != null)
452                 {
453                     buffer.append('(').append(frame.getFileName()).append(':').append(frame.getLineNumber()).append(')');
454                 }
455                 buffer.append(':');
456                 break;
457             }
458         }
459     }
460 
461     private void format(StringBuilder builder, String msg, Object... args)
462     {
463         if (msg == null)
464         {
465             msg = "";
466             for (int i = 0; i < args.length; i++)
467             {
468                 msg += "{} ";
469             }
470         }
471         String braces = "{}";
472         int start = 0;
473         for (Object arg : args)
474         {
475             int bracesIndex = msg.indexOf(braces,start);
476             if (bracesIndex < 0)
477             {
478                 escape(builder,msg.substring(start));
479                 builder.append(" ");
480                 builder.append(arg);
481                 start = msg.length();
482             }
483             else
484             {
485                 escape(builder,msg.substring(start,bracesIndex));
486                 builder.append(String.valueOf(arg));
487                 start = bracesIndex + braces.length();
488             }
489         }
490         escape(builder,msg.substring(start));
491     }
492 
493     private void escape(StringBuilder builder, String string)
494     {
495         for (int i = 0; i < string.length(); ++i)
496         {
497             char c = string.charAt(i);
498             if (Character.isISOControl(c))
499             {
500                 if (c == '\n')
501                 {
502                     builder.append('|');
503                 }
504                 else if (c == '\r')
505                 {
506                     builder.append('<');
507                 }
508                 else
509                 {
510                     builder.append('?');
511                 }
512             }
513             else
514             {
515                 builder.append(c);
516             }
517         }
518     }
519 
520     private void format(StringBuilder buffer, Throwable thrown)
521     {
522         if (thrown == null)
523         {
524             buffer.append("null");
525         }
526         else
527         {
528             buffer.append('\n');
529             format(buffer,thrown.toString());
530             StackTraceElement[] elements = thrown.getStackTrace();
531             for (int i = 0; elements != null && i < elements.length; i++)
532             {
533                 buffer.append("\n\tat ");
534                 format(buffer,elements[i].toString());
535             }
536 
537             Throwable cause = thrown.getCause();
538             if (cause != null && cause != thrown)
539             {
540                 buffer.append("\nCaused by: ");
541                 format(buffer,cause);
542             }
543         }
544     }
545 
546     public Logger getLogger(String name)
547     {
548         String fullname = _name == null || _name.length() == 0?name:_name + "." + name;
549 
550         if ((name == null && this._name == null) || fullname.equals(_name))
551         {
552             return this;
553         }
554 
555         StdErrLog logger = __loggers.get(name);
556         if (logger == null)
557         {
558             StdErrLog sel = new StdErrLog(fullname);
559             // Preserve configuration for new loggers configuration
560             sel.setPrintLongNames(_printLongNames);
561             // Let Level come from configured Properties instead - sel.setLevel(_level);
562             sel.setSource(_source);
563             logger = __loggers.putIfAbsent(fullname,sel);
564             if (logger == null)
565             {
566                 logger = sel;
567             }
568         }
569 
570         return logger;
571     }
572 
573     @Override
574     public String toString()
575     {
576         StringBuilder s = new StringBuilder();
577         s.append("StdErrLog:");
578         s.append(_name);
579         s.append(":LEVEL=");
580         switch (_level)
581         {
582             case LEVEL_ALL:
583                 s.append("ALL");
584                 break;
585             case LEVEL_DEBUG:
586                 s.append("DEBUG");
587                 break;
588             case LEVEL_INFO:
589                 s.append("INFO");
590                 break;
591             case LEVEL_WARN:
592                 s.append("WARN");
593                 break;
594             default:
595                 s.append("?");
596                 break;
597         }
598         return s.toString();
599     }
600 
601     public void ignore(Throwable ignored)
602     {
603         if (_level <= LEVEL_ALL)
604         {
605             StringBuilder buffer = new StringBuilder(64);
606             format(buffer,":IGNORED:","",ignored);
607             _stderr.println(buffer);
608         }
609     }
610 }