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