KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > log > format > PatternFormatter


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12  * implied.
13  *
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.log.format;
18
19 import java.io.StringWriter JavaDoc;
20 import java.text.SimpleDateFormat JavaDoc;
21 import java.util.Date JavaDoc;
22 import java.util.Stack JavaDoc;
23 import org.apache.log.ContextMap;
24 import org.apache.log.LogEvent;
25 import org.apache.log.Priority;
26
27 /**
28  * This formater formats the LogEvents according to a input pattern
29  * string.
30  * <p>
31  * The format of each pattern element can be
32  * <code>%[+|-][#[.#]]{field:subformat}</code>.
33  * </p>
34  * <ul>
35  * <li><p>The <code>+|-</code> indicates left or right justify.
36  * </p></li>
37  * <li><p>The <code>#.#</code> indicates the minimum and maximum
38  * size of output. You may omit the values and the field will be
39  * formatted without size restriction.<br />
40  * You may specify <code>#</code>, or <code>#.</code> to only
41  * define the minimum size.<br />
42  * You may specify <code>.#</code> to only define the maximum
43  * size.
44  * </p></li>
45  * <li><p><code>field</code> indicates which field is to be output and must be
46  * one of properties of LogEvent. The following fields are
47  * currently supported:
48  * <table border="0" cellpadding="4" cellspacing="0">
49  * <tr>
50  * <td><b>category</b></td>
51  * <td>Category value of the logging event.</td>
52  * </tr><tr>
53  * <td><b>context</b></td>
54  * <td>Context value of the logging event.</td>
55  * </tr><tr>
56  * <td><b>message</b></td>
57  * <td>Message value of the logging event.</td>
58  * </tr><tr>
59  * <td><b>time</b></td>
60  * <td>Time value of the logging event.</td>
61  * </tr><tr>
62  * <td><b>rtime</b></td>
63  * <td>Relative time value of the logging event.</td>
64  * </tr><tr>
65  * <td><b>throwable</b></td>
66  * <td>Throwable value of the logging event.</td>
67  * </tr><tr>
68  * <td><b>priority</b></td>
69  * <td>Priority value of the logging event.</td>
70  * </tr><tr>
71  * <td><b>thread</b></td>
72  * <td>Name of the thread which logged the event.</td>
73  * </tr>
74  * </table>
75  * </p></li>
76  *
77  * <li><p><code>subformat</code> indicates a particular subformat to
78  * use on the specified field, and is currently only supported by:
79  * <table border="0" cellpadding="4" cellspacing="0">
80  * <tr>
81  * <td><b>context</b></td>
82  * <td>Specifies the context map parameter name.</td>
83  * </tr><tr>
84  * <td><b>time</b></td>
85  * <td>Specifies the pattern to be pass to
86  * {@link java.text.SimpleDateFormat SimpleDateFormat} to format the time.</td>
87  * </tr>
88  * </table>
89  * </p></li>
90  * </ul>
91  * <p>A simple example of a typical PatternFormatter format would be:
92  * <pre><code>
93  * %{time} %5.5{priority}[%-10.10{category}]: %{message}
94  * </code></pre>
95  * </p><p>
96  * This would produce a line like:
97  * <pre><code>
98  * 1000928827905 DEBUG [ junit]: Sample message
99  * </code></pre>
100  * </p><p>
101  * The format string specifies that the logger should first print the
102  * time value of the log event without size restriction, then the
103  * priority of the log event with a minimum and maximum size of 5,
104  * then the category of the log event right justified with a minimum
105  * and maximum size of 10, followed by the message of the log event
106  * without any size restriction.
107  * </p>
108  * @author <a HREF="mailto:dev@avalon.apache.org">Avalon Development Team</a>
109  * @author Peter Donald
110  * @author <a HREF="mailto:sylvain@apache.org">Sylvain Wallez</a>
111  * @author <a HREF="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
112  * @version CVS $Revision: 1.42 $ $Date: 2004/02/28 11:31:24 $
113  */

