1   package org.slf4j.ext;
2   
3   import org.slf4j.Logger;
4   import org.slf4j.Marker;
5   import org.slf4j.MarkerFactory;
6   import org.slf4j.helpers.FormattingTuple;
7   import org.slf4j.helpers.MessageFormatter;
8   import org.slf4j.spi.LocationAwareLogger;
9   
10  /**
11   * A utility that provides standard mechanisms for logging certain kinds of
12   * activities.
13   * 
14   * @author Ralph Goers
15   * @author Ceki Gülcü
16   */
17  public class XLogger extends LoggerWrapper implements Logger {
18  
19    private static final String FQCN = XLogger.class.getName();
20    static Marker FLOW_MARKER = MarkerFactory.getMarker("FLOW");
21    static Marker ENTRY_MARKER = MarkerFactory.getMarker("ENTRY");
22    static Marker EXIT_MARKER = MarkerFactory.getMarker("EXIT");
23  
24    static Marker EXCEPTION_MARKER = MarkerFactory.getMarker("EXCEPTION");
25    static Marker THROWING_MARKER = MarkerFactory.getMarker("THROWING");
26    static Marker CATCHING_MARKER = MarkerFactory.getMarker("CATCHING");
27  
28    static String EXIT_MESSAGE_0 = "exit";
29    static String EXIT_MESSAGE_1 = "exit with ({})";
30  
31    static String ENTRY_MESSAGE_0 = "entry";
32    static String ENTRY_MESSAGE_1 = "entry with ({})";
33    static String ENTRY_MESSAGE_2 = "entry with ({}, {})";
34    static String ENTRY_MESSAGE_3 = "entry with ({}, {}, {})";
35    static String ENTRY_MESSAGE_4 = "entry with ({}, {}, {}, {})";
36    static int ENTRY_MESSAGE_ARRAY_LEN = 5;
37    static String[] ENTRY_MESSAGE_ARRAY = new String[ENTRY_MESSAGE_ARRAY_LEN];
38    static {
39      ENTRY_MARKER.add(FLOW_MARKER);
40      EXIT_MARKER.add(FLOW_MARKER);
41      THROWING_MARKER.add(EXCEPTION_MARKER);
42      CATCHING_MARKER.add(EXCEPTION_MARKER);
43  
44      ENTRY_MESSAGE_ARRAY[0] = ENTRY_MESSAGE_0;
45      ENTRY_MESSAGE_ARRAY[1] = ENTRY_MESSAGE_1;
46      ENTRY_MESSAGE_ARRAY[2] = ENTRY_MESSAGE_2;
47      ENTRY_MESSAGE_ARRAY[3] = ENTRY_MESSAGE_3;
48      ENTRY_MESSAGE_ARRAY[4] = ENTRY_MESSAGE_4;
49    }
50  
51    public enum Level {
52      TRACE("TRACE", LocationAwareLogger.TRACE_INT), DEBUG("DEBUG",
53          LocationAwareLogger.DEBUG_INT), INFO("INFO",
54          LocationAwareLogger.INFO_INT), WARN("WARN",
55          LocationAwareLogger.WARN_INT), ERROR("ERROR",
56          LocationAwareLogger.ERROR_INT);
57  
58      private final String name;
59      private final int level;
60  
61      public String toString() {
62        return this.name;
63      }
64  
65      public int intValue() {
66        return this.level;
67      }
68  
69      private Level(String name, int level) {
70        this.name = name;
71        this.level = level;
72      }
73    }
74  
75    /**
76     * Given an underlying logger, construct an XLogger
77     * 
78     * @param logger
79     *          underlying logger
80     */
81    public XLogger(Logger logger) {
82      // If class B extends A, assuming B does not override method x(), the caller
83      // of new B().x() is A and not B, see also
84      // http://bugzilla.slf4j.org/show_bug.cgi?id=114
85      super(logger, LoggerWrapper.class.getName());
86    }
87  
88    /**
89     * Log method entry.
90     * 
91     * @param argArray
92     *          supplied parameters
93     */
94    public void entry(Object... argArray) {
95      if (instanceofLAL && logger.isTraceEnabled(ENTRY_MARKER)) {
96        String messagePattern = null;
97        if (argArray.length < ENTRY_MESSAGE_ARRAY_LEN) {
98          messagePattern = ENTRY_MESSAGE_ARRAY[argArray.length];
99        } else {
100         messagePattern = buildMessagePattern(argArray.length);
101       }
102       FormattingTuple tp = MessageFormatter.arrayFormat(messagePattern, argArray);
103       ((LocationAwareLogger) logger).log(ENTRY_MARKER, FQCN,
104           LocationAwareLogger.TRACE_INT, tp.getMessage(), argArray, tp.getThrowable());
105     }
106   }
107 
108   /**
109    * Log method exit
110    */
111   public void exit() {
112     if (instanceofLAL && logger.isTraceEnabled(ENTRY_MARKER)) {
113       ((LocationAwareLogger) logger).log(EXIT_MARKER, FQCN,
114           LocationAwareLogger.TRACE_INT, EXIT_MESSAGE_0, null, null);
115     }
116   }
117 
118   /**
119    * Log method exit
120    * 
121    * @param result
122    *          The result of the method being exited
123    */
124   public void exit(Object result) {
125     if (instanceofLAL && logger.isTraceEnabled(ENTRY_MARKER)) {
126       FormattingTuple tp = MessageFormatter.format(EXIT_MESSAGE_1, result);
127       ((LocationAwareLogger) logger).log(EXIT_MARKER, FQCN,
128           LocationAwareLogger.TRACE_INT, tp.getMessage(),
129           new Object[] { result }, tp.getThrowable());
130     }
131   }
132 
133   /**
134    * Log an exception being thrown. The generated log event uses Level ERROR.
135    * 
136    * @param throwable
137    *          the exception being caught.
138    */
139   public void throwing(Throwable throwable) {
140     if (instanceofLAL) {
141       ((LocationAwareLogger) logger).log(THROWING_MARKER, FQCN,
142           LocationAwareLogger.ERROR_INT, "throwing", null, throwable);
143     }
144   }
145 
146   /**
147    * Log an exception being thrown allowing the log level to be specified.
148    * 
149    * @param level
150    *          the logging level to use.
151    * @param throwable
152    *          the exception being caught.
153    */
154   public void throwing(Level level, Throwable throwable) {
155     if (instanceofLAL) {
156       ((LocationAwareLogger) logger).log(THROWING_MARKER, FQCN, level.level,
157           "throwing", null, throwable);
158     }
159   }
160 
161   /**
162    * Log an exception being caught. The generated log event uses Level ERROR.
163    * 
164    * @param throwable
165    *          the exception being caught.
166    */
167   public void catching(Throwable throwable) {
168     if (instanceofLAL) {
169       ((LocationAwareLogger) logger).log(CATCHING_MARKER, FQCN,
170           LocationAwareLogger.ERROR_INT, "catching", null, throwable);
171     }
172   }
173 
174   /**
175    * Log an exception being caught allowing the log level to be specified.
176    * 
177    * @param level
178    *          the logging level to use.
179    * @param throwable
180    *          the exception being caught.
181    */
182   public void catching(Level level, Throwable throwable) {
183     if (instanceofLAL) {
184       ((LocationAwareLogger) logger).log(CATCHING_MARKER, FQCN, level.level,
185           "catching", null, throwable);
186     }
187   }
188 
189   private static String buildMessagePattern(int len) {
190     StringBuilder sb = new StringBuilder();
191     sb.append(" entry with (");
192     for (int i = 0; i < len; i++) {
193       sb.append("{}");
194       if (i != len - 1)
195         sb.append(", ");
196     }
197     sb.append(')');
198     return sb.toString();
199   }
200 }