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.security.AccessControlException;
17  import java.util.concurrent.ConcurrentHashMap;
18  import java.util.concurrent.ConcurrentMap;
19  
20  import org.eclipse.jetty.util.DateCache;
21  
22  /**
23   * StdErr Logging. This implementation of the Logging facade sends all logs to
24   * StdErr with minimal formatting.
25   * <p>
26   * If the system property "org.eclipse.jetty.util.log.DEBUG" is set, then debug
27   * logs are printed if stderr is being used. For named debuggers, the system 
28   * property name+".DEBUG" is checked. If it is not not set, then 
29   * "org.eclipse.jetty.util.log.DEBUG" is used as the default.
30   * <p>
31   * If the system property "org.eclipse.jetty.util.log.SOURCE" is set, then the
32   * source method/file of a log is logged. For named debuggers, the system 
33   * property name+".SOURCE" is checked. If it is not not set, then 
34   * "org.eclipse.jetty.util.log.SOURCE" is used as the default.
35   * <p>
36   * If the system property "org.eclipse.jetty.util.log.LONG" is set, then the
37   * full, unabbreviated name of the logger is used for logging. 
38   * For named debuggers, the system property name+".LONG" is checked. 
39   * If it is not not set, then "org.eclipse.jetty.util.log.LONG" is used as the default.
40   */
41  public class StdErrLog implements Logger
42  {
43      private static DateCache _dateCache;
44  
45      private final static boolean __debug = Boolean.parseBoolean(
46              System.getProperty("org.eclipse.jetty.util.log.DEBUG",
47                      System.getProperty("org.eclipse.jetty.util.log.stderr.DEBUG", "false")));
48      private final static boolean __source = Boolean.parseBoolean(
49              System.getProperty("org.eclipse.jetty.util.log.SOURCE",
50                      System.getProperty("org.eclipse.jetty.util.log.stderr.SOURCE", "false")));
51      private final static boolean __long = Boolean.parseBoolean(
52              System.getProperty("org.eclipse.jetty.util.log.stderr.LONG", "false"));
53  
54      private final static ConcurrentMap<String,StdErrLog> __loggers = new ConcurrentHashMap<String, StdErrLog>();
55      
56      static
57      {
58          try
59          {
60              _dateCache = new DateCache("yyyy-MM-dd HH:mm:ss");
61          }
62          catch (Exception x)
63          {
64              x.printStackTrace();
65          }
66      }
67  
68      private boolean _debug = __debug;
69      private boolean _source = __source;
70      // Print the long form names, otherwise use abbreviated
71      private boolean _printLongNames = __long;
72      // The full log name, as provided by the system.
73      private final String _name; 
74      // The abbreviated log name (used by default, unless _long is specified)
75      private final String _abbrevname;
76      private boolean _hideStacks = false;
77  
78      public StdErrLog()
79      {
80          this(null);
81      }
82  
83      public StdErrLog(String name)
84      {
85          this._name = name == null ? "" : name;
86          this._abbrevname = condensePackageString(this._name);
87  
88          try
89          {
90              _debug = Boolean.parseBoolean(System.getProperty(_name + ".DEBUG", Boolean.toString(_debug)));
91          }
92          catch (AccessControlException ace)
93          {
94              _debug = __debug;
95          }
96          
97          try
98          {
99              _source = Boolean.parseBoolean(System.getProperty(_name + ".SOURCE", Boolean.toString(_source)));
100         }
101         catch (AccessControlException ace)
102         {
103             _source = __source;
104         }
105     }
106     
107     /**
108      * Condenses a classname by stripping down the package name to just the first character of each package name
109      * segment.
110      * <p>
111      * 
112      * <pre>
113      * Examples:
114      * "org.eclipse.jetty.test.FooTest"           = "oejt.FooTest"
115      * "org.eclipse.jetty.server.logging.LogTest" = "orjsl.LogTest"
116      * </pre>
117      * 
118      * @param classname
119      *            the fully qualified class name
120      * @return the condensed name
121      */
122     protected static String condensePackageString(String classname)
123     {
124         String parts[] = classname.split("\\.");
125         StringBuilder dense = new StringBuilder();
126         for (int i = 0; i < (parts.length - 1); i++)
127         {
128             dense.append(parts[i].charAt(0));
129         }
130         if (dense.length() > 0)
131         {
132             dense.append('.');
133         }
134         dense.append(parts[parts.length - 1]);
135         return dense.toString();
136     }
137 
138     public String getName()
139     {
140         return _name;
141     }
142     
143     public void setPrintLongNames(boolean printLongNames)
144     {
145         this._printLongNames = printLongNames;
146     }
147 
148     public boolean isPrintLongNames()
149     {
150         return this._printLongNames;
151     }
152 
153     public boolean isHideStacks()
154     {
155         return _hideStacks;
156     }
157 
158     public void setHideStacks(boolean hideStacks)
159     {
160         _hideStacks = hideStacks;
161     }
162 
163     /* ------------------------------------------------------------ */
164     /** Is the source of a log, logged
165      * @return true if the class, method, file and line number of a log is logged.
166      */
167     public boolean isSource()
168     {
169         return _source;
170     }
171 
172     /* ------------------------------------------------------------ */
173     /** Set if a log source is logged.
174      * @param source true if the class, method, file and line number of a log is logged.
175      */
176     public void setSource(boolean source)
177     {
178         _source = source;
179     }
180 
181     public void warn(String msg, Object... args)
182     {
183         StringBuilder buffer = new StringBuilder(64);
184         format(buffer, ":WARN:", msg, args);
185         System.err.println(buffer);
186     }
187 
188     public void warn(Throwable thrown)
189     {
190         warn("", thrown);
191     }
192 
193     public void warn(String msg, Throwable thrown)
194     {
195         StringBuilder buffer = new StringBuilder(64);
196         format(buffer, ":WARN:", msg, thrown);
197         System.err.println(buffer);
198     }
199 
200     public void info(String msg, Object... args)
201     {
202         StringBuilder buffer = new StringBuilder(64);
203         format(buffer, ":INFO:", msg, args);
204         System.err.println(buffer);
205     }
206 
207     public void info(Throwable thrown)
208     {
209         info("", thrown);
210     }
211 
212     public void info(String msg, Throwable thrown)
213     {
214         StringBuilder buffer = new StringBuilder(64);
215         format(buffer, ":INFO:", msg, thrown);
216         System.err.println(buffer);
217     }
218 
219     public boolean isDebugEnabled()
220     {
221         return _debug;
222     }
223 
224     public void setDebugEnabled(boolean enabled)
225     {
226         _debug = enabled;
227     }
228 
229     public void debug(String msg, Object... args)
230     {
231         if (!_debug)
232             return;
233         StringBuilder buffer = new StringBuilder(64);
234         format(buffer, ":DBUG:", msg, args);
235         System.err.println(buffer);
236     }
237 
238     public void debug(Throwable thrown)
239     {
240         debug("", thrown);
241     }
242 
243     public void debug(String msg, Throwable thrown)
244     {
245         if (!_debug)
246             return;
247         StringBuilder buffer = new StringBuilder(64);
248         format(buffer, ":DBUG:", msg, thrown);
249         System.err.println(buffer);
250     }
251 
252     private void format(StringBuilder buffer, String level, String msg, Object... args)
253     {
254         String d = _dateCache.now();
255         int ms = _dateCache.lastMs();
256         tag(buffer, d, ms, level);
257         format(buffer, msg, args);
258     }
259 
260     private void format(StringBuilder buffer, String level, String msg, Throwable thrown)
261     {
262         format(buffer, level, msg);
263         if (isHideStacks())
264             format(buffer, String.valueOf(thrown));
265         else
266             format(buffer, thrown);
267     }
268 
269     private void tag(StringBuilder buffer, String d, int ms, String tag)
270     {
271         buffer.setLength(0);
272         buffer.append(d);
273         if (ms > 99)
274             buffer.append('.');
275         else if (ms > 9)
276             buffer.append(".0");
277         else
278             buffer.append(".00");
279         buffer.append(ms).append(tag);
280         if(_printLongNames) {
281             buffer.append(_name);
282         } else {
283             buffer.append(_abbrevname);
284         }
285         buffer.append(':');
286         if (_source)
287         {
288             Throwable source = new Throwable();
289             StackTraceElement[] frames =  source.getStackTrace();
290             for (int i=0;i<frames.length;i++)
291             {
292                 final StackTraceElement frame = frames[i];
293                 String clazz = frame.getClassName();
294                 if (clazz.equals(StdErrLog.class.getName())|| clazz.equals(Log.class.getName()))
295                     continue;
296                 if (!_printLongNames && clazz.startsWith("org.eclipse.jetty.")) {
297                     buffer.append(condensePackageString(clazz));
298                 } else {
299                     buffer.append(clazz);
300                 }
301                 buffer.append('#').append(frame.getMethodName());
302                 if (frame.getFileName()!=null)
303                     buffer.append('(').append(frame.getFileName()).append(':').append(frame.getLineNumber()).append(')');
304                 buffer.append(':');
305                 break;
306             }
307         }
308     }
309 
310     private void format(StringBuilder builder, String msg, Object... args)
311     {
312         if (msg==null)
313         {
314             msg="";
315             for (Object o : args)
316                 msg+="{} ";
317         }
318         String braces = "{}";
319         int start = 0;
320         for (Object arg : args)
321         {
322             int bracesIndex = msg.indexOf(braces, start);
323             if (bracesIndex < 0)
324             {
325                 escape(builder, msg.substring(start));
326                 builder.append(" ");
327                 builder.append(arg);
328                 start = msg.length();
329             }
330             else
331             {
332                 escape(builder, msg.substring(start, bracesIndex));
333                 builder.append(String.valueOf(arg));
334                 start = bracesIndex + braces.length();
335             }
336         }
337         escape(builder, msg.substring(start));
338     }
339 
340     private void escape(StringBuilder builder, String string)
341     {
342         for (int i = 0; i < string.length(); ++i)
343         {
344             char c = string.charAt(i);
345             if (Character.isISOControl(c))
346             {
347                 if (c == '\n')
348                     builder.append('|');
349                 else if (c == '\r')
350                     builder.append('<');
351                 else
352                     builder.append('?');
353             }
354             else
355                 builder.append(c);
356         }
357     }
358 
359     private void format(StringBuilder buffer, Throwable thrown)
360     {
361         if (thrown == null)
362         {
363             buffer.append("null");
364         }
365         else
366         {
367             buffer.append('\n');
368             format(buffer, thrown.toString());
369             StackTraceElement[] elements = thrown.getStackTrace();
370             for (int i = 0; elements != null && i < elements.length; i++)
371             {
372                 buffer.append("\n\tat ");
373                 format(buffer, elements[i].toString());
374             }
375             
376             Throwable cause = thrown.getCause();
377             if (cause!=null && cause!=thrown)
378             {
379                 buffer.append("\nCaused by: ");
380                 format(buffer,cause);
381             }
382         }
383     }
384 
385     public Logger getLogger(String name)
386     {
387         String fullname=_name == null || _name.length() == 0?name:_name + "." + name;
388         
389         if ((name == null && this._name == null) || fullname.equals(_name))
390             return this;
391         
392         StdErrLog logger = __loggers.get(name);
393         if (logger==null)
394         {
395             StdErrLog sel=new StdErrLog(fullname);
396             // Preserve configuration for new loggers configuration
397             sel.setPrintLongNames(_printLongNames);
398             sel.setDebugEnabled(_debug);
399             sel.setSource(_source);
400             logger=__loggers.putIfAbsent(fullname,sel);
401             if (logger==null)
402                 logger=sel;
403         }
404         
405         return logger;
406     }
407 
408     @Override
409     public String toString()
410     {
411         return "StdErrLog:" + _name + ":DEBUG=" + _debug;
412     }
413 
414     public void ignore(Throwable ignored)
415     {
416         if (Log.isIgnored())
417         {
418             warn(Log.IGNORED, ignored);
419         }
420 	else
421 	{
422 	    debug("Ignored {}",ignored.toString());
423 	}
424     }
425 }