114 public class PatternFormatter
115     implements Formatter
116 {
117     private static final int TYPE_TEXT = 1;
118     private static final int TYPE_CATEGORY = 2;
119     private static final int TYPE_CONTEXT = 3;
120     private static final int TYPE_MESSAGE = 4;
121     private static final int TYPE_TIME = 5;
122     private static final int TYPE_RELATIVE_TIME = 6;
123     private static final int TYPE_THROWABLE = 7;
124     private static final int TYPE_PRIORITY = 8;
125     private static final int TYPE_THREAD = 9;
126
127     /**
128      * The maximum value used for TYPEs. Subclasses can define their own TYPEs
129      * starting at <code>MAX_TYPE + 1</code>.
130      */

131     protected static final int MAX_TYPE = TYPE_PRIORITY;
132
133     private static final String JavaDoc TYPE_CATEGORY_STR = "category";
134     private static final String JavaDoc TYPE_CONTEXT_STR = "context";
135     private static final String JavaDoc TYPE_MESSAGE_STR = "message";
136     private static final String JavaDoc TYPE_TIME_STR = "time";
137     private static final String JavaDoc TYPE_RELATIVE_TIME_STR = "rtime";
138     private static final String JavaDoc TYPE_THROWABLE_STR = "throwable";
139     private static final String JavaDoc TYPE_PRIORITY_STR = "priority";
140     private static final String JavaDoc TYPE_THREAD_STR = "thread";
141
142     private static final String JavaDoc SPACE_16 = " ";
143     private static final String JavaDoc SPACE_8 = " ";
144     private static final String JavaDoc SPACE_4 = " ";
145     private static final String JavaDoc SPACE_2 = " ";
146     private static final String JavaDoc SPACE_1 = " ";
147
148     private static final String JavaDoc EOL = System.getProperty( "line.separator", "\n" );
149
150     protected static class PatternRun
151     {
152         public String JavaDoc m_data;
153         public boolean m_rightJustify;
154         public int m_minSize;
155         public int m_maxSize;
156         public int m_type;
157         public String JavaDoc m_format;
158     }
159
160     private PatternRun m_formatSpecification[];
161
162     private SimpleDateFormat JavaDoc m_simpleDateFormat;
163     private final Date JavaDoc m_date = new Date JavaDoc();
164
165     /**
166      * Creation of a new patter formatter baseed on a supplied pattern.
167      * @param pattern the patter
168      */

169     public PatternFormatter( final String JavaDoc pattern )
170     {
171         parse( pattern );
172     }
173
174     /**
175      * Extract and build a pattern from input string.
176      *
177      * @param stack the stack on which to place patterns
178      * @param pattern the input string
179      * @param index the start of pattern run
180      * @return the number of characters in pattern run
181      */

182     private int addPatternRun( final Stack JavaDoc stack,
183                                final char pattern[],
184                                int index )
185     {
186         final PatternRun run = new PatternRun();
187         final int start = index++;
188
189         //first check for a +|- sign
190
if( '+' == pattern[ index ] )
191         {
192             index++;
193         }
194         else if( '-' == pattern[ index ] )
195         {
196             run.m_rightJustify = true;
197             index++;
198         }
199
200         if( Character.isDigit( pattern[ index ] ) )
201         {
202             int total = 0;
203             while( Character.isDigit( pattern[ index ] ) )
204             {
205                 total = total * 10 + ( pattern[ index ] - '0' );
206                 index++;
207             }
208             run.m_minSize = total;
209         }
210
211         //check for . sign indicating a maximum is to follow
212
if( index < pattern.length && '.' == pattern[ index ] )
213         {
214             index++;
215
216             if( Character.isDigit( pattern[ index ] ) )
217             {
218                 int total = 0;
219                 while( Character.isDigit( pattern[ index ] ) )
220                 {
221                     total = total * 10 + ( pattern[ index ] - '0' );
222                     index++;
223                 }
224                 run.m_maxSize = total;
225             }
226         }
227
228         if( index >= pattern.length || '{' != pattern[ index ] )
229         {
230             throw new IllegalArgumentException JavaDoc(
231                 "Badly formed pattern at character " + index );
232         }
233
234         int typeStart = index;
235
236         while( index < pattern.length
237             && pattern[ index ] != ':' && pattern[ index ] != '}' )
238         {
239             index++;
240         }
241
242         int typeEnd = index - 1;
243
244         final String JavaDoc type =
245             new String JavaDoc( pattern, typeStart + 1, typeEnd - typeStart );
246
247         run.m_type = getTypeIdFor( type );
248
249         if( index < pattern.length && pattern[ index ] == ':' )
250         {
251             index++;
252             while( index < pattern.length && pattern[ index ] != '}' ) index++;
253
254             final int length = index - typeEnd - 2;
255
256             if( 0 != length )
257             {
258                 run.m_format = new String JavaDoc( pattern, typeEnd + 2, length );
259             }
260         }
261
262         if( index >= pattern.length || '}' != pattern[ index ] )
263         {
264             throw new IllegalArgumentException JavaDoc(
265                 "Unterminated type in pattern at character "
266                 + index );
267         }
268
269         index++;
270
271         stack.push( run );
272
273         return index - start;
274     }
275
276     /**
277      * Extract and build a text run from input string.
278      * It does special handling of '\n' and '\t' replaceing
279      * them with newline and tab.
280      *
281      * @param stack the stack on which to place runs
282      * @param pattern the input string
283      * @param index the start of the text run
284      * @return the number of characters in run
285      */

286     private int addTextRun( final Stack JavaDoc stack,
287                             final char pattern[],
288                             int index )
289     {
290         final PatternRun run = new PatternRun();
291         final int start = index;
292         boolean escapeMode = false;
293
294         if( '%' == pattern[ index ] )
295         {
296             index++;
297         }
298
299         final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
300
301         while( index < pattern.length && pattern[ index ] != '%' )
302         {
303             if( escapeMode )
304             {
305                 if( 'n' == pattern[ index ] )
306                 {
307                     sb.append( EOL );
308                 }
309                 else if( 't' == pattern[ index ] )
310                 {
311                     sb.append( '\t' );
312                 }
313                 else
314                 {
315                     sb.append( pattern[ index ] );
316                 }
317                 escapeMode = false;
318             }
319             else if( '\\' == pattern[ index ] )
320             {
321                 escapeMode = true;
322             }
323             else
324             {
325                 sb.append( pattern[ index ] );
326             }
327             index++;
328         }
329
330         run.m_data = sb.toString();
331         run.m_type = TYPE_TEXT;
332
333         stack.push( run );
334
335         return index - start;
336     }
337
338     /**
339      * Utility to append a string to buffer given certain constraints.
340      *
341      * @param sb the StringBuffer
342      * @param minSize the minimum size of output (0 to ignore)
343      * @param maxSize the maximum size of output (0 to ignore)
344      * @param rightJustify true if the string is to be right justified in it's box.
345      * @param output the input string
346      */

347     private void append( final StringBuffer JavaDoc sb,
348                          final int minSize,
349                          final int maxSize,
350                          final boolean rightJustify,
351                          final String JavaDoc output )
352     {
353         final int size = output.length();
354
355         if( size < minSize )
356         {
357             //assert( minSize > 0 );
358
if( rightJustify )
359             {
360                 appendWhiteSpace( sb, minSize - size );
361                 sb.append( output );
362             }
363             else
364             {
365                 sb.append( output );
366                 appendWhiteSpace( sb, minSize - size );
367             }
368         }
369         else if( maxSize > 0 && maxSize < size )
370         {
371             if( rightJustify )
372             {
373                 sb.append( output.substring( size - maxSize ) );
374             }
375             else
376             {
377                 sb.append( output.substring( 0, maxSize ) );
378             }
379         }
380         else
381         {
382             sb.append( output );
383         }
384     }
385
386     /**
387      * Append a certain number of whitespace characters to a StringBuffer.
388      *
389      * @param sb the StringBuffer
390      * @param length the number of spaces to append
391      */

392     private void appendWhiteSpace( final StringBuffer JavaDoc sb, int length )
393     {
394         while( length >= 16 )
395         {
396             sb.append( SPACE_16 );
397             length -= 16;
398         }
399
400         if( length >= 8 )
401         {
402             sb.append( SPACE_8 );
403             length -= 8;
404         }
405
406         if( length >= 4 )
407         {
408             sb.append( SPACE_4 );
409             length -= 4;
410         }
411
412         if( length >= 2 )
413         {
414             sb.append( SPACE_2 );
415             length -= 2;
416         }
417
418         if( length >= 1 )
419         {
420             sb.append( SPACE_1 );
421             length -= 1;
422         }
423     }
424
425     /**
426      * Format the event according to the pattern.
427      *
428      * @param event the event
429      * @return the formatted output
430      */

431     public String JavaDoc format( final LogEvent event )
432     {
433         final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
434
435         for( int i = 0; i < m_formatSpecification.length; i++ )
436         {
437             final PatternRun run = m_formatSpecification[ i ];
438
439             //treat text differently as it doesn't need min/max padding
440
if( run.m_type == TYPE_TEXT )
441             {
442                 sb.append( run.m_data );
443             }
444             else
445             {
446                 final String JavaDoc data = formatPatternRun( event, run );
447                 if( null != data )
448                 {
449                     append( sb, run.m_minSize, run.m_maxSize, run.m_rightJustify, data );
450                 }
451             }
452         }
453
454         return sb.toString();
455     }
456
457     /**
458      * Formats a single pattern run (can be extended in subclasses).
459      *
460      * @param run the pattern run to format.
461      * @return the formatted result.
462      */

463     protected String JavaDoc formatPatternRun( final LogEvent event, final PatternRun run )
464     {
465         switch( run.m_type )
466         {
467             case TYPE_RELATIVE_TIME:
468                 return getRTime( event.getRelativeTime(), run.m_format );
469             case TYPE_TIME:
470                 return getTime( event.getTime(), run.m_format );
471             case TYPE_THROWABLE:
472                 return getStackTrace( event.getThrowable(), run.m_format );
473             case TYPE_MESSAGE:
474                 return getMessage( event.getMessage(), run.m_format );
475             case TYPE_CATEGORY:
476                 return getCategory( event.getCategory(), run.m_format );
477             case TYPE_PRIORITY:
478                 return getPriority( event.getPriority(), run.m_format );
479
480             case TYPE_CONTEXT:
481                 return getContextMap( event.getContextMap(), run.m_format );
482
483             case TYPE_THREAD:
484                 return getThread( run.m_format );
485
486             default:
487                 throw new IllegalStateException JavaDoc( "Unknown Pattern specification." + run.m_type );
488         }
489     }
490
491     /**
492      * Utility method to format category.
493      *
494      * @param category the category string
495      * @param format ancilliary format parameter - allowed to be null
496      * @return the formatted string
497      */

498     protected String JavaDoc getCategory( final String JavaDoc category, final String JavaDoc format )
499     {
500         return category;
501     }
502
503     /**
504      * Get formatted priority string.
505      */

506     protected String JavaDoc getPriority( final Priority priority, final String JavaDoc format )
507     {
508         return priority.getName();
509     }
510
511     /**
512      * Get formatted thread string.
513      */

514     protected String JavaDoc getThread( final String JavaDoc format )
515     {
516         return Thread.currentThread().getName();
517     }
518
519     /**
520      * Utility method to format context map.
521      *
522      * @param map the context map
523      * @param format ancilliary format parameter - allowed to be null
524      * @return the formatted string
525      */

526     protected String JavaDoc getContextMap( final ContextMap map, final String JavaDoc format )
527     {
528         if( null == map ) return "";
529         return map.get( format, "" ).toString();
530     }
531
532     /**
533      * Utility method to format message.
534      *
535      * @param message the message string
536      * @param format ancilliary format parameter - allowed to be null
537      * @return the formatted string
538      */

539     protected String JavaDoc getMessage( final String JavaDoc message, final String JavaDoc format )
540     {
541         return message;
542     }
543
544     /**
545      * Utility method to format stack trace.
546      *
547      * @param throwable the throwable instance
548      * @param format ancilliary format parameter - allowed to be null
549      * @return the formatted string
550      */

551     protected String JavaDoc getStackTrace( final Throwable JavaDoc throwable, final String JavaDoc format )
552     {
553         if( null == throwable ) return "";
554         final StringWriter JavaDoc sw = new StringWriter JavaDoc();
555         throwable.printStackTrace( new java.io.PrintWriter JavaDoc( sw ) );
556         return sw.toString();
557     }
558
559     /**
560      * Utility method to format relative time.
561      *
562      * @param time the time
563      * @param format ancilliary format parameter - allowed to be null
564      * @return the formatted string
565      */

566     protected String JavaDoc getRTime( final long time, final String JavaDoc format )
567     {
568         return getTime( time, format );
569     }
570
571     /**
572      * Utility method to format time.
573      *
574      * @param time the time
575      * @param format ancilliary format parameter - allowed to be null
576      * @return the formatted string
577      */

578     protected String JavaDoc getTime( final long time, final String JavaDoc format )
579     {
580         if( null == format )
581         {
582             return Long.toString( time );
583         }
584         else
585         {
586             synchronized( m_date )
587             {
588                 if( null == m_simpleDateFormat )
589                 {
590                     m_simpleDateFormat = new SimpleDateFormat JavaDoc( format );
591                 }
592                 m_date.setTime( time );
593                 return m_simpleDateFormat.format( m_date );
594             }
595         }
596     }
597
598     /**
599      * Retrieve the type-id for a particular string.
600      *
601      * @param type the string
602      * @return the type-id
603      */

604     protected int getTypeIdFor( final String JavaDoc type )
605     {
606         if( type.equalsIgnoreCase( TYPE_CATEGORY_STR ) )
607         {
608             return TYPE_CATEGORY;
609         }
610         else if( type.equalsIgnoreCase( TYPE_CONTEXT_STR ) )
611         {
612             return TYPE_CONTEXT;
613         }
614         else if( type.equalsIgnoreCase( TYPE_MESSAGE_STR ) )
615         {
616             return TYPE_MESSAGE;
617         }
618         else if( type.equalsIgnoreCase( TYPE_PRIORITY_STR ) )
619         {
620             return TYPE_PRIORITY;
621         }
622         else if( type.equalsIgnoreCase( TYPE_TIME_STR ) )
623         {
624             return TYPE_TIME;
625         }
626         else if( type.equalsIgnoreCase( TYPE_RELATIVE_TIME_STR ) )
627         {
628             return TYPE_RELATIVE_TIME;
629         }
630         else if( type.equalsIgnoreCase( TYPE_THREAD_STR ) )
631         {
632             return TYPE_THREAD;
633         }
634         else if( type.equalsIgnoreCase( TYPE_THROWABLE_STR ) )
635         {
636             return TYPE_THROWABLE;
637         }
638         else
639         {
640             throw new IllegalArgumentException JavaDoc( "Unknown Type in pattern - " +
641                                                 type );
642         }
643     }
644
645     /**
646      * Parse the input pattern and build internal data structures.
647      *
648      * @param patternString the pattern
649      */

650     protected final void parse( final String JavaDoc patternString )
651     {
652         final Stack JavaDoc stack = new Stack JavaDoc();
653         final int size = patternString.length();
654         final char pattern[] = new char[ size ];
655         int index = 0;
656
657         patternString.getChars( 0, size, pattern, 0 );
658
659         while( index < size )
660         {
661             if( pattern[ index ] == '%'
662                 && !( index != size - 1 && pattern[ index + 1 ] == '%' ) )
663             {
664                 index += addPatternRun( stack, pattern, index );
665             }
666             else
667             {
668                 index += addTextRun( stack, pattern, index );
669             }
670         }
671
672         final int elementCount = stack.size();
673
674         m_formatSpecification = new PatternRun[ elementCount ];
675
676         for( int i = 0; i < elementCount; i++ )
677         {
678             m_formatSpecification[ i ] = (PatternRun)stack.elementAt( i );
679         }
680     }
681
682 }
683
Popular Tags