View Javadoc

1   /*
2      Copyright 2008 Ramon Servadei
3   
4      Licensed under the Apache License, Version 2.0 (the "License");
5      you may not use this file except in compliance with the License.
6      You may obtain a copy of the License at
7   
8          http://www.apache.org/licenses/LICENSE-2.0
9   
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15   */
16  package fulmine.util.log;
17  
18  import static fulmine.util.Utils.string;
19  
20  import java.text.SimpleDateFormat;
21  import java.util.Date;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  
26  import fulmine.util.concurrent.ITaskExecutor;
27  import fulmine.util.concurrent.ITaskHandler;
28  import fulmine.util.concurrent.Task;
29  import fulmine.util.concurrent.TaskExecutor;
30  
31  /**
32   * Wraps a {@link Log} and executes the logging operations using an
33   * {@link ITaskExecutor}. This ensures that, regardless of the logging framework
34   * and configuration, all internal logging is done asynchronously.
35   * 
36   * @author Ramon Servadei
37   */
38  public final class AsyncLog implements Log
39  {
40      /** The single task handler to use for all logging tasks */
41      private final static LogTaskHandler handler = new LogTaskHandler();
42  
43      /** Handles all logging tasks - polls every 5ms for messages */
44      static final ITaskExecutor executor =
45      // note: no info container
46          new TaskExecutor("Log", 5, null);
47      static
48      {
49          System.out.println("Starting logging executor");
50          Runtime.getRuntime().addShutdownHook(new Thread()
51          {
52              public void run()
53              {
54                  executor.destroy();
55              }
56          });
57          executor.start();
58          System.out.println("Started logging executor");
59      }
60  
61      /** log that is wrapped */
62      private final Log log;
63  
64      /**
65       * Create an internal {@link Log} identified by the name
66       * 
67       * @param name
68       *            the name for the {@link Log}
69       */
70      public AsyncLog(String name)
71      {
72          this.log = LogFactory.getLog(name);
73      }
74  
75      /**
76       * Create an internal {@link Log} identified by the class
77       * 
78       * @param type
79       *            the class that will identify the {@link Log}
80       */
81      public AsyncLog(Class<?> type)
82      {
83          this.log = LogFactory.getLog(type);
84      }
85  
86      public void debug(Object message, Throwable t)
87      {
88          log(new Task<LogMessage>(handler, new LogMessage(log, Level.debug,
89              Thread.currentThread().getName(), message, t)));
90      }
91  
92      public void debug(Object message)
93      {
94          log(new Task<LogMessage>(handler, new LogMessage(log, Level.debug,
95              Thread.currentThread().getName(), message)));
96      }
97  
98      public void error(Object message, Throwable t)
99      {
100         log(new Task<LogMessage>(handler, new LogMessage(log, Level.error,
101             Thread.currentThread().getName(), message, t)));
102     }
103 
104     public void error(Object message)
105     {
106         log(new Task<LogMessage>(handler, new LogMessage(log, Level.error,
107             Thread.currentThread().getName(), message)));
108     }
109 
110     public void fatal(Object message, Throwable t)
111     {
112         log(new Task<LogMessage>(handler, new LogMessage(log, Level.fatal,
113             Thread.currentThread().getName(), message, t)));
114     }
115 
116     public void fatal(Object message)
117     {
118         log(new Task<LogMessage>(handler, new LogMessage(log, Level.fatal,
119             Thread.currentThread().getName(), message)));
120     }
121 
122     public void info(Object message, Throwable t)
123     {
124         log(new Task<LogMessage>(handler, new LogMessage(log, Level.info,
125             Thread.currentThread().getName(), message, t)));
126     }
127 
128     public void info(Object message)
129     {
130         log(new Task<LogMessage>(handler, new LogMessage(log, Level.info,
131             Thread.currentThread().getName(), message)));
132     }
133 
134     public boolean isDebugEnabled()
135     {
136         return log.isDebugEnabled();
137     }
138 
139     public boolean isErrorEnabled()
140     {
141         return log.isErrorEnabled();
142     }
143 
144     public boolean isFatalEnabled()
145     {
146         return log.isFatalEnabled();
147     }
148 
149     public boolean isInfoEnabled()
150     {
151         return log.isInfoEnabled();
152     }
153 
154     public boolean isTraceEnabled()
155     {
156         return log.isTraceEnabled();
157     }
158 
159     public boolean isWarnEnabled()
160     {
161         return log.isWarnEnabled();
162     }
163 
164     public void trace(Object message, Throwable t)
165     {
166         log(new Task<LogMessage>(handler, new LogMessage(log, Level.trace,
167             Thread.currentThread().getName(), message, t)));
168     }
169 
170     public void trace(Object message)
171     {
172         log(new Task<LogMessage>(handler, new LogMessage(log, Level.trace,
173             Thread.currentThread().getName(), message)));
174     }
175 
176     public void warn(Object message, Throwable t)
177     {
178         log(new Task<LogMessage>(handler, new LogMessage(log, Level.warn,
179             Thread.currentThread().getName(), message, t)));
180     }
181 
182     public void warn(Object message)
183     {
184         log(new Task<LogMessage>(handler, new LogMessage(log, Level.warn,
185             Thread.currentThread().getName(), message)));
186     }
187 
188     private final void log(Task<LogMessage> message)
189     {
190         AsyncLog.executor.execute(message);
191     }
192 }
193 
194 /**
195  * A stateless task handler that will process a {@link LogMessage} object.
196  * 
197  * @author Ramon Servadei
198  */
199 final class LogTaskHandler implements ITaskHandler<LogMessage>
200 {
201 
202     LogTaskHandler()
203     {
204         super();
205         System.out.println("Constructed LogTaskHandler");
206     }
207 
208     public void handleTask(LogMessage task)
209     {
210         task.level.handle(task.log, task);
211     }
212 }
213 
214 /**
215  * Encapsulates a message to log
216  * 
217  * @author Ramon Servadei
218  */
219 class LogMessage
220 {
221     /** The time of the message */
222     final Date timeStamp = new Date();
223 
224     /** The message to log */
225     final Object message;
226 
227     /** The level to log at */
228     final Level level;
229 
230     /** The log to use */
231     final Log log;
232 
233     /** The thread that raised the log message */
234     final String threadName;
235 
236     /** The {@link Throwable} to log, if any */
237     final Throwable throwable;
238 
239     LogMessage(Log log, Level level, String threadName, Object message)
240     {
241         this(log, level, threadName, message, null);
242     }
243 
244     LogMessage(Log log, Level level, String threadName, Object message,
245         Throwable throwable)
246     {
247         this.log = log;
248         this.message = message;
249         this.level = level;
250         this.threadName = threadName;
251         this.throwable = throwable;
252     }
253 
254     boolean hasException()
255     {
256         return this.throwable != null;
257     }
258 
259     @Override
260     public String toString()
261     {
262         return string(this, "level=" + this.level + ", thread="
263             + this.threadName + ", message=" + this.message + ", exception="
264             + this.throwable);
265     }
266 }
267 
268 /**
269  * Expresses logging levels and contains the logic to log to each level.
270  * 
271  * @author Ramon Servadei
272  */
273 enum Level
274 {
275     // the levels
276         trace,
277         debug,
278         info,
279         warn,
280         error,
281         fatal;
282 
283     /** Used to format the log times */
284     private final SimpleDateFormat format =
285         new SimpleDateFormat("ddMMMyy:HH:mm:ss:SSS");
286 
287     /**
288      * Handle executing the specific log operation
289      * 
290      * @param message
291      *            the message to log
292      */
293     void handle(Log log, LogMessage message)
294     {
295         switch (this)
296         {
297             case trace:
298                 if (message.hasException())
299                 {
300                     log.trace(formatMessage(message), message.throwable);
301                 }
302                 else
303                 {
304                     log.trace(formatMessage(message));
305                 }
306                 break;
307             case debug:
308                 if (message.hasException())
309                 {
310                     log.debug(formatMessage(message), message.throwable);
311                 }
312                 else
313                 {
314                     log.debug(formatMessage(message));
315                 }
316                 break;
317             case info:
318                 if (message.hasException())
319                 {
320                     log.info(formatMessage(message), message.throwable);
321                 }
322                 else
323                 {
324                     log.info(formatMessage(message));
325                 }
326                 break;
327             case warn:
328                 if (message.hasException())
329                 {
330                     log.warn(formatMessage(message), message.throwable);
331                 }
332                 else
333                 {
334                     log.warn(formatMessage(message));
335                 }
336                 break;
337             case error:
338                 if (message.hasException())
339                 {
340                     log.error(formatMessage(message), message.throwable);
341                 }
342                 else
343                 {
344                     log.error(formatMessage(message));
345                 }
346                 break;
347             case fatal:
348                 if (message.hasException())
349                 {
350                     log.fatal(formatMessage(message), message.throwable);
351                 }
352                 else
353                 {
354                     log.fatal(formatMessage(message));
355 
356                 }
357                 break;
358         }
359     }
360 
361     /**
362      * Format the message to log
363      * 
364      * @param message
365      *            the message
366      * @return the formatted message to log
367      */
368     private String formatMessage(LogMessage message)
369     {
370         return format.format(message.timeStamp) + ", Thread ["
371             + message.threadName + "] " + message.message;
372     }
373 }