001: /**
002: * Copyright (C) 2001-2003 France Telecom R&D
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */package org.objectweb.util.monolog.wrapper.log4j;
018:
019: import org.apache.log4j.Appender;
020: import org.apache.log4j.Priority; //import org.apache.log4j.Logger;
021: import org.apache.log4j.spi.LoggingEvent;
022: import org.objectweb.util.monolog.api.BasicLevel;
023: import org.objectweb.util.monolog.api.Handler;
024: import org.objectweb.util.monolog.api.Level;
025: import org.objectweb.util.monolog.api.TopicalLogger;
026: import org.objectweb.util.monolog.wrapper.common.AbstractFactory;
027: import org.objectweb.util.monolog.wrapper.common.EnumrationImpl;
028:
029: import java.io.PrintWriter;
030: import java.io.StringWriter;
031: import java.util.ArrayList;
032: import java.util.Enumeration;
033: import java.util.HashMap;
034: import java.util.Iterator;
035:
036: /**
037: * This class wraps the Logger concept into the log4j world. This class extends
038: * therefore the Logger class. This implementation supports
039: * <ul>
040: * <li>The multiple topic feature by adding a parent list into each node.</li>
041: * <li>The inheritance model: The same instance represents the monolog aspect
042: * (Logger) and the Log4j aspect (Logger).
043: * <li>The delegation model: There are two instances, a Logger object (or a
044: * class which inherits from Logger as RootLogger), and a MonologCategory
045: * which delegates calls to the first objects.</li>
046: * </ul>
047: *
048: * @author Sebastien Chassande-Barrioz
049: */
050: public class MonologCategory extends org.apache.log4j.Logger implements
051: TopicalLogger {
052:
053: protected boolean enable = true;
054: protected final static int DISABLE_OFF = -1;
055: protected final static int DISABLE_ON = org.apache.log4j.Level.FATAL
056: .toInt();
057: protected OwPriority interPriority = null;
058: protected byte depth = 2;
059:
060: /**
061: * This field references all parent of this Logger.
062: * key = topic of the current logger
063: * value = its parent either the topic. A parent can be a MonologCategory or
064: * a Logger.
065: */
066: protected HashMap topicToparents = null;
067:
068: /**
069: * This field references all appenders associated to the current Logger
070: */
071: protected ArrayList appenders = null;
072:
073: /**
074: * This field references the inner Logger if the delegation was choosen.
075: */
076: protected org.apache.log4j.Logger categ = null;
077:
078: /**
079: * This field is the class name of the class localized in the top of the
080: * log system layer. The initial value is by default the class name of this
081: * class. Indeed the monolog call directly this class. This information
082: * is used to knwon the caller in the execution stack.
083: */
084: private final static String instanceFQN = "org.objectweb.util.monolog.wrapper.log4j.MonologCategory";
085:
086: /**
087: * This constructor initializes the instance in inheritance mode. It
088: * initializes the instanceFQN, and struture of parents.
089: */
090: public MonologCategory(String _initialName) {
091: super (_initialName);
092: topicToparents = new HashMap();
093: }
094:
095: /**
096: * This constructor initializes the instance in delegation mode.
097: * @param c is the inner Logger. All calls will be foward to this instance
098: */
099: public MonologCategory(org.apache.log4j.Logger c) {
100: super (c.getName());
101: categ = c;
102: }
103:
104: /**
105: * It formats a message by adding the object and the method name where the
106: * call to monolog was done.
107: * @param msg is the original message
108: * @param removeTopStack is the number of monolog method call. Indeed this method
109: * fetch a stack trace. This method fetches one line in this stack. The
110: * parameter is the line number in this stack.
111: */
112: public static String format(String msg, int removeTopStack) {
113: Throwable t = new Throwable().fillInStackTrace();
114: StringWriter sw = new StringWriter();
115: t.printStackTrace(new PrintWriter(sw));
116: String m = sw.getBuffer().toString();
117:
118: int deb = -1, fin = 0;
119:
120: // take the right line
121: deb = -1;
122: for (int i = 0; i < (removeTopStack + 1); i++) {
123: deb = m.indexOf("\n", deb + 1);
124: }
125:
126: deb = m.indexOf("at ", deb);
127: fin = m.indexOf("\n", deb);
128: m = m.substring(deb + 3, fin);
129:
130: // remove param
131: deb = m.indexOf("(");
132: fin = m.indexOf(":");
133: m = m.substring(0, deb + 1) + m.substring(fin + 1, m.length());
134:
135: // remove package name
136: deb = m.indexOf("(");
137: int c1 = 0;
138: int c2 = 0;
139: int current = m.indexOf(".");
140: while (current != -1 && current < deb) {
141: c1 = c2;
142: c2 = current;
143: current = m.indexOf(".", current + 1);
144: }
145: m = m.substring(c1 + 1, m.length());
146:
147: return m + ": " + msg;
148: }
149:
150: private org.apache.log4j.Level convertToLog4jLevel(int value) {
151: switch (value) {
152: case org.apache.log4j.Level.DEBUG_INT:
153: return org.apache.log4j.Level.DEBUG;
154: case org.apache.log4j.Level.INFO_INT:
155: return org.apache.log4j.Level.INFO;
156: case org.apache.log4j.Level.WARN_INT:
157: return org.apache.log4j.Level.WARN;
158: case org.apache.log4j.Level.ERROR_INT:
159: return org.apache.log4j.Level.ERROR;
160: case org.apache.log4j.Level.FATAL_INT:
161: return org.apache.log4j.Level.FATAL;
162: default:
163: if (interPriority == null)
164: interPriority = new OwPriority(value);
165: else
166: interPriority.level = value;
167: return interPriority;
168: }
169: }
170:
171: // IMPLEMENTATION OF CLASS org.apache.log4j.Logger
172:
173: /**
174: Starting from this Logger, search the Logger hierarchy for a
175: non-null priority and return it. Otherwise, return the priority of the
176: root Logger.
177:
178: <p>The Logger class is designed so that this method executes as
179: quickly as possible.
180: */
181: public org.apache.log4j.Level getChainedLevel() {
182: if (categ != null) {
183: return categ.getEffectiveLevel();
184: }
185: if (level != null) {
186: return level;
187: }
188: org.apache.log4j.Level current = parent.getEffectiveLevel();
189: org.apache.log4j.Logger[] cats = (org.apache.log4j.Logger[]) topicToparents
190: .values().toArray(new org.apache.log4j.Logger[0]);
191: for (int i = 0; i < cats.length; i++) {
192: org.apache.log4j.Level neo = cats[i].getEffectiveLevel();
193: if (neo.isGreaterOrEqual(current)) {
194: current = neo;
195: }
196: }
197: return current;
198: }
199:
200: /**
201: * In inheritance mode this method delegates the treatment to the other
202: * callAppendes methods.
203: * In delegation mode, the call is forwarded on the inner Logger instance.
204: */
205: public void callAppenders(LoggingEvent event) {
206: if (categ != null) {
207: categ.callAppenders(event);
208: }
209: callAppenders(event, false);
210: }
211:
212: /**
213: * This method calls all the parent loggers and call its appender either
214: * the followin condition:
215: * <ul>
216: * <li>if the called parameter is equals to true then all parent are call
217: * with the same value, and the logging event are transmitted to the
218: * appenders. The true is return beacause the event must be transmitted</li>
219: *
220: * <li>Or if the current priority is define and the message priority is
221: * equals or greater then the current priority then all parent are call
222: * with the same value, and the logging event are transmitted to the
223: * appenders. The true is return beacause the event must be transmitted</li>
224: *
225: * <li>Else It is needed to check one of the parent is enable for the
226: * logging event. This is done by the recall of each parent. If one of the
227: * parent return true, then the event must be logged.</li>
228: * </ul>
229: * @param event is the logging event
230: * @param called is the boolean which permits to know if the current logger must
231: * call or not its appender without check its priority.
232: * return true is the logging event is enabled in the current logger or one
233: * of its ancestors.
234: */
235: public synchronized boolean callAppenders(LoggingEvent event,
236: boolean called) {
237: org.apache.log4j.Level l = event.getLevel();
238: if (called || (level != null && l.isGreaterOrEqual(level))) {
239: for (Enumeration en = getAllAppenders(); en
240: .hasMoreElements();) {
241: ((Appender) en.nextElement()).doAppend(event);
242: }
243: if (additive) {
244: if (parent instanceof MonologCategory)
245: ((MonologCategory) parent).callAppenders(event,
246: true);
247: else
248: parent.callAppenders(event);
249: for (Iterator it = topicToparents.values().iterator(); it
250: .hasNext();) {
251: org.apache.log4j.Logger c = (org.apache.log4j.Logger) it
252: .next();
253: if (c instanceof MonologCategory)
254: ((MonologCategory) c)
255: .callAppenders(event, true);
256: else
257: c.callAppenders(event);
258: }
259: }
260: return true;
261: } else if (level == null && additive) {
262: if (parent instanceof MonologCategory) {
263: called |= ((MonologCategory) parent).callAppenders(
264: event, false);
265: } else if (parent.isEnabledFor(l)) {
266: called = true;
267: parent.callAppenders(event);
268: }
269: for (Iterator it = topicToparents.values().iterator(); it
270: .hasNext();) {
271: org.apache.log4j.Logger c = (org.apache.log4j.Logger) it
272: .next();
273: if (c instanceof MonologCategory) {
274: called |= ((MonologCategory) c).callAppenders(
275: event, false);
276: } else if (c.isEnabledFor(l)) {
277: called = true;
278: c.callAppenders(event);
279: }
280: }
281: if (called) {
282: for (Enumeration en = getAllAppenders(); en
283: .hasMoreElements();) {
284: ((Appender) en.nextElement()).doAppend(event);
285: }
286: }
287: }
288: return called;
289: }
290:
291: // IMPLEMENTATION OF INTERFACE Logger
292:
293: /**
294: * Check if the level parameter are not filtered by the logger
295: */
296: public boolean isLoggable(int l) {
297: if (categ != null) {
298: return l >= categ.getEffectiveLevel().toInt();
299: }
300: return l >= getEffectiveLevel().toInt();
301: }
302:
303: public boolean isLoggable(Level l) {
304: if (categ != null) {
305: return l.getIntValue() >= categ.getEffectiveLevel().toInt();
306: }
307: return l.getIntValue() >= getEffectiveLevel().toInt();
308: }
309:
310: /**
311: * Is the handler enabled
312: */
313: public boolean isOn() {
314: return enable;
315: }
316:
317: /**
318: * Log an object with a specific level. If the level parameter is
319: * loggable the object is handled.
320: */
321: public void log(int l, Object o) {
322: if (!enable || !isLoggable(l)) {
323: return;
324: }
325: if (categ != null)
326: categ.log(convertToLog4jLevel(l), (o == null ? o : o
327: .toString()), null);
328: else
329: forcedLog(instanceFQN, convertToLog4jLevel(l),
330: (o == null ? o : o.toString()), null);
331: }
332:
333: public void log(Level l, Object o) {
334: if (!enable || !isLoggable(l.getIntValue())) {
335: return;
336: }
337: if (categ != null)
338: categ.log(convertToLog4jLevel(l.getIntValue()),
339: (o == null ? o : o.toString()), null);
340: else
341: forcedLog(instanceFQN,
342: convertToLog4jLevel(l.getIntValue()),
343: (o == null ? o : o.toString()), null);
344: }
345:
346: /**
347: * Log an object and a trowable with a specific level.
348: */
349: public void log(int l, Object o, Throwable t) {
350: if (!enable || !isLoggable(l)) {
351: return;
352: }
353: if (categ != null)
354: categ.log(convertToLog4jLevel(l), (o == null ? o : o
355: .toString()), t);
356: else
357: forcedLog(instanceFQN, convertToLog4jLevel(l),
358: (o == null ? o : o.toString()), t);
359: }
360:
361: public void log(Level l, Object o, Throwable t) {
362: if (!enable || !isLoggable(l.getIntValue())) {
363: return;
364: }
365: if (categ != null)
366: categ.log(convertToLog4jLevel(l.getIntValue()),
367: (o == null ? o : o.toString()), t);
368: else
369: forcedLog(instanceFQN,
370: convertToLog4jLevel(l.getIntValue()),
371: (o == null ? o : o.toString()), t);
372: }
373:
374: /**
375: * Log an object and a trowable with a specific level. This method
376: * permits to specify an object instance and a method.
377: */
378: public void log(int l, Object o, Object location, Object method) {
379: if (!enable || !isLoggable(l)) {
380: return;
381: }
382: if (categ != null)
383: categ.log(convertToLog4jLevel(l), (o == null ? o : o
384: .toString()), null);
385: else
386: forcedLog(instanceFQN, convertToLog4jLevel(l),
387: (location == null ? "" : location.toString())
388: + (method == null ? "" : method.toString())
389: + (o == null ? o : o.toString()), null);
390: }
391:
392: public void log(Level l, Object o, Object location, Object method) {
393: if (!enable || !isLoggable(l.getIntValue())) {
394: return;
395: }
396: if (categ != null)
397: categ.log(convertToLog4jLevel(l.getIntValue()),
398: (o == null ? o : o.toString()), null);
399: else
400: forcedLog(instanceFQN,
401: convertToLog4jLevel(l.getIntValue()),
402: (location == null ? "" : location.toString())
403: + (method == null ? "" : method.toString())
404: + (o == null ? o : o.toString()), null);
405: }
406:
407: /**
408: * Log an object and a trowable with a specific level. This method
409: * permits to specify an object instance and a method.
410: */
411: public void log(int l, Object o, Throwable t, Object location,
412: Object method) {
413: if (!enable || !isLoggable(l)) {
414: return;
415: }
416: if (categ != null)
417: categ.log(convertToLog4jLevel(l), (o == null ? o : o
418: .toString()), t);
419: else
420: forcedLog(instanceFQN, convertToLog4jLevel(l),
421: (location == null ? "" : location.toString())
422: + (method == null ? "" : method.toString())
423: + (o == null ? o : o.toString()), t);
424: }
425:
426: public void log(Level l, Object o, Throwable t, Object location,
427: Object method) {
428: if (!enable || !isLoggable(l.getIntValue())) {
429: return;
430: }
431: if (categ != null)
432: categ.log(convertToLog4jLevel(l.getIntValue()),
433: (o == null ? o : o.toString()), t);
434: else
435: forcedLog(instanceFQN,
436: convertToLog4jLevel(l.getIntValue()),
437: (location == null ? "" : location.toString())
438: + (method == null ? "" : method.toString())
439: + (o == null ? o : o.toString()), t);
440: }
441:
442: /**
443: * Enable the handler
444: */
445: public void turnOn() {
446: enable = true;
447: }
448:
449: /**
450: * Disable the handler
451: */
452: public void turnOff() {
453: enable = false;
454: }
455:
456: // IMPLEMENTATION OF INTERFACE TopicalLogger
457:
458: /**
459: * Set the current level of the logger
460: */
461: public void setIntLevel(int level) {
462: if (level == BasicLevel.INHERIT) {
463: if (categ != null)
464: categ.setLevel(null);
465: else
466: super .setLevel(null);
467: return;
468: }
469: if (categ != null)
470: categ.setLevel(convertToLog4jLevel(level));
471: else
472: super .setLevel(convertToLog4jLevel(level));
473: }
474:
475: public void setLevel(Level l) {
476: if (l == null || l.getIntValue() == BasicLevel.INHERIT) {
477: if (categ != null)
478: categ.setLevel(null);
479: else
480: super .setLevel(null);
481: return;
482: }
483: if (categ != null)
484: categ.setLevel(convertToLog4jLevel(l.getIntValue()));
485: else
486: super .setLevel(convertToLog4jLevel(l.getIntValue()));
487: }
488:
489: /**
490: * Return the current Level of the logger
491: */
492: public int getCurrentIntLevel() {
493: if (categ != null)
494: return categ.getLevel().toInt();
495: else
496: return (level != null ? level.toInt() : BasicLevel.INHERIT);
497: }
498:
499: public Level getCurrentLevel() {
500: org.apache.log4j.Level p = null;
501: if (categ != null)
502: p = categ.getLevel();
503: else
504: p = level;
505: if (p != null)
506: return LevelImpl.getLevel(p.toInt());
507: else
508: return BasicLevel.LEVEL_INHERIT;
509: }
510:
511: /**
512: * Add a handler in the Handler list of the topicalLogger
513: */
514: public void addHandler(Handler h) throws Exception {
515: if (h instanceof Appender) {
516: if (categ != null)
517: categ.addAppender((Appender) h);
518: else
519: super .addAppender((Appender) h);
520: } else
521: throw new UnsupportedOperationException(
522: "The type of the handler does not match with this wrapper");
523: }
524:
525: /**
526: * Add a topic to the topicalLogger
527: */
528: public void addTopic(String topic) throws Exception {
529: if (categ == null) {
530: Object p = parent;
531: String n = name;
532: org.apache.log4j.Logger.getLogger(topic, new BetaCF(this ));
533: topicToparents.put(topic, parent);
534: parent = (org.apache.log4j.Logger) p;
535: name = n;
536: }
537: }
538:
539: public Handler[] getHandler() {
540: ArrayList al = new ArrayList();
541: if (categ != null) {
542: for (Enumeration en = categ.getAllAppenders(); en
543: .hasMoreElements();) {
544: Appender a = (Appender) en.nextElement();
545: if (a instanceof Handler) {
546: al.add(a);
547: } else {
548: al.add(new GenericHandler(a));
549: }
550: }
551: } else {
552: for (Enumeration en = getAllAppenders(); en
553: .hasMoreElements();) {
554: Appender a = (Appender) en.nextElement();
555: if (a instanceof Handler) {
556: al.add(a);
557: } else {
558: al.add(new GenericHandler(a));
559: }
560: }
561: }
562: return (Handler[]) al.toArray(new Handler[0]);
563: }
564:
565: public Handler getHandler(String hn) {
566: Appender a = null;
567: if (categ != null) {
568: a = categ.getAppender(hn);
569: } else {
570: a = getAppender(hn);
571: }
572: if (a instanceof Handler) {
573: return (Handler) a;
574: } else {
575: return new GenericHandler(a);
576: }
577: }
578:
579: public void removeAllHandlers() throws Exception {
580: if (categ != null) {
581: categ.removeAllAppenders();
582: } else {
583: removeAllAppenders();
584: }
585: }
586:
587: /**
588: * Returns the list of the different names of the topicalLogger
589: */
590: public Enumeration getTopics() {
591: return new EnumrationImpl(topicToparents.keySet().toArray(
592: new String[0]));
593: }
594:
595: /**
596: * Returns the list of the different names of the topicalLogger
597: */
598: public String[] getTopic() {
599: String[] res = null;
600: if (categ != null) {
601: res = new String[1];
602: res[0] = AbstractFactory.getTopicWithoutPrefix(categ
603: .getName());
604: return res;
605: }
606: res = new String[topicToparents.size() + 1];
607: res[0] = AbstractFactory.getTopicWithoutPrefix(name);
608: int i = 1;
609: for (Iterator it = topicToparents.keySet().iterator(); it
610: .hasNext();) {
611: res[i] = AbstractFactory.getTopicWithoutPrefix((String) it
612: .next());
613: i++;
614: }
615: return res;
616: }
617:
618: /**
619: * Remove a handler from the Handler list of the topicalLogger
620: */
621: public void removeHandler(Handler h) throws Exception {
622: if (h instanceof Appender) {
623: if (categ != null) {
624: categ.removeAppender((Appender) h);
625: if (h instanceof GenericHandler) {
626: categ.removeAppender(((GenericHandler) h)
627: .getAppender());
628: }
629: } else {
630: super .removeAppender((Appender) h);
631: if (h instanceof GenericHandler) {
632: super .removeAppender(((GenericHandler) h)
633: .getAppender());
634: }
635: }
636: } else
637: throw new UnsupportedOperationException(
638: "The type of the handler does not match with this wrapper");
639: }
640:
641: /**
642: * Remove a topic from the topicalLogger
643: */
644: public void removeTopic(String topic) throws Exception {
645: if (categ == null)
646: topicToparents.remove(topic);
647: }
648:
649: // IMPLEMENTATION OF THE Handler INTERFACE //
650: //-----------------------------------------//
651: public void setName(String n) {
652: name = n;
653: }
654:
655: public String getType() {
656: return "logger";
657: }
658:
659: public String[] getAttributeNames() {
660: return new String[0];
661: }
662:
663: public Object getAttribute(String name) {
664: return null;
665: }
666:
667: public Object setAttribute(String name, Object value) {
668: return null;
669: }
670:
671: // INNER CLASSES //
672: //---------------//
673: public static class OwPriority extends org.apache.log4j.Level {
674:
675: protected int level = 10000;
676:
677: public OwPriority(int l) {
678: super (l, "INTER", l);
679: this .level = l;
680: }
681:
682: public boolean isGreaterOrEqual(Priority pr) {
683: return level >= pr.toInt();
684: }
685: }
686:
687: public static class BetaCF implements
688: org.apache.log4j.spi.LoggerFactory {
689: MonologCategory mc = null;
690:
691: public BetaCF(MonologCategory mc) {
692: this .mc = mc;
693: }
694:
695: public org.apache.log4j.Logger makeNewLoggerInstance(String name) {
696: return mc;
697: }
698: }
699:
700: }
|