KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > petals > util > monolog > wrapper > javalog > MonologFormatter


1 /**
2  * Copyright (C) 2001-2003 France Telecom R&D
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */

18 /**
19  * PETALS - PETALS Services Platform.
20  * Copyright (c) 2005 EBM Websourcing, http://www.ebmwebsourcing.com/
21  *
22  * This library is free software; you can redistribute it and/or
23  * modify it under the terms of the GNU Lesser General Public
24  * License as published by the Free Software Foundation; either
25  * version 2.1 of the License, or (at your option) any later version.
26  * This library is distributed in the hope that it will be useful,
27  * but WITHOUT ANY WARRANTY; without even the implied warranty of
28  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29  * Lesser General Public License for more details.
30  *
31  * You should have received a copy of the GNU Lesser General Public
32  * License along with this library; if not, write to the Free Software
33  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34  *
35  * -------------------------------------------------------------------------
36  * $Id: MonologFormatter.java 12:04:24 ddesjardins $
37  * -------------------------------------------------------------------------
38  */

39 package org.objectweb.petals.util.monolog.wrapper.javalog;
40
41 import java.io.PrintWriter JavaDoc;
42 import java.io.StringWriter JavaDoc;
43 import java.util.ArrayList JavaDoc;
44 import java.util.Calendar JavaDoc;
45 import java.util.Date JavaDoc;
46 import java.util.List JavaDoc;
47 import java.util.StringTokenizer JavaDoc;
48 import java.util.logging.Formatter JavaDoc;
49 import java.util.logging.LogRecord JavaDoc;
50 import java.util.logging.SimpleFormatter JavaDoc;
51
52 import org.objectweb.util.monolog.file.api.Pattern;
53 import org.objectweb.util.monolog.wrapper.common.AbstractFactory;
54
55 /**
56  * The goal of this class is to format a LogRecord with regard to a pattern. In
57  * order to support support additional logging layer on top monolog, this class
58  * permits to specify package name or class names of its own logging layer. By
59  * default the org.apache.commons.logging package is included. To specify the
60  * list of names you have to assign the system property 'monolog.wrappers' with
61  * a list (separated with commas or white spaces) of your names.
62  *
63  * @author S.Chassande-Barrioz
64  * @author ddesjardins - eBMWebsourcing
65  */

66 public class MonologFormatter extends Formatter JavaDoc {
67
68     public final static String JavaDoc WRAPPERS_PROPERTY = "monolog.wrappers";
69
70     private static boolean debug = Boolean.getBoolean("monolog.pattern.debug");
71
72     private static String JavaDoc[] LOGWRAPPER = {"org.objectweb.util.monolog.wrapper",
73         "org.apache.commons.logging"};
74
75     private final static int PATTERN_ID_DATE = -300;
76
77     private final static int PATTERN_ID_INTERVAL = 100;
78
79     private final static int PATTERN_ID_LEVEL = -100;
80
81     private final static int PATTERN_ID_LINE_NUMBER = -800;
82
83     private final static int PATTERN_ID_MESSAGE = -500;
84
85     private final static int PATTERN_ID_METHOD = -600;
86
87     private final static int PATTERN_ID_NEW_LINE = -900;
88
89     private final static int PATTERN_ID_OBJECT = -700;
90
91     private final static int PATTERN_ID_THREAD = -400;
92
93     private final static int PATTERN_ID_TOPIC = -200;
94
95     private static long previousTime;
96
97     private static char[] previousTimeWithoutMillis = new char[20]; // "YYYY-MM-DD
98

99     /**
100      * Number of items to get back in stack trace to see the caller
101      */

102     private static final int STACK_TRACE_ITEMS = 9;
103
104     private final static String JavaDoc TOKENS = "{}" + Pattern.LEVEL + Pattern.TOPIC
105         + Pattern.DATE + Pattern.THREAD + Pattern.MESSAGE + Pattern.METHOD
106         + Pattern.OBJECT + Pattern.LINE_NUMBER + Pattern.PREFIX
107         + Pattern.NEW_LINE;
108
109     Calendar JavaDoc calendar;
110
111     /**
112      * An array of pattern id representing the user pattern
113      */

114     int[] pattern;
115
116     // HH:mm:ss."
117

118     /**
119      * An array of String used into the pattern
120      */

121     String JavaDoc[] strings;
122
123     /**
124      * the pattern in the string format (user value)
125      */

126     String JavaDoc strPattern;
127
128     public MonologFormatter() {
129         calendar = Calendar.getInstance();
130     }
131
132     public MonologFormatter(String JavaDoc strPattern) {
133         this();
134         setPattern(strPattern);
135     }
136
137     static {
138         String JavaDoc wrap = System.getProperty(WRAPPERS_PROPERTY);
139         if (wrap != null) {
140             List JavaDoc<String JavaDoc> ws = new ArrayList JavaDoc<String JavaDoc>(5);
141             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(wrap, ",;: /", false);
142             while (st.hasMoreTokens()) {
143                 ws.add(st.nextToken());
144             }
145             String JavaDoc[] wsa = new String JavaDoc[LOGWRAPPER.length + ws.size()];
146             System.arraycopy(LOGWRAPPER, 0, wsa, 0, LOGWRAPPER.length);
147             for (int i = 0; i < ws.size(); i++) {
148                 wsa[LOGWRAPPER.length + i] = (String JavaDoc) ws.get(i);
149             }
150             LOGWRAPPER = wsa;
151         }
152     }
153
154     /**
155      * Calculate the class name, the method name and the line number of the
156      * logger user.
157      *
158      * @return a string array containing 3 String [ "classname", "method name",
159      * "line number"] ex: ["com.foo.Bar", "myMethod", "512"]
160      */

161     public static String JavaDoc[] getContext() {
162
163         Throwable JavaDoc t = new Throwable JavaDoc().fillInStackTrace();
164         StringWriter JavaDoc sw = new StringWriter JavaDoc();
165         t.printStackTrace(new PrintWriter JavaDoc(sw));
166         String JavaDoc m = sw.getBuffer().toString();
167         int fin = 0;
168         int deb = 0;
169         // remove monolog methods from the stack trace
170
for (int i = 0; i < STACK_TRACE_ITEMS; i++) {
171             deb = m.indexOf("\n", deb) + 1;
172         }
173         boolean isWrapper = true;
174         deb = m.indexOf("at ", deb) + 3;
175         while (isWrapper) {
176             isWrapper = false;
177             for (int i = 0; i < LOGWRAPPER.length && !isWrapper; i++) {
178                 isWrapper |= m.startsWith(LOGWRAPPER[i], deb);
179             }
180             if (isWrapper) {
181                 deb = m.indexOf("at ", deb) + 3;
182             }
183         }
184         fin = m.indexOf("\n", deb);
185         m = m.substring(deb, fin);
186
187         // m = %C.%M(Toto.java:%L)
188
deb = m.indexOf("(");
189         fin = m.indexOf(":");
190         if (deb == -1 || fin == -1) {
191             AbstractFactory
192                 .warn("Bad stack trace. '(' and ':' expected in the string '"
193                     + m + "'. The full stack trace is the following:\n"
194                     + sw.getBuffer().toString());
195             return new String JavaDoc[] {"", "", ""};
196         }
197         String JavaDoc[] res = new String JavaDoc[3];
198         res[2] = (fin == -1 ? "unknown" : m.substring(fin + 1, m.length() - 1));
199         m = m.substring(0, deb);
200
201         // m = %C.%M
202
fin = m.lastIndexOf('.');
203         res[0] = m.substring(0, fin);
204         res[1] = m.substring(fin + 1);
205         return res;
206     }
207
208     private final static String JavaDoc patternIdToString(int id) {
209         switch (id) {
210         case PATTERN_ID_LEVEL:
211             return "" + Pattern.LEVEL;
212         case PATTERN_ID_TOPIC:
213             return "" + Pattern.TOPIC;
214         case PATTERN_ID_DATE:
215             return "" + Pattern.DATE;
216         case PATTERN_ID_THREAD:
217             return "" + Pattern.THREAD;
218         case PATTERN_ID_MESSAGE:
219             return "" + Pattern.MESSAGE;
220         case PATTERN_ID_METHOD:
221             return "" + Pattern.METHOD;
222         case PATTERN_ID_OBJECT:
223             return "" + Pattern.OBJECT;
224         case PATTERN_ID_LINE_NUMBER:
225             return "" + Pattern.LINE_NUMBER;
226         case PATTERN_ID_NEW_LINE:
227             return "" + Pattern.NEW_LINE;
228         default:
229             return null;
230         }
231     }
232
233     /**
234      * Appends to <code>sbuf</code> the time in the format "YYYY-MM-DD
235      * HH:mm:ss,SSS" for example, "2004-04-28 15:49:37,459"
236      *
237      * @param date
238      * the date to format
239      * @param sbuf
240      * the string buffer to write to
241      */

242     public void format(Date JavaDoc date, StringBuffer JavaDoc sbuf) {
243         long now = date.getTime();
244         int millis = (int) (now % 1000);
245
246         if ((now - millis) != previousTime) {
247             // We reach this point at most once per second
248
// across all threads instead of each time format()
249
// is called. This saves considerable CPU time.
250

251             calendar.setTime(date);
252
253             int start = sbuf.length();
254
255             int year = calendar.get(Calendar.YEAR);
256             sbuf.append(year);
257             sbuf.append('-');
258             int month = calendar.get(Calendar.MONTH);
259             month++;
260             if (month < 10) {
261                 sbuf.append('0');
262             }
263             sbuf.append(month);
264             sbuf.append('-');
265             int day = calendar.get(Calendar.DAY_OF_MONTH);
266             if (day < 10) {
267                 sbuf.append('0');
268             }
269             sbuf.append(day);
270
271             sbuf.append(' ');
272
273             int hour = calendar.get(Calendar.HOUR_OF_DAY);
274             if (hour < 10) {
275                 sbuf.append('0');
276             }
277             sbuf.append(hour);
278             sbuf.append(':');
279
280             int mins = calendar.get(Calendar.MINUTE);
281             if (mins < 10) {
282                 sbuf.append('0');
283             }
284             sbuf.append(mins);
285             sbuf.append(':');
286
287             int secs = calendar.get(Calendar.SECOND);
288             if (secs < 10) {
289                 sbuf.append('0');
290             }
291             sbuf.append(secs);
292             sbuf.append(',');
293
294             // store the time string for next time to avoid recomputation
295
sbuf.getChars(start, sbuf.length(), previousTimeWithoutMillis, 0);
296
297             previousTime = now - millis;
298         } else {
299             sbuf.append(previousTimeWithoutMillis);
300         }
301         if (millis < 100) {
302             sbuf.append('0');
303         }
304         if (millis < 10) {
305             sbuf.append('0');
306         }
307         sbuf.append(millis);
308     }
309
310     /**
311      * Format the given log record and return the formatted string.
312      * <p>
313      * The resulting formatted String will normally include a localized and
314      * formated version of the LogRecord's message field. The
315      * Formatter.formatMessage convenience method can (optionally) be used to
316      * localize and format the message field.
317      *
318      * @param record
319      * the log record to be formatted.
320      * @return the formatted log record
321      */

322     public String JavaDoc format(LogRecord JavaDoc record) {
323         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
324         String JavaDoc[] ctx = null;
325         for (int i = 0; i < pattern.length; i++) {
326             int p = pattern[i];
327             if (debug) {
328                 AbstractFactory.debug("format: pattern=" + p + "="
329                     + patternIdToString(p));
330             }
331             switch (p) {
332             case PATTERN_ID_LEVEL:
333                 sb.append(record.getLevel().getName());
334                 break;
335             case PATTERN_ID_TOPIC:
336                 sb.append(record.getLoggerName());
337                 break;
338             case PATTERN_ID_DATE:
339                 format(new Date JavaDoc(record.getMillis()), sb);
340                 break;
341             case PATTERN_ID_THREAD:
342                 sb.append(Thread.currentThread().getName());
343                 break;
344             case PATTERN_ID_MESSAGE:
345                 SimpleFormatter JavaDoc simpleFormatter = new SimpleFormatter JavaDoc();
346                 sb.append(simpleFormatter.formatMessage(record));
347                 break;
348             case PATTERN_ID_METHOD:
349                 if (ctx == null) {
350                     ctx = getContext();
351                 }
352                 sb.append(ctx[1]);
353                 break;
354             case PATTERN_ID_OBJECT:
355                 if (ctx == null) {
356                     ctx = getContext();
357                 }
358                 sb.append(ctx[0]);
359                 break;
360             case PATTERN_ID_LINE_NUMBER:
361                 if (ctx == null) {
362                     ctx = getContext();
363                 }
364                 sb.append(ctx[2]);
365                 break;
366             case PATTERN_ID_NEW_LINE:
367                 sb.append("\n");
368                 break;
369             default:
370                 if (p < 0) {
371                     if (ctx == null) {
372                         ctx = getContext();
373                     }
374                     if (p > (PATTERN_ID_OBJECT - PATTERN_ID_INTERVAL)
375                         && p < PATTERN_ID_OBJECT) {
376                         p = PATTERN_ID_OBJECT - p;
377                         String JavaDoc res = ctx[0];
378                         if (p == 1) {// optimize the usual case
379
int idx = res.lastIndexOf('.');
380                             if (idx != -1) {
381                                 res = res.substring(idx + 1);
382                             }
383                         } else if (p == 0) {
384                             // Nothing to do
385
} else {
386                             int idx = res.lastIndexOf('.');
387                             for (; p > 1 && idx != -1; p--) {
388                                 if (idx != -1) {
389                                     idx = res.lastIndexOf('.', idx - 1);
390                                 }
391                             }
392                             if (idx != -1) {
393                                 res = res.substring(idx + 1);
394                             }
395                         }
396                         sb.append(res);
397                     }
398                 } else if (p >= strings.length) {
399                     System.err
400                         .println("ERROR: String identifier unknown: " + p);
401                 } else {
402                     sb.append(strings[p]);
403                 }
404             }
405         }
406         if (record.getThrown() != null) {
407             StringWriter JavaDoc sw = new StringWriter JavaDoc();
408             record.getThrown().printStackTrace(new PrintWriter JavaDoc(sw));
409             sb.append(sw.getBuffer());
410         }
411         return sb.toString();
412     }
413
414     public String JavaDoc format(String JavaDoc msg, String JavaDoc levelName, String JavaDoc topic, long time) {
415         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
416         String JavaDoc[] ctx = null;
417         for (int i = 0; i < pattern.length; i++) {
418             int p = pattern[i];
419             if (debug) {
420                 AbstractFactory.debug("format: pattern=" + p + "="
421                     + patternIdToString(p));
422             }
423             switch (p) {
424             case PATTERN_ID_LEVEL:
425                 sb.append(levelName);
426                 break;
427             case PATTERN_ID_TOPIC:
428                 sb.append(topic);
429                 break;
430             case PATTERN_ID_DATE:
431                 format(new Date JavaDoc(time), sb);
432                 break;
433             case PATTERN_ID_THREAD:
434                 sb.append(Thread.currentThread().getName());
435                 break;
436             case PATTERN_ID_MESSAGE:
437                 sb.append(msg);
438                 break;
439             case PATTERN_ID_METHOD:
440                 if (ctx == null) {
441                     ctx = getContext();
442                 }
443                 sb.append(ctx[1]);
444                 break;
445             case PATTERN_ID_OBJECT:
446                 if (ctx == null) {
447                     ctx = getContext();
448                 }
449                 sb.append(ctx[0]);
450                 break;
451             case PATTERN_ID_LINE_NUMBER:
452                 if (ctx == null) {
453                     ctx = getContext();
454                 }
455                 sb.append(ctx[2]);
456                 break;
457             case PATTERN_ID_NEW_LINE:
458                 sb.append("\n");
459                 break;
460             default:
461                 if (p < 0) {
462                     if (ctx == null) {
463                         ctx = getContext();
464                     }
465                     if (p > (PATTERN_ID_OBJECT - PATTERN_ID_INTERVAL)
466                         && p < PATTERN_ID_OBJECT) {
467                         p = PATTERN_ID_OBJECT - p;
468                         String JavaDoc res = ctx[0];
469                         if (p == 1) {// optimize the usual case
470
int idx = res.lastIndexOf('.');
471                             if (idx != -1) {
472                                 res = res.substring(idx + 1);
473                             }
474                         } else if (p == 0) {
475                             // Nothing to do
476
} else {
477                             int idx = res.lastIndexOf('.');
478                             for (; p > 1 && idx != -1; p--) {
479                                 if (idx != -1) {
480                                     idx = res.lastIndexOf('.', idx - 1);
481                                 }
482                             }
483                             if (idx != -1) {
484                                 res = res.substring(idx + 1);
485                             }
486                         }
487                         sb.append(res);
488                     }
489                 } else if (p >= strings.length) {
490                     System.err
491                         .println("ERROR: String identifier unknown: " + p);
492                 } else {
493                     sb.append(strings[p]);
494                 }
495             }
496         }
497         return sb.toString();
498     }
499
500     public String JavaDoc getPattern() {
501         return strPattern;
502     }
503
504     public void setPattern(String JavaDoc p) {
505         if (debug) {
506             AbstractFactory.debug("Pattern=" + p);
507         }
508         this.strPattern = p;
509         if (strPattern == null) {
510             pattern = new int[0];
511         } else {
512             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(p, TOKENS, true);
513             List JavaDoc<Object JavaDoc> sections = new ArrayList JavaDoc<Object JavaDoc>();
514             boolean isPrefix = false;
515             boolean isObject = false;
516             boolean isInSubObject = false;
517             int subObjectNumber = 1;
518             while (st.hasMoreElements()) {
519                 String JavaDoc token = st.nextToken();
520                 if (debug) {
521                     AbstractFactory.debug("token=<" + token + ">");
522                 }
523                 if (isObject && token.equals("{")) {
524                     isInSubObject = true;
525                     isObject = false;
526                 }
527                 if (token.length() == 1) {
528                     char c = token.charAt(0);
529                     switch (c) {
530                     case '{':
531                         if (!isInSubObject) {
532                             addSection(sections, token);
533                         }
534                         break;
535                     case '}':
536                         if (isInSubObject) {
537                             int old = ((Integer JavaDoc) sections
538                                 .get(sections.size() - 1)).intValue();
539                             sections.set(sections.size() - 1, new Integer JavaDoc(old
540                                 - subObjectNumber));
541                             isInSubObject = false;
542                         } else {
543                             addSection(sections, token);
544                         }
545                         break;
546                     case Pattern.PREFIX:
547                         if (isPrefix) {
548                             sections.add(String.valueOf(Pattern.PREFIX));
549                         }
550                         isPrefix = !isPrefix;
551                         break;
552                     case Pattern.LEVEL:
553                         isPrefix = treatPattern(sections, token,
554                             PATTERN_ID_LEVEL, isPrefix);
555                         break;
556                     case Pattern.TOPIC:
557                         isPrefix = treatPattern(sections, token,
558                             PATTERN_ID_TOPIC, isPrefix);
559                         break;
560                     case Pattern.DATE:
561                         isPrefix = treatPattern(sections, token,
562                             PATTERN_ID_DATE, isPrefix);
563                         break;
564                     case Pattern.THREAD:
565                         isPrefix = treatPattern(sections, token,
566                             PATTERN_ID_THREAD, isPrefix);
567                         break;
568                     case Pattern.MESSAGE:
569                         isPrefix = treatPattern(sections, token,
570                             PATTERN_ID_MESSAGE, isPrefix);
571                         break;
572                     case Pattern.METHOD:
573                         isPrefix = treatPattern(sections, token,
574                             PATTERN_ID_METHOD, isPrefix);
575                         break;
576                     case Pattern.OBJECT:
577                         isPrefix = treatPattern(sections, token,
578                             PATTERN_ID_OBJECT, isPrefix);
579                         isObject = true;
580                         break;
581                     case Pattern.LINE_NUMBER:
582                         isPrefix = treatPattern(sections, token,
583                             PATTERN_ID_LINE_NUMBER, isPrefix);
584                         break;
585                     case Pattern.NEW_LINE:
586                         isPrefix = treatPattern(sections, token,
587                             PATTERN_ID_NEW_LINE, isPrefix);
588                         break;
589                     default:
590                         if (isInSubObject) {
591                             subObjectNumber = Integer.parseInt(token);
592                         } else {
593                             addSection(sections, token);
594                         }
595                         break;
596                     }
597                 } else if (isObject) {
598                     // Ignore
599
} else if (isInSubObject) {
600                     subObjectNumber = Integer.parseInt(token);
601                 } else {
602                     addSection(sections, token);
603                 }
604             }
605             pattern = new int[sections.size()];
606             if (debug) {
607                 AbstractFactory.debug("building pattern array...");
608                 AbstractFactory.debug("nb of pattern:" + pattern.length);
609             }
610             List JavaDoc<String JavaDoc> stringList = new ArrayList JavaDoc<String JavaDoc>(sections.size());
611             int cpt = 0;
612             for (int i = 0; i < pattern.length; i++) {
613                 Object JavaDoc o = sections.get(i);
614                 if (o instanceof String JavaDoc) {
615                     if (debug) {
616                         AbstractFactory
617                             .debug("add current pattern into strings: [" + cpt
618                                 + ", " + o + "]");
619                     }
620                     stringList.add((String JavaDoc) o);
621                     pattern[i] = cpt;
622                     cpt++;
623                 } else if (o instanceof Integer JavaDoc) {
624                     if (debug) {
625                         AbstractFactory
626                             .debug("add current pattern as negative number:"
627                                 + o);
628                     }
629                     pattern[i] = ((Integer JavaDoc) o).intValue();
630                 }
631             }
632             strings = (String JavaDoc[]) stringList.toArray(new String JavaDoc[cpt]);
633             if (debug) {
634                 AbstractFactory.debug("nb of string:" + strings.length);
635             }
636         }
637     }
638
639     private void addSection(List JavaDoc<Object JavaDoc> sections, String JavaDoc s) {
640         int size = sections.size();
641         if (size == 0) {
642             if (debug) {
643                 AbstractFactory.debug("addSection(" + s + ", " + sections
644                     + "): first elem");
645             }
646             sections.add(s);
647         } else {
648             Object JavaDoc last = sections.get(size - 1);
649             if (last instanceof String JavaDoc) {
650                 sections.set(size - 1, last + s);
651                 if (debug) {
652                     AbstractFactory.debug("addSection(" + s + ", " + sections
653                         + "): concat: " + sections.get(size - 1));
654                 }
655             } else {
656                 if (debug) {
657                     AbstractFactory.debug("addSection(" + s + ", " + sections
658                         + "): new elem");
659                 }
660                 sections.add(s);
661             }
662         }
663     }
664
665     private boolean treatPattern(List JavaDoc<Object JavaDoc> sections, String JavaDoc token,
666         int tokenId, boolean isPrefix) {
667         if (debug) {
668             AbstractFactory.debug("treatPttern(" + tokenId + "):"
669                 + " isPrefix=" + isPrefix + " token=" + token + " sections="
670                 + sections);
671         }
672         if (isPrefix) {
673             sections.add(new Integer JavaDoc(tokenId));
674             return false;
675         } else {
676             addSection(sections, token);
677             return isPrefix;
678         }
679     }
680
681 }
Popular Tags