KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jivesoftware > util > log > format > PatternFormatter


1 /*
2  * Copyright (C) The Apache Software Foundation. All rights reserved.
3  *
4  * This software is published under the terms of the Apache Software License
5  * version 1.1, a copy of which has been included with this distribution in
6  * the LICENSE file.
7  */

8 package org.jivesoftware.util.log.format;
9
10 import org.jivesoftware.util.log.ContextMap;
11 import org.jivesoftware.util.log.LogEvent;
12 import org.jivesoftware.util.log.Priority;
13 import java.io.StringWriter JavaDoc;
14 import java.text.SimpleDateFormat JavaDoc;
15 import java.util.Date JavaDoc;
16 import java.util.Stack JavaDoc;
17
18 /**
19  * This formater formats the LogEvents according to a input pattern
20  * string.
21  * <p/>
22  * The format of each pattern element can be %[+|-][#[.#]]{field:subformat}.
23  * </p>
24  * <ul>
25  * <li>The +|- indicates left or right justify.
26  * </li>
27  * <li>The #.# indicates the minimum and maximum size of output.<br>
28  * You may omit the values and the field will be formatted without size
29  * restriction.<br>
30  * You may specify '#', or '#.' to define an minimum size, only.</br>
31  * You may specify '.#' to define an maximum size only.
32  * </li>
33  * <li>
34  * 'field' indicates which field is to be output and must be one of
35  * properties of LogEvent.<br>
36  * Currently following fields are supported:
37  * <dl>
38  * <dt>category</dt>
39  * <dd>Category value of the logging event.</dd>
40  * <dt>context</dt>
41  * <dd>Context value of the logging event.</dd>
42  * <dt>message</dt>
43  * <dd>Message value of the logging event.</dd>
44  * <dt>time</dt>
45  * <dd>Time value of the logging event.</dd>
46  * <dt>rtime</dt>
47  * <dd>Relative time value of the logging event.</dd>
48  * <dt>throwable</dt>
49  * <dd>Throwable value of the logging event.</dd>
50  * <dt>priority</dt>
51  * <dd>Priority value of the logging event.</dd>
52  * </dl>
53  * </li>
54  * <li>'subformat' indicates a particular subformat and is currently only used
55  * for category context to specify the context map parameter name.
56  * </li>
57  * </ul>
58  * <p>A simple example of a typical PatternFormatter format:
59  * </p>
60  * <pre><code>%{time} %5.5{priority}[%-10.10{category}]: %{message}
61  * </pre></code>
62  * <p/>
63  * This format string will format a log event printing first time value of
64  * of log event with out size restriction, next priority with minum and maximum size 5,
65  * next category right justified having minmum and maximum size of 10,
66  * at last the message of the log event without size restriction.
67  * </p>
68  * <p>A formatted sample message of the above pattern format:
69  * </p>
70  * <pre><code>1000928827905 DEBUG [ junit]: Sample message
71  * </pre><code>
72  *
73  * @author <a HREF="mailto:peter@apache.org">Peter Donald</a>
74  * @author <a HREF="mailto:sylvain@apache.org">Sylvain Wallez</a>
75  * @version CVS $Revision: 1.1 $ $Date: 2004/10/21 06:08:42 $
76  */

77 public class PatternFormatter implements Formatter {
78     private final static int TYPE_TEXT = 1;
79     private final static int TYPE_CATEGORY = 2;
80     private final static int TYPE_CONTEXT = 3;
81     private final static int TYPE_MESSAGE = 4;
82     private final static int TYPE_TIME = 5;
83     private final static int TYPE_RELATIVE_TIME = 6;
84     private final static int TYPE_THROWABLE = 7;
85     private final static int TYPE_PRIORITY = 8;
86
87     /**
88      * The maximum value used for TYPEs. Subclasses can define their own TYPEs
89      * starting at <code>MAX_TYPE + 1</code>.
90      */

91     protected final static int MAX_TYPE = TYPE_PRIORITY;
92
93     private final static String JavaDoc TYPE_CATEGORY_STR = "category";
94     private final static String JavaDoc TYPE_CONTEXT_STR = "context";
95     private final static String JavaDoc TYPE_MESSAGE_STR = "message";
96     private final static String JavaDoc TYPE_TIME_STR = "time";
97     private final static String JavaDoc TYPE_RELATIVE_TIME_STR = "rtime";
98     private final static String JavaDoc TYPE_THROWABLE_STR = "throwable";
99     private final static String JavaDoc TYPE_PRIORITY_STR = "priority";
100
101     private final static String JavaDoc SPACE_16 = " ";
102     private final static String JavaDoc SPACE_8 = " ";
103     private final static String JavaDoc SPACE_4 = " ";
104     private final static String JavaDoc SPACE_2 = " ";
105     private final static String JavaDoc SPACE_1 = " ";
106
107     private final static String JavaDoc EOL = System.getProperty("line.separator", "\n");
108
109     protected static class PatternRun {
110         public String JavaDoc m_data;
111         public boolean m_rightJustify;
112         public int m_minSize;
113         public int m_maxSize;
114         public int m_type;
115         public String JavaDoc m_format;
116     }
117
118     private PatternRun m_formatSpecification[];
119
120     private SimpleDateFormat JavaDoc m_simpleDateFormat;
121     private final Date JavaDoc m_date = new Date JavaDoc();
122
123     /**
124      * @deprecated Use constructor PatternFormatter(String pattern) as this does not
125      * correctly initialize object
126      */

127     public PatternFormatter() {
128     }
129
130     public PatternFormatter(final String JavaDoc pattern) {
131         parse(pattern);
132     }
133
134     /**
135      * Extract and build a pattern from input string.
136      *
137      * @param stack the stack on which to place patterns
138      * @param pattern the input string
139      * @param index the start of pattern run
140      * @return the number of characters in pattern run
141      */

142     private int addPatternRun(final Stack JavaDoc stack,
143                               final char pattern[],
144                               int index) {
145         final PatternRun run = new PatternRun();
146         final int start = index++;
147
148         //first check for a +|- sign
149
if ('+' == pattern[index])
150             index++;
151         else if ('-' == pattern[index]) {
152             run.m_rightJustify = true;
153             index++;
154         }
155
156         if (Character.isDigit(pattern[index])) {
157             int total = 0;
158             while (Character.isDigit(pattern[index])) {
159                 total = total * 10 + (pattern[index] - '0');
160                 index++;
161             }
162             run.m_minSize = total;
163         }
164
165         //check for . sign indicating a maximum is to follow
166
if (index < pattern.length && '.' == pattern[index]) {
167             index++;
168
169             if (Character.isDigit(pattern[index])) {
170                 int total = 0;
171                 while (Character.isDigit(pattern[index])) {
172                     total = total * 10 + (pattern[index] - '0');
173                     index++;
174                 }
175                 run.m_maxSize = total;
176             }
177         }
178
179         if (index >= pattern.length || '{' != pattern[index]) {
180             throw
181                     new IllegalArgumentException JavaDoc("Badly formed pattern at character " +
182                     index);
183         }
184
185         int typeStart = index;
186
187         while (index < pattern.length &&
188                 pattern[index] != ':' && pattern[index] != '}') {
189             index++;
190         }
191
192         int typeEnd = index - 1;
193
194         final String JavaDoc type =
195                 new String JavaDoc(pattern, typeStart + 1, typeEnd - typeStart);
196
197         run.m_type = getTypeIdFor(type);
198
199         if (index < pattern.length && pattern[index] == ':') {
200             index++;
201             while (index < pattern.length && pattern[index] != '}') index++;
202
203             final int length = index - typeEnd - 2;
204
205             if (0 != length) {
206                 run.m_format = new String JavaDoc(pattern, typeEnd + 2, length);
207             }
208         }
209
210         if (index >= pattern.length || '}' != pattern[index]) {
211             throw new
212                     IllegalArgumentException JavaDoc("Unterminated type in pattern at character "
213                     + index);
214         }
215
216         index++;
217
218         stack.push(run);
219
220         return index - start;
221     }
222
223     /**
224      * Extract and build a text run from input string.
225      * It does special handling of '\n' and '\t' replaceing
226      * them with newline and tab.
227      *
228      * @param stack the stack on which to place runs
229      * @param pattern the input string
230      * @param index the start of the text run
231      * @return the number of characters in run
232      */

233     private int addTextRun(final Stack JavaDoc stack,
234                            final char pattern[],
235                            int index) {
236         final PatternRun run = new PatternRun();
237         final int start = index;
238         boolean escapeMode = false;
239
240         if ('%' == pattern[index]) index++;
241
242         final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
243
244         while (index < pattern.length && pattern[index] != '%') {
245             if (escapeMode) {
246                 if ('n' == pattern[index])
247                     sb.append(EOL);
248                 else if ('t' == pattern[index])
249                     sb.append('\t');
250                 else
251                     sb.append(pattern[index]);
252                 escapeMode = false;
253             }
254             else if ('\\' == pattern[index])
255                 escapeMode = true;
256             else
257                 sb.append(pattern[index]);
258             index++;
259         }
260
261         run.m_data = sb.toString();
262         run.m_type = TYPE_TEXT;
263
264         stack.push(run);
265
266         return index - start;
267     }
268
269     /**
270      * Utility to append a string to buffer given certain constraints.
271      *
272      * @param sb the StringBuffer
273      * @param minSize the minimum size of output (0 to ignore)
274      * @param maxSize the maximum size of output (0 to ignore)
275      * @param rightJustify true if the string is to be right justified in it's box.
276      * @param output the input string
277      */

278     private void append(final StringBuffer JavaDoc sb,
279                         final int minSize,
280                         final int maxSize,
281                         final boolean rightJustify,
282                         final String JavaDoc output) {
283         final int size = output.length();
284
285         if (size < minSize) {
286             //assert( minSize > 0 );
287
if (rightJustify) {
288                 appendWhiteSpace(sb, minSize - size);
289                 sb.append(output);
290             }
291             else {
292                 sb.append(output);
293                 appendWhiteSpace(sb, minSize - size);
294             }
295         }
296         else if (maxSize > 0 && maxSize < size) {
297             if (rightJustify) {
298                 sb.append(output.substring(size - maxSize));
299             }
300             else {
301                 sb.append(output.substring(0, maxSize));
302             }
303         }
304         else {
305             sb.append(output);
306         }
307     }
308
309     /**
310      * Append a certain number of whitespace characters to a StringBuffer.
311      *
312      * @param sb the StringBuffer
313      * @param length the number of spaces to append
314      */

315     private void appendWhiteSpace(final StringBuffer JavaDoc sb, int length) {
316         while (length >= 16) {
317             sb.append(SPACE_16);
318             length -= 16;
319         }
320
321         if (length >= 8) {
322             sb.append(SPACE_8);
323             length -= 8;
324         }
325
326         if (length >= 4) {
327             sb.append(SPACE_4);
328             length -= 4;
329         }
330
331         if (length >= 2) {
332             sb.append(SPACE_2);
333             length -= 2;
334         }
335
336         if (length >= 1) {
337             sb.append(SPACE_1);
338             length -= 1;
339         }
340     }
341
342     /**
343      * Format the event according to the pattern.
344      *
345      * @param event the event
346      * @return the formatted output
347      */

348     public String JavaDoc format(final LogEvent event) {
349         final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
350
351         for (int i = 0; i < m_formatSpecification.length; i++) {
352             final PatternRun run = m_formatSpecification[i];
353
354             //treat text differently as it doesn't need min/max padding
355
if (run.m_type == TYPE_TEXT) {
356                 sb.append(run.m_data);
357             }
358             else {
359                 final String JavaDoc data = formatPatternRun(event, run);
360
361                 if (null != data) {
362                     append(sb, run.m_minSize, run.m_maxSize, run.m_rightJustify, data);
363                 }
364             }
365         }
366
367         return sb.toString();
368     }
369
370     /**
371      * Formats a single pattern run (can be extended in subclasses).
372      *
373      * @param run the pattern run to format.
374      * @return the formatted result.
375      */

376     protected String JavaDoc formatPatternRun(final LogEvent event, final PatternRun run) {
377         switch (run.m_type) {
378             case TYPE_RELATIVE_TIME:
379                 return getRTime(event.getRelativeTime(), run.m_format);
380             case TYPE_TIME:
381                 return getTime(event.getTime(), run.m_format);
382             case TYPE_THROWABLE:
383                 return getStackTrace(event.getThrowable(), run.m_format);
384             case TYPE_MESSAGE:
385                 return getMessage(event.getMessage(), run.m_format);
386             case TYPE_CATEGORY:
387                 return getCategory(event.getCategory(), run.m_format);
388             case TYPE_PRIORITY:
389                 return getPriority(event.getPriority(), run.m_format);
390
391             case TYPE_CONTEXT:
392 // if( null == run.m_format ||
393
// run.m_format.startsWith( "stack" ) )
394
// {
395
// //Print a warning out to stderr here
396
// //to indicate you are using a deprecated feature?
397
// return getContext( event.getContextStack(), run.m_format );
398
// }
399
// else
400
// {
401
return getContextMap(event.getContextMap(), run.m_format);
402 // }
403

404             default:
405                 throw new IllegalStateException JavaDoc("Unknown Pattern specification." + run.m_type);
406         }
407     }
408
409     /**
410      * Utility method to format category.
411      *
412      * @param category the category string
413      * @param format ancilliary format parameter - allowed to be null
414      * @return the formatted string
415      */

416     protected String JavaDoc getCategory(final String JavaDoc category, final String JavaDoc format) {
417         return category;
418     }
419
420     /**
421      * Get formatted priority string.
422      */

423     protected String JavaDoc getPriority(final Priority priority, final String JavaDoc format) {
424         return priority.getName();
425     }
426
427 // /**
428
// * Utility method to format context.
429
// *
430
// * @param context the context string
431
// * @param format ancilliary format parameter - allowed to be null
432
// * @return the formatted string
433
// * @deprecated Use getContextStack rather than this method
434
// */
435
// protected String getContext( final ContextStack stack, final String format )
436
// {
437
// return getContextStack( stack, format );
438
// }
439

440 // /**
441
// * Utility method to format context.
442
// *
443
// * @param context the context string
444
// * @param format ancilliary format parameter - allowed to be null
445
// * @return the formatted string
446
// */
447
// protected String getContextStack( final ContextStack stack, final String format )
448
// {
449
// if( null == stack ) return "";
450
// return stack.toString( Integer.MAX_VALUE );
451
// }
452

453     /**
454      * Utility method to format context map.
455      *
456      * @param map the context map
457      * @param format ancilliary format parameter - allowed to be null
458      * @return the formatted string
459      */

460     protected String JavaDoc getContextMap(final ContextMap map, final String JavaDoc format) {
461         if (null == map) return "";
462         return map.get(format, "").toString();
463     }
464
465     /**
466      * Utility method to format message.
467      *
468      * @param message the message string
469      * @param format ancilliary format parameter - allowed to be null
470      * @return the formatted string
471      */

472     protected String JavaDoc getMessage(final String JavaDoc message, final String JavaDoc format) {
473         return message;
474     }
475
476     /**
477      * Utility method to format stack trace.
478      *
479      * @param throwable the throwable instance
480      * @param format ancilliary format parameter - allowed to be null
481      * @return the formatted string
482      */

483     protected String JavaDoc getStackTrace(final Throwable JavaDoc throwable, final String JavaDoc format) {
484         if (null == throwable) return "";
485         final StringWriter JavaDoc sw = new StringWriter JavaDoc();
486         throwable.printStackTrace(new java.io.PrintWriter JavaDoc(sw));
487         return sw.toString();
488     }
489
490     /**
491      * Utility method to format relative time.
492      *
493      * @param time the time
494      * @param format ancilliary format parameter - allowed to be null
495      * @return the formatted string
496      */

497     protected String JavaDoc getRTime(final long time, final String JavaDoc format) {
498         return getTime(time, format);
499     }
500
501     /**
502      * Utility method to format time.
503      *
504      * @param time the time
505      * @param format ancilliary format parameter - allowed to be null
506      * @return the formatted string
507      */

508     protected String JavaDoc getTime(final long time, final String JavaDoc format) {
509         if (null == format) {
510             return Long.toString(time);
511         }
512         else {
513             synchronized (m_date) {
514                 if (null == m_simpleDateFormat) {
515                     m_simpleDateFormat = new SimpleDateFormat JavaDoc(format);
516                 }
517                 m_date.setTime(time);
518                 return m_simpleDateFormat.format(m_date);
519             }
520         }
521     }
522
523     /**
524      * Retrieve the type-id for a particular string.
525      *
526      * @param type the string
527      * @return the type-id
528      */

529     protected int getTypeIdFor(final String JavaDoc type) {
530         if (type.equalsIgnoreCase(TYPE_CATEGORY_STR))
531             return TYPE_CATEGORY;
532         else if (type.equalsIgnoreCase(TYPE_CONTEXT_STR))
533             return TYPE_CONTEXT;
534         else if (type.equalsIgnoreCase(TYPE_MESSAGE_STR))
535             return TYPE_MESSAGE;
536         else if (type.equalsIgnoreCase(TYPE_PRIORITY_STR))
537             return TYPE_PRIORITY;
538         else if (type.equalsIgnoreCase(TYPE_TIME_STR))
539             return TYPE_TIME;
540         else if (type.equalsIgnoreCase(TYPE_RELATIVE_TIME_STR))
541             return TYPE_RELATIVE_TIME;
542         else if (type.equalsIgnoreCase(TYPE_THROWABLE_STR)) {
543             return TYPE_THROWABLE;
544         }
545         else {
546             throw new IllegalArgumentException JavaDoc("Unknown Type in pattern - " +
547                     type);
548         }
549     }
550
551     /**
552      * Parse the input pattern and build internal data structures.
553      *
554      * @param patternString the pattern
555      */

556     protected final void parse(final String JavaDoc patternString) {
557         final Stack JavaDoc stack = new Stack JavaDoc();
558         final int size = patternString.length();
559         final char pattern[] = new char[size];
560         int index = 0;
561
562         patternString.getChars(0, size, pattern, 0);
563
564         while (index < size) {
565             if (pattern[index] == '%' &&
566                     !(index != size - 1 && pattern[index + 1] == '%')) {
567                 index += addPatternRun(stack, pattern, index);
568             }
569             else {
570                 index += addTextRun(stack, pattern, index);
571             }
572         }
573
574         final int elementCount = stack.size();
575
576         m_formatSpecification = new PatternRun[elementCount];
577
578         for (int i = 0; i < elementCount; i++) {
579             m_formatSpecification[i] = (PatternRun)stack.elementAt(i);
580         }
581     }
582
583     /**
584      * Set the string description that the format is extracted from.
585      *
586      * @param format the string format
587      * @deprecated Parse format in via constructor rather than use this method
588      */

589     public void setFormat(final String JavaDoc format) {
590         parse(format);
591     }
592 }
593
Popular Tags