View Javadoc
1   /*
2    * #%L
3    * Nuiton Utils
4    * %%
5    * Copyright (C) 2004 - 2010 CodeLutin
6    * %%
7    * This program is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as 
9    * published by the Free Software Foundation, either version 3 of the 
10   * License, or (at your option) any later version.
11   * 
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Lesser Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Lesser Public 
18   * License along with this program.  If not, see
19   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
20   * #L%
21   */
22  
23  package org.nuiton.util;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  
28  import java.io.PrintWriter;
29  import java.io.StringWriter;
30  import java.text.SimpleDateFormat;
31  import java.util.ArrayList;
32  import java.util.Date;
33  import java.util.HashMap;
34  import java.util.Iterator;
35  import java.util.logging.Formatter;
36  import java.util.logging.LogManager;
37  import java.util.logging.LogRecord;
38  
39  /**
40   * Classe org.nuiton.logging.PatternFormatter.
41   * <ul>
42   * <li>n: new line</li>
43   * <li>%: %</li>
44   * <li>{: {</li>
45   * </ul>
46   * <ul>
47   * <li>d: date</li>
48   * </ul>
49   *
50   * Date follow the same pattern as DateFormat.
51   *
52   * Sized
53   * <ul>
54   * <li>o: free memory</li>
55   * <li>O: total memory</li>
56   * <li>t: thread id</li>
57   * <li>p: priority level</li>
58   * <li>c: class name</li>
59   * <li>m: message</li>
60   * <li>a: argument</li>
61   * <li>e: exception</li>
62   * </ul>
63   * <p>
64   * Sized element support justify pattern.
65   * {[+|-]&lt;size&gt;[:&lt;maxPo&gt;]}.
66   * '+' is
67   * left justify, '-' rigth justify, size the prefered size for the element if it is not bigger.
68   * If maxPos option is present blanc is not add if it go up to maxPos.
69   * <p>
70   * SubString
71   * <ul>
72   * <li>M: method name</li>
73   * </ul>
74   * <p>
75   * L'element SubString a les memes possibilites que le pattern justify,
76   * et permet en plus de suprimer une sous chaine,
77   * cela permet de supprimer le debut du nom d'une classe.
78   * Syntaxe :
79   * {*&lt;substring&gt;|[+|-]&lt;size&gt;[:&lt;maxPos&gt;]}
80   * {&lt;substring&gt;*|[+|-]&lt;size&gt;[:&lt;maxPos&gt;]}
81   * L'etoile represente le texte qui restera.
82   */
83  public class LoggingPatternFormatter extends Formatter { // PatternFormatter
84  
85      private static final Log log = LogFactory.getLog(LoggingPatternFormatter.class);
86  
87      private static final String DEFAULT_PATTERN = "%d{yyyy-MM-dd HH:mm:ss} [free:%o{-7}|total:%O{-7}][%t][%p{7}] %c{org.nuiton.*|25} %M{15:105}: %m%n%e";
88  
89      protected HashMap<String, Class<?>> arguments = null;
90  
91      protected ArrayList<Argument> compile = null;
92  
93      protected String pattern = null;
94  
95      public LoggingPatternFormatter() {
96          try {
97              arguments = new HashMap<String, Class<?>>();
98              initArguments();
99              LogManager manager = LogManager.getLogManager();
100             String cname = this.getClass().getName();
101             pattern = manager.getProperty(cname + ".pattern");
102             if (pattern == null)
103                 pattern = DEFAULT_PATTERN;
104             compilePattern(pattern);
105         } catch (Exception eee) {
106             log.error("Impossible d'utiliser le PatternFormatter", eee);
107             throw new LoggingException(
108                     "Exception durant l'initialisation du PatternFormatter",
109                     eee);
110         }
111     }
112 
113     /**
114      * Methode qui formate le record
115      *
116      * @param record FIXME
117      * @return FIXME
118      */
119     public String format(LogRecord record) {
120         StringBuffer result = new StringBuffer();
121         for (Iterator i = compile.iterator(); i.hasNext(); ) {
122             ((Argument) i.next()).toString(record, result);
123         }
124         return result.toString();
125     }
126 
127     /**
128      * Si vous souhaitez ajouter des type d'argument
129      * Surcharger cette methode et a la fin fait un super.initArguments()
130      */
131     protected void initArguments() {
132         arguments.put("d", DateArgument.class);
133         arguments.put("o", FreeMemoryArgument.class);
134         arguments.put("O", TotalMemoryArgument.class);
135         arguments.put("t", ThreadArgument.class);
136         arguments.put("p", PriorityLevelArgument.class);
137         arguments.put("c", ClassNameArgument.class);
138         arguments.put("M", MethodNameArgument.class);
139         arguments.put("m", MessageArgument.class);
140         arguments.put("e", ExceptionArgument.class);
141     }
142 
143     /**
144      * Genere a partir de la chaine la liste des objet Argument.
145      *
146      * @param pattern FIXME
147      */
148     protected void compilePattern(String pattern) {
149         compile = new ArrayList<Argument>();
150         String[] match = findNextPattern(pattern);
151         while (!match[1].equals("")) {
152             compile.add(new StringArgument(match[0]));
153             compile.add(patternToArgument(match[1]));
154             match = findNextPattern(match[2]);
155         }
156         compile.add(new StringArgument(match[0]));
157     }
158 
159     /**
160      * Recherche dans la chaine le prochaine pattern.
161      *
162      * @param s FIXME
163      * @return un tableau de 3 chaines, [0] ce qu'il y a avant le
164      * parttern, [1] le parttern, [2] ce qu'il y a apres le pattern.
165      */
166     protected String[] findNextPattern(String s) {
167         String[] result = new String[]{"", "", ""};
168         if (s == null) {
169             return result;
170         }
171 
172         int d = s.indexOf("%");
173 
174         if (d != -1) { // il y a un %
175             if (d + 2 < s.length() && s.charAt(d + 2) == '{') {
176                 int f = s.indexOf("}", d);
177                 if (f != -1) { // il y a une pattern %c{pattern}
178                     result[0] = s.substring(0, d);
179                     result[1] = s.substring(d + 1, f);
180                     result[2] = s.substring(f + 1);
181                 } else {
182                     throw new LoggingException("Error, { at position "
183                             + (d + 2) + " not terminated in :" + s);
184                 }
185             } else { //pas de pattern
186                 result[0] = s.substring(0, d);
187                 result[1] = s.substring(d + 1, d + 2);
188                 result[2] = s.substring(d + 2);
189             }
190         } else {
191             result[0] = s;
192         }
193 
194         return result;
195     }
196 
197     /**
198      * Converti un pattern en un objet Argument
199      *
200      * @param s FIXME
201      * @return FIXME
202      */
203     protected Argument patternToArgument(String s) {
204         if (s.charAt(0) == 'n') { // new ligne
205             return new StringArgument("\n");
206 
207         } else if (s.charAt(0) == '%') { // le caractere %
208             return new StringArgument("%");
209 
210         } else if (s.charAt(0) == '{') { // le caractere {
211             return new StringArgument("{");
212 
213         } else {
214             String code = s.substring(0, 1);
215             Class argumentClass = (Class) arguments.get(code);
216             if (argumentClass == null)
217                 throw new LoggingException("Erreur dans le pattern '" + code
218                         + "' inconnu");
219             Argument argument;
220             try {
221                 argument = (Argument) argumentClass.newInstance();
222             } catch (InstantiationException eee) {
223                 throw new LoggingException(
224                         "Erreur lors de l'instanciation de l'objet Argument: "
225                                 + argumentClass.getName(), eee);
226             } catch (IllegalAccessException eee) {
227                 throw new LoggingException(
228                         "Erreur lors de l'instanciation de l'objet Argument: "
229                                 + argumentClass.getName(), eee);
230             }
231             if (s.length() > 1) { // on a un pattern
232                 argument.setPattern(s.substring(2));
233             }
234 
235             return argument;
236         }
237     }
238 
239     ///////////////////////////////////////////////////////////////////////////////
240     //////////////////////////// Les Classes Argument /////////////////////////////
241     ///////////////////////////////////////////////////////////////////////////////
242 
243     static protected abstract class Argument {
244         protected String pattern;
245 
246         public Argument() {
247         }
248 
249         public void setPattern(String pattern) {
250             this.pattern = pattern;
251         }
252 
253         abstract public StringBuffer toString(LogRecord record,
254                                               StringBuffer toAppendTo);
255     }
256 
257     static protected class StringArgument extends Argument {
258         protected String s = null;
259 
260         public StringArgument(String s) {
261             super();
262             this.s = s;
263         }
264 
265         public StringBuffer toString(LogRecord record, StringBuffer toAppendTo) {
266             toAppendTo.append(s);
267             return toAppendTo;
268         }
269     }
270 
271     static protected class DateArgument extends Argument {
272         protected SimpleDateFormat dateFormat = null;
273 
274         public DateArgument() {
275             super();
276         }
277 
278         public void setPattern(String pattern) {
279             super.setPattern(pattern);
280             dateFormat = new SimpleDateFormat(pattern);
281         }
282 
283         public StringBuffer toString(LogRecord record, StringBuffer toAppendTo) {
284             toAppendTo.append(dateFormat.format(new Date(record.getMillis())));
285             return toAppendTo;
286         }
287     }
288 
289     static abstract protected class SizedArgument extends Argument {
290         protected boolean left = true;
291 
292         protected int width = 0;
293 
294         protected int maxPos = -1;
295 
296         public SizedArgument() {
297             super();
298         }
299 
300         public void setPattern(String pattern) {
301             super.setPattern(pattern);
302             if (pattern.length() > 0) {
303                 String[] subpattern = pattern.split(":");
304                 pattern = subpattern[0];
305                 char op = pattern.charAt(0);
306                 if (pattern.charAt(0) == '+' || pattern.charAt(0) == '-') {
307                     pattern = pattern.substring(1);
308                 }
309                 width = Integer.parseInt(pattern);
310                 left = op != '-';
311                 if (subpattern.length > 1) {
312                     maxPos = Integer.parseInt(subpattern[1]);
313                 }
314             }
315         }
316 
317         protected StringBuffer justify(String s, StringBuffer toAppendTo) {
318             int blanc = width - s.length();
319             if (left) {
320                 toAppendTo.append(s);
321                 while (0 < blanc--
322                         && (maxPos == -1 || toAppendTo.length() < maxPos)) {
323                     toAppendTo.append(" ");
324                 }
325             } else {
326                 while (0 < blanc--
327                         && (maxPos == -1 || toAppendTo.length() < maxPos)) {
328                     toAppendTo.append(" ");
329                 }
330                 toAppendTo.append(s);
331             }
332             return toAppendTo;
333         }
334     }
335 
336     static abstract protected class SubStringArgument extends SizedArgument {
337         /**
338          * la chaine qui doit etre supprimee
339          */
340         protected String removeString = null;
341 
342         /**
343          * vrai si la chaine doit etre retiree du debut, faux pour la fin
344          */
345         protected boolean atBeginning = true;
346 
347         public SubStringArgument() {
348             super();
349         }
350 
351         public void setPattern(String pattern) {
352             String[] subpattern = pattern.split("\\|");
353             for (int i = 0; i < subpattern.length; i++) {
354                 try { //on essai de voir si le pattern convient au SizedArgument
355                     super.setPattern(subpattern[i]);
356                 } catch (NumberFormatException eee) {
357                     // il ne convient pas au SizedArgument
358                     // c pour le substring
359                     atBeginning = subpattern[i].charAt(0) != '*';
360                     if (subpattern[i].charAt(0) == '*') {
361                         removeString = subpattern[i].substring(1);
362                     } else if (subpattern[i].endsWith("*")) {
363                         removeString = subpattern[i].substring(0, subpattern[i]
364                                 .length() - 1);
365                     }
366                 }
367             }
368         }
369 
370         protected String substring(String s) {
371             if (atBeginning) {
372                 if (s.startsWith(removeString))
373                     return s.substring(removeString.length());
374             } else {
375                 if (s.endsWith(removeString))
376                     return s.substring(0, s.length() - removeString.length());
377             }
378             return s;
379         }
380     }
381 
382     static abstract protected class OctetArgument extends SizedArgument {
383         protected static final String[] UNITE = {"o", "Ko", "Mo", "Go", "To",
384                 "Po"};
385 
386         protected int diviseur = 1024;
387 
388         public OctetArgument() {
389             super();
390         }
391 
392         /**
393          * Methode permettant l'affichage d'un taille avec une representation
394          * humainement lisible.
395          *
396          * @param size     la taille rendre lisible
397          * @param unit     les unites a utiliser (les petits en premier)
398          * @param diviseur le diviseur entre unite (ex: 1000 ou 1024)
399          * @return la representation
400          */
401         protected String toReadableSize(long size, String[] unit, int diviseur) {
402             int unitIndex = 0;
403             while (size > 99999 && unitIndex < unit.length) {
404                 size /= diviseur;
405                 unitIndex++;
406             }
407             String result = size + unit[unitIndex];
408             return result;
409         }
410     }
411 
412     static protected class FreeMemoryArgument extends OctetArgument {
413         public FreeMemoryArgument() {
414             super();
415         }
416 
417         public StringBuffer toString(LogRecord record, StringBuffer toAppendTo) {
418             return justify(toReadableSize(Runtime.getRuntime().freeMemory(),
419                     UNITE, diviseur), toAppendTo);
420         }
421     }
422 
423     static protected class TotalMemoryArgument extends OctetArgument {
424         public TotalMemoryArgument() {
425             super();
426         }
427 
428         public StringBuffer toString(LogRecord record, StringBuffer toAppendTo) {
429             return justify(toReadableSize(Runtime.getRuntime().totalMemory(),
430                     UNITE, diviseur), toAppendTo);
431         }
432     }
433 
434     static protected class ThreadArgument extends SizedArgument {
435         public ThreadArgument() {
436             super();
437         }
438 
439         public StringBuffer toString(LogRecord record, StringBuffer toAppendTo) {
440             return justify(Thread.currentThread().getName(), toAppendTo);
441         }
442     }
443 
444     static protected class PriorityLevelArgument extends SizedArgument {
445         public PriorityLevelArgument() {
446             super();
447         }
448 
449         public StringBuffer toString(LogRecord record, StringBuffer toAppendTo) {
450             return justify(record.getLevel().toString(), toAppendTo);
451         }
452     }
453 
454     static protected class ClassNameArgument extends SubStringArgument {
455         public ClassNameArgument() {
456             super();
457         }
458 
459         public StringBuffer toString(LogRecord record, StringBuffer toAppendTo) {
460             return justify(substring(record.getSourceClassName()), toAppendTo);
461         }
462     }
463 
464     static protected class MethodNameArgument extends SizedArgument {
465         public MethodNameArgument() {
466             super();
467         }
468 
469         public StringBuffer toString(LogRecord record, StringBuffer toAppendTo) {
470             return justify(record.getSourceMethodName(), toAppendTo);
471         }
472     }
473 
474     static protected class MessageArgument extends SizedArgument {
475         public MessageArgument() {
476             super();
477         }
478 
479         public StringBuffer toString(LogRecord record, StringBuffer toAppendTo) {
480             return justify(record.getMessage(), toAppendTo);
481         }
482     }
483 
484     static protected class ExceptionArgument extends Argument {
485         public ExceptionArgument() {
486             super();
487         }
488 
489         public StringBuffer toString(LogRecord record, StringBuffer toAppendTo) {
490             Throwable e = record.getThrown();
491             if (e != null) {
492                 toAppendTo.append(e.getMessage());
493                 toAppendTo.append("\n");
494                 StringWriter st = new StringWriter();
495                 e.printStackTrace(new PrintWriter(st));
496                 toAppendTo.append(st.toString());
497                 toAppendTo.append("\n");
498             }
499             return toAppendTo;
500         }
501     }
502 
503 } // PatternFormatter