]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/util/log/StdErrLog.java
updating jetty to jetty-9.2.16.v2016040
[gigi.git] / lib / jetty / org / eclipse / jetty / util / log / StdErrLog.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4 //  ------------------------------------------------------------------------
5 //  All rights reserved. This program and the accompanying materials
6 //  are made available under the terms of the Eclipse Public License v1.0
7 //  and Apache License v2.0 which accompanies this distribution.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
16 //  ========================================================================
17 //
18
19 package org.eclipse.jetty.util.log;
20
21 import java.io.PrintStream;
22 import java.security.AccessControlException;
23 import java.util.Properties;
24
25 import org.eclipse.jetty.util.DateCache;
26 import org.eclipse.jetty.util.annotation.ManagedAttribute;
27 import org.eclipse.jetty.util.annotation.ManagedObject;
28
29 /**
30  * StdErr Logging implementation. 
31  * <p>
32  * A Jetty {@link Logger} that sends all logs to STDERR ({@link System#err}) with basic formatting.
33  * <p>
34  * Supports named loggers, and properties based configuration. 
35  * <p>
36  * Configuration Properties:
37  * <dl>
38  *   <dt>${name|hierarchy}.LEVEL=(ALL|DEBUG|INFO|WARN|OFF)</dt>
39  *   <dd>
40  *   Sets the level that the Logger should log at.<br/>
41  *   Names can be a package name, or a fully qualified class name.<br/>
42  *   Default: INFO<br/>
43  *   <br/>
44  *   Examples:
45  *   <dl>
46  *   <dt>org.eclipse.jetty.LEVEL=WARN</dt>
47  *   <dd>indicates that all of the jetty specific classes, in any package that 
48  *   starts with <code>org.eclipse.jetty</code> should log at level WARN.</dd>
49  *   <dt>org.eclipse.jetty.io.ChannelEndPoint.LEVEL=ALL</dt>
50  *   <dd>indicates that the specific class, ChannelEndPoint, should log all
51  *   logging events that it can generate, including DEBUG, INFO, WARN (and even special
52  *   internally ignored exception cases).</dd>
53  *   </dl>  
54  *   </dd>
55  *   
56  *   <dt>${name}.SOURCE=(true|false)</dt>
57  *   <dd>
58  *   Logger specific, attempt to print the java source file name and line number
59  *   where the logging event originated from.<br/>
60  *   Name must be a fully qualified class name (package name hierarchy is not supported
61  *   by this configurable)<br/>
62  *   Warning: this is a slow operation and will have an impact on performance!<br/>
63  *   Default: false
64  *   </dd>
65  *   
66  *   <dt>${name}.STACKS=(true|false)</dt>
67  *   <dd>
68  *   Logger specific, control the display of stacktraces.<br/>
69  *   Name must be a fully qualified class name (package name hierarchy is not supported
70  *   by this configurable)<br/>
71  *   Default: true
72  *   </dd>
73  *   
74  *   <dt>org.eclipse.jetty.util.log.stderr.SOURCE=(true|false)</dt>
75  *   <dd>Special Global Configuration, attempt to print the java source file name and line number
76  *   where the logging event originated from.<br/>
77  *   Default: false
78  *   </dd>
79  *   
80  *   <dt>org.eclipse.jetty.util.log.stderr.LONG=(true|false)</dt>
81  *   <dd>Special Global Configuration, when true, output logging events to STDERR using
82  *   long form, fully qualified class names.  when false, use abbreviated package names<br/>
83  *   Default: false
84  *   </dd>
85  *   <dt>org.eclipse.jetty.util.log.stderr.ESCAPE=(true|false)</dt>
86  *   <dd>Global Configuration, when true output logging events to STDERR are always
87  *   escaped so that control characters are replaced with '?";  '\r' with '<' and '\n' replaced '|'<br/>
88  *   Default: true
89  *   </dd>
90  * </dl>
91  */
92 @ManagedObject("Jetty StdErr Logging Implementation")
93 public class StdErrLog extends AbstractLogger
94 {
95     private static final String EOL = System.getProperty("line.separator");
96     private static DateCache _dateCache;
97     private static final Properties __props = new Properties();
98
99     private final static boolean __source = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.SOURCE",
100             Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.SOURCE","false")));
101     private final static boolean __long = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.LONG","false"));
102     private final static boolean __escape = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.ESCAPE","true"));
103
104     static
105     {
106         __props.putAll(Log.__props);
107
108         String deprecatedProperties[] =
109         { "DEBUG", "org.eclipse.jetty.util.log.DEBUG", "org.eclipse.jetty.util.log.stderr.DEBUG" };
110
111         // Toss a message to users about deprecated system properties
112         for (String deprecatedProp : deprecatedProperties)
113         {
114             if (System.getProperty(deprecatedProp) != null)
115             {
116                 System.err.printf("System Property [%s] has been deprecated! (Use org.eclipse.jetty.LEVEL=DEBUG instead)%n",deprecatedProp);
117             }
118         }
119
120         try
121         {
122             _dateCache = new DateCache("yyyy-MM-dd HH:mm:ss");
123         }
124         catch (Exception x)
125         {
126             x.printStackTrace(System.err);
127         }
128     }
129
130     public static final int LEVEL_ALL = 0;
131     public static final int LEVEL_DEBUG = 1;
132     public static final int LEVEL_INFO = 2;
133     public static final int LEVEL_WARN = 3;
134     public static final int LEVEL_OFF = 10;
135
136     private int _level = LEVEL_INFO;
137     // Level that this Logger was configured as (remembered in special case of .setDebugEnabled())
138     private int _configuredLevel;
139     private PrintStream _stderr = null;
140     private boolean _source = __source;
141     // Print the long form names, otherwise use abbreviated
142     private boolean _printLongNames = __long;
143     // The full log name, as provided by the system.
144     private final String _name;
145     // The abbreviated log name (used by default, unless _long is specified)
146     private final String _abbrevname;
147     private boolean _hideStacks = false;
148
149     /**
150      * Obtain a StdErrLog reference for the specified class, a convenience method used most often during testing to allow for control over a specific logger.
151      * <p>
152      * Must be actively using StdErrLog as the Logger implementation.
153      * 
154      * @param clazz
155      *            the Class reference for the logger to use.
156      * @return the StdErrLog logger
157      * @throws RuntimeException
158      *             if StdErrLog is not the active Logger implementation.
159      */
160     public static StdErrLog getLogger(Class<?> clazz)
161     {
162         Logger log = Log.getLogger(clazz);
163         if (log instanceof StdErrLog)
164         {
165             return (StdErrLog)log;
166         }
167         throw new RuntimeException("Logger for " + clazz + " is not of type StdErrLog");
168     }
169
170     /**
171      * Construct an anonymous StdErrLog (no name).
172      * <p>
173      * NOTE: Discouraged usage!
174      */
175     public StdErrLog()
176     {
177         this(null);
178     }
179
180     /**
181      * Construct a named StdErrLog using the {@link Log} defined properties
182      * 
183      * @param name
184      *            the name of the logger
185      */
186     public StdErrLog(String name)
187     {
188         this(name,__props);
189     }
190
191     /**
192      * Construct a named Logger using the provided properties to configure logger.
193      * 
194      * @param name
195      *            the name of the logger
196      * @param props
197      *            the configuration properties
198      */
199     public StdErrLog(String name, Properties props)
200     {
201         if (props!=null && props!=__props)
202             __props.putAll(props);
203         this._name = name == null?"":name;
204         this._abbrevname = condensePackageString(this._name);
205         this._level = getLoggingLevel(props,this._name);
206         this._configuredLevel = this._level;
207
208         try
209         {
210             String source = getLoggingProperty(props,_name,"SOURCE");
211             _source = source==null?__source:Boolean.parseBoolean(source);
212         }
213         catch (AccessControlException ace)
214         {
215             _source = __source;
216         }
217
218         try
219         {
220             // allow stacktrace display to be controlled by properties as well
221             String stacks = getLoggingProperty(props,_name,"STACKS");
222             _hideStacks = stacks==null?false:!Boolean.parseBoolean(stacks);
223         }
224         catch (AccessControlException ignore)
225         {
226             /* ignore */
227         }        
228     }
229
230     /**
231      * Get the Logging Level for the provided log name. Using the FQCN first, then each package segment from longest to
232      * shortest.
233      *
234      * @param props
235      *            the properties to check
236      * @param name
237      *            the name to get log for
238      * @return the logging level
239      */
240     public static int getLoggingLevel(Properties props, final String name)
241     {
242         if ((props == null) || (props.isEmpty()))
243         {
244             // Default Logging Level
245             return getLevelId("log.LEVEL","INFO");
246         }
247         
248         // Calculate the level this named logger should operate under.
249         // Checking with FQCN first, then each package segment from longest to shortest.
250         String nameSegment = name;
251
252         while ((nameSegment != null) && (nameSegment.length() > 0))
253         {
254             String levelStr = props.getProperty(nameSegment + ".LEVEL");
255             // System.err.printf("[StdErrLog.CONFIG] Checking for property [%s.LEVEL] = %s%n",nameSegment,levelStr);
256             int level = getLevelId(nameSegment + ".LEVEL",levelStr);
257             if (level != (-1))
258             {
259                 return level;
260             }
261
262             // Trim and try again.
263             int idx = nameSegment.lastIndexOf('.');
264             if (idx >= 0)
265             {
266                 nameSegment = nameSegment.substring(0,idx);
267             }
268             else
269             {
270                 nameSegment = null;
271             }
272         }
273
274         // Default Logging Level
275         return getLevelId("log.LEVEL",props.getProperty("log.LEVEL","INFO"));
276     }
277     
278     public static String getLoggingProperty(Properties props, String name, String property)
279     {
280         // Calculate the level this named logger should operate under.
281         // Checking with FQCN first, then each package segment from longest to shortest.
282         String nameSegment = name;
283
284         while ((nameSegment != null) && (nameSegment.length() > 0))
285         {
286             String s = props.getProperty(nameSegment+"."+property);
287             if (s!=null)
288                 return s;
289
290             // Trim and try again.
291             int idx = nameSegment.lastIndexOf('.');
292             nameSegment = (idx >= 0)?nameSegment.substring(0,idx):null;
293         }
294
295         return null;
296     }
297
298     protected static int getLevelId(String levelSegment, String levelName)
299     {
300         if (levelName == null)
301         {
302             return -1;
303         }
304         String levelStr = levelName.trim();
305         if ("ALL".equalsIgnoreCase(levelStr))
306         {
307             return LEVEL_ALL;
308         }
309         else if ("DEBUG".equalsIgnoreCase(levelStr))
310         {
311             return LEVEL_DEBUG;
312         }
313         else if ("INFO".equalsIgnoreCase(levelStr))
314         {
315             return LEVEL_INFO;
316         }
317         else if ("WARN".equalsIgnoreCase(levelStr))
318         {
319             return LEVEL_WARN;
320         }
321         else if ("OFF".equalsIgnoreCase(levelStr))
322         {
323             return LEVEL_OFF;
324         }
325
326         System.err.println("Unknown StdErrLog level [" + levelSegment + "]=[" + levelStr + "], expecting only [ALL, DEBUG, INFO, WARN, OFF] as values.");
327         return -1;
328     }
329
330     /**
331      * Condenses a classname by stripping down the package name to just the first character of each package name
332      * segment.Configured
333      * <p>
334      *
335      * <pre>
336      * Examples:
337      * "org.eclipse.jetty.test.FooTest"           = "oejt.FooTest"
338      * "org.eclipse.jetty.server.logging.LogTest" = "orjsl.LogTest"
339      * </pre>
340      *
341      * @param classname
342      *            the fully qualified class name
343      * @return the condensed name
344      */
345     protected static String condensePackageString(String classname)
346     {
347         String parts[] = classname.split("\\.");
348         StringBuilder dense = new StringBuilder();
349         for (int i = 0; i < (parts.length - 1); i++)
350         {
351             dense.append(parts[i].charAt(0));
352         }
353         if (dense.length() > 0)
354         {
355             dense.append('.');
356         }
357         dense.append(parts[parts.length - 1]);
358         return dense.toString();
359     }
360
361     public String getName()
362     {
363         return _name;
364     }
365
366     public void setPrintLongNames(boolean printLongNames)
367     {
368         this._printLongNames = printLongNames;
369     }
370
371     public boolean isPrintLongNames()
372     {
373         return this._printLongNames;
374     }
375
376     public boolean isHideStacks()
377     {
378         return _hideStacks;
379     }
380
381     public void setHideStacks(boolean hideStacks)
382     {
383         _hideStacks = hideStacks;
384     }
385
386     /* ------------------------------------------------------------ */
387     /**
388      * Is the source of a log, logged
389      *
390      * @return true if the class, method, file and line number of a log is logged.
391      */
392     public boolean isSource()
393     {
394         return _source;
395     }
396
397     /* ------------------------------------------------------------ */
398     /**
399      * Set if a log source is logged.
400      *
401      * @param source
402      *            true if the class, method, file and line number of a log is logged.
403      */
404     public void setSource(boolean source)
405     {
406         _source = source;
407     }
408
409     public void warn(String msg, Object... args)
410     {
411         if (_level <= LEVEL_WARN)
412         {
413             StringBuilder buffer = new StringBuilder(64);
414             format(buffer,":WARN:",msg,args);
415             (_stderr==null?System.err:_stderr).println(buffer);
416         }
417     }
418
419     public void warn(Throwable thrown)
420     {
421         warn("",thrown);
422     }
423
424     public void warn(String msg, Throwable thrown)
425     {
426         if (_level <= LEVEL_WARN)
427         {
428             StringBuilder buffer = new StringBuilder(64);
429             format(buffer,":WARN:",msg,thrown);
430             (_stderr==null?System.err:_stderr).println(buffer);
431         }
432     }
433
434     public void info(String msg, Object... args)
435     {
436         if (_level <= LEVEL_INFO)
437         {
438             StringBuilder buffer = new StringBuilder(64);
439             format(buffer,":INFO:",msg,args);
440             (_stderr==null?System.err:_stderr).println(buffer);
441         }
442     }
443
444     public void info(Throwable thrown)
445     {
446         info("",thrown);
447     }
448
449     public void info(String msg, Throwable thrown)
450     {
451         if (_level <= LEVEL_INFO)
452         {
453             StringBuilder buffer = new StringBuilder(64);
454             format(buffer,":INFO:",msg,thrown);
455             (_stderr==null?System.err:_stderr).println(buffer);
456         }
457     }
458
459     @ManagedAttribute("is debug enabled for root logger Log.LOG")
460     public boolean isDebugEnabled()
461     {
462         return (_level <= LEVEL_DEBUG);
463     }
464
465     /**
466      * Legacy interface where a programmatic configuration of the logger level
467      * is done as a wholesale approach.
468      */
469     @Override
470     public void setDebugEnabled(boolean enabled)
471     {
472         if (enabled)
473         {
474             this._level = LEVEL_DEBUG;
475
476             for (Logger log : Log.getLoggers().values())
477             {                
478                 if (log.getName().startsWith(getName()) && log instanceof StdErrLog)
479                     ((StdErrLog)log).setLevel(LEVEL_DEBUG);
480             }
481         }
482         else
483         {
484             this._level = this._configuredLevel;
485
486             for (Logger log : Log.getLoggers().values())
487             {
488                 if (log.getName().startsWith(getName()) && log instanceof StdErrLog)
489                     ((StdErrLog)log).setLevel(((StdErrLog)log)._configuredLevel);
490             }
491         }
492     }
493
494     public int getLevel()
495     {
496         return _level;
497     }
498
499     /**
500      * Set the level for this logger.
501      * <p>
502      * Available values ({@link StdErrLog#LEVEL_ALL}, {@link StdErrLog#LEVEL_DEBUG}, {@link StdErrLog#LEVEL_INFO},
503      * {@link StdErrLog#LEVEL_WARN})
504      *
505      * @param level
506      *            the level to set the logger to
507      */
508     public void setLevel(int level)
509     {
510         this._level = level;
511     }
512
513     public void setStdErrStream(PrintStream stream)
514     {
515         this._stderr = stream==System.err?null:stream;
516     }
517
518     public void debug(String msg, Object... args)
519     {
520         if (_level <= LEVEL_DEBUG)
521         {
522             StringBuilder buffer = new StringBuilder(64);
523             format(buffer,":DBUG:",msg,args);
524             (_stderr==null?System.err:_stderr).println(buffer);
525         }
526     }
527
528     public void debug(String msg, long arg)
529     {
530         if (isDebugEnabled())
531         {
532             StringBuilder buffer = new StringBuilder(64);
533             format(buffer,":DBUG:",msg,arg);
534             (_stderr==null?System.err:_stderr).println(buffer);
535         }
536     }
537     
538     public void debug(Throwable thrown)
539     {
540         debug("",thrown);
541     }
542
543     public void debug(String msg, Throwable thrown)
544     {
545         if (_level <= LEVEL_DEBUG)
546         {
547             StringBuilder buffer = new StringBuilder(64);
548             format(buffer,":DBUG:",msg,thrown);
549             (_stderr==null?System.err:_stderr).println(buffer);
550         }
551     }
552
553     private void format(StringBuilder buffer, String level, String msg, Object... args)
554     {
555         long now = System.currentTimeMillis();
556         int ms=(int)(now%1000);
557         String d = _dateCache.formatNow(now);
558         tag(buffer,d,ms,level);
559         format(buffer,msg,args);
560     }
561
562     private void format(StringBuilder buffer, String level, String msg, Throwable thrown)
563     {
564         format(buffer,level,msg);
565         if (isHideStacks())
566         {
567             format(buffer,": "+String.valueOf(thrown));
568         }
569         else
570         {
571             format(buffer,thrown);
572         }
573     }
574
575     private void tag(StringBuilder buffer, String d, int ms, String tag)
576     {
577         buffer.setLength(0);
578         buffer.append(d);
579         if (ms > 99)
580         {
581             buffer.append('.');
582         }
583         else if (ms > 9)
584         {
585             buffer.append(".0");
586         }
587         else
588         {
589             buffer.append(".00");
590         }
591         buffer.append(ms).append(tag);
592         if (_printLongNames)
593         {
594             buffer.append(_name);
595         }
596         else
597         {
598             buffer.append(_abbrevname);
599         }
600         buffer.append(':');
601         buffer.append(Thread.currentThread().getName()).append(": ");
602         if (_source)
603         {
604             Throwable source = new Throwable();
605             StackTraceElement[] frames = source.getStackTrace();
606             for (int i = 0; i < frames.length; i++)
607             {
608                 final StackTraceElement frame = frames[i];
609                 String clazz = frame.getClassName();
610                 if (clazz.equals(StdErrLog.class.getName()) || clazz.equals(Log.class.getName()))
611                 {
612                     continue;
613                 }
614                 if (!_printLongNames && clazz.startsWith("org.eclipse.jetty."))
615                 {
616                     buffer.append(condensePackageString(clazz));
617                 }
618                 else
619                 {
620                     buffer.append(clazz);
621                 }
622                 buffer.append('#').append(frame.getMethodName());
623                 if (frame.getFileName() != null)
624                 {
625                     buffer.append('(').append(frame.getFileName()).append(':').append(frame.getLineNumber()).append(')');
626                 }
627                 buffer.append(':');
628                 break;
629             }
630         }
631     }
632
633     private void format(StringBuilder builder, String msg, Object... args)
634     {
635         if (msg == null)
636         {
637             msg = "";
638             for (int i = 0; i < args.length; i++)
639             {
640                 msg += "{} ";
641             }
642         }
643         String braces = "{}";
644         int start = 0;
645         for (Object arg : args)
646         {
647             int bracesIndex = msg.indexOf(braces,start);
648             if (bracesIndex < 0)
649             {
650                 escape(builder,msg.substring(start));
651                 builder.append(" ");
652                 builder.append(arg);
653                 start = msg.length();
654             }
655             else
656             {
657                 escape(builder,msg.substring(start,bracesIndex));
658                 builder.append(String.valueOf(arg));
659                 start = bracesIndex + braces.length();
660             }
661         }
662         escape(builder,msg.substring(start));
663     }
664
665     private void escape(StringBuilder builder, String string)
666     {
667         if (__escape)
668         {
669             for (int i = 0; i < string.length(); ++i)
670             {
671                 char c = string.charAt(i);
672                 if (Character.isISOControl(c))
673                 {
674                     if (c == '\n')
675                     {
676                         builder.append('|');
677                     }
678                     else if (c == '\r')
679                     {
680                         builder.append('<');
681                     }
682                     else
683                     {
684                         builder.append('?');
685                     }
686                 }
687                 else
688                 {
689                     builder.append(c);
690                 }
691             }
692         }
693         else
694             builder.append(string);
695     }
696
697     protected void format(StringBuilder buffer, Throwable thrown)
698     {
699         format(buffer,thrown,"");
700     }
701     
702     protected void format(StringBuilder buffer, Throwable thrown, String indent)
703     {
704         if (thrown == null)
705         {
706             buffer.append("null");
707         }
708         else
709         {
710             buffer.append(EOL).append(indent);
711             format(buffer,thrown.toString());
712             StackTraceElement[] elements = thrown.getStackTrace();
713             for (int i = 0; elements != null && i < elements.length; i++)
714             {
715                 buffer.append(EOL).append(indent).append("\tat ");
716                 format(buffer,elements[i].toString());
717             }
718
719             for (Throwable suppressed:thrown.getSuppressed())
720             {
721                 buffer.append(EOL).append(indent).append("Suppressed: ");
722                 format(buffer,suppressed,"\t|"+indent);
723             }
724             
725             Throwable cause = thrown.getCause();
726             if (cause != null && cause != thrown)
727             {
728                 buffer.append(EOL).append(indent).append("Caused by: ");
729                 format(buffer,cause,indent);
730             }
731         }
732     }
733
734
735     /**
736      * Create a Child Logger of this Logger.
737      */
738     @Override
739     protected Logger newLogger(String fullname)
740     {
741         StdErrLog logger = new StdErrLog(fullname);
742         // Preserve configuration for new loggers configuration
743         logger.setPrintLongNames(_printLongNames);
744         logger._stderr = this._stderr;
745
746         // Force the child to have any programmatic configuration
747         if (_level!=_configuredLevel)
748             logger._level=_level;
749
750         return logger;
751     }
752
753     @Override
754     public String toString()
755     {
756         StringBuilder s = new StringBuilder();
757         s.append("StdErrLog:");
758         s.append(_name);
759         s.append(":LEVEL=");
760         switch (_level)
761         {
762             case LEVEL_ALL:
763                 s.append("ALL");
764                 break;
765             case LEVEL_DEBUG:
766                 s.append("DEBUG");
767                 break;
768             case LEVEL_INFO:
769                 s.append("INFO");
770                 break;
771             case LEVEL_WARN:
772                 s.append("WARN");
773                 break;
774             default:
775                 s.append("?");
776                 break;
777         }
778         return s.toString();
779     }
780
781     public static void setProperties(Properties props)
782     {
783         __props.clear();
784         __props.putAll(props);
785     }
786
787     public void ignore(Throwable ignored)
788     {
789         if (_level <= LEVEL_ALL)
790         {
791             StringBuilder buffer = new StringBuilder(64);
792             format(buffer,":IGNORED:","",ignored);
793             (_stderr==null?System.err:_stderr).println(buffer);
794         }
795     }
796 }