001: /*
002: * This file or a portion of this file is licensed under the terms of
003: * the Globus Toolkit Public License, found in file GTPL, or at
004: * http://www.globus.org/toolkit/download/license.html. This notice must
005: * appear in redistributions of this file, with or without modification.
006: *
007: * Redistributions of this Software, with or without modification, must
008: * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
009: * some other similar material which is provided with the Software (if
010: * any).
011: *
012: * Copyright 1999-2004 University of Chicago and The University of
013: * Southern California. All rights reserved.
014: */
015: package org.griphyn.cPlanner.common;
016:
017: import org.griphyn.common.util.Currently;
018:
019: import org.apache.log4j.Level;
020:
021: import java.io.OutputStream;
022: import java.io.PrintWriter;
023: import java.io.File;
024: import java.io.FileOutputStream;
025: import java.io.IOException;
026: import java.io.Writer;
027:
028: import java.util.Stack;
029:
030: /**
031: * The logging class that to log messages at different levels.
032: * Currently the following levels are supported.<p>
033: *
034: * Eventually, each of the level can have a different writer stream underneath.
035: *
036: * <p>
037: * The messages can be logged at various levels. The various levels of logging
038: * with increasing levels of verbosity are displayed in the following table.
039: *
040: * <p>
041: * <table border="1">
042: * <tr align="left"><th>Logging Level</th><th>Description</th></tr>
043: * <tr align="left"><th>FATAL</th>
044: * <td>all fatal error messages are logged in this level.</td>
045: * </tr>
046: * <tr align="left"><th>ERROR</th>
047: * <td>all non fatal error messages are logged in this level.</td>
048: * </tr>
049: * <tr align="left"><th>WARNING</th>
050: * <td>all warning messages are logged in this level.</td>
051: * </tr>
052: * <tr align="left"><th>INFO</th>
053: * <td>all information logging messages are logged in this level.</td>
054: * </tr>
055: * <tr align="left"><th>CONFIG</th>
056: * <td>all configuration messages are logged in this level.</td>
057: * </tr>
058: * <tr align="left"><th>DEBUG</th>
059: * <td>all debug messages are logged in this level.</td>
060: * </tr>
061: * </table>
062:
063: *
064: * @author Karan Vahi
065: * @author Gaurang Mehta
066: * @version $Revision: 158 $
067: */
068: public class LogManager {
069:
070: //level constants that loosely match Log4J and are used
071: //to generate the appropriate mask values.
072:
073: /**
074: * The level value, to indicate a FATAL error message.
075: */
076: public static final int FATAL_MESSAGE_LEVEL = 0;
077:
078: /**
079: * The level value, to indicate an ERROR message.
080: */
081: public static final int ERROR_MESSAGE_LEVEL = 1;
082:
083: /**
084: * The level value, to indicate a WARNING error message.
085: */
086: public static final int WARNING_MESSAGE_LEVEL = 2;
087:
088: /**
089: * The level value, to indicate a INFO message.
090: */
091: public static final int INFO_MESSAGE_LEVEL = 3;
092:
093: /**
094: * The level value, to indicate a CONFIG message.
095: */
096: public static final int CONFIG_MESSAGE_LEVEL = 4;
097:
098: /**
099: * The level value, to indicate a DEBUG message.
100: */
101: public static final int DEBUG_MESSAGE_LEVEL = 5;
102:
103: /**
104: * The type value to indicate a FATAL error message.
105: */
106: private static final int FATAL_MESSAGE_TYPE = 0x1;
107:
108: /**
109: * The type value to indicate an ERROR message.
110: */
111: private static final int ERROR_MESSAGE_TYPE = 0x2;
112:
113: /**
114: * The type value to indicate a WARNING message.
115: */
116: private static final int WARNING_MESSAGE_TYPE = 0x4;
117:
118: /**
119: * The type value to indicate an INFORMATIVE message.
120: */
121: private static final int INFO_MESSAGE_TYPE = 0x8;
122:
123: /**
124: * The type value to indicate a CONFIG message.
125: */
126: private static final int CONFIG_MESSAGE_TYPE = 0x10;
127:
128: /**
129: * The type value to indicate a DEBUG message.
130: */
131: private static final int DEBUG_MESSAGE_TYPE = 0x20;
132:
133: /**
134: * Ensures only one object is created always. Implements the Singleton.
135: */
136: private static LogManager logger;
137:
138: /**
139: * The debug level. Higher the level the more the detail is logged. At present
140: * can be 0 or 1. This is set according to the option given by the user, whether
141: * verbose or not.
142: */
143: private int mDebugLevel;
144:
145: /**
146: * The stream to which one writes. It is System.out by default for the
147: * current release. One can set it using setOutputWriter.
148: *
149: * @see #setOutputWriter
150: */
151: private PrintWriter mWriter;
152:
153: /**
154: * The stream to which all the error messages are logged.By default it is
155: * System.err
156: */
157: private PrintWriter mErrWriter;
158:
159: /**
160: * The mask that needs to be deployed to determine what messages are to be
161: * logged.
162: */
163: private int mMask;
164:
165: /**
166: * This is used to format the time stamp.
167: */
168: private static Currently mFormatter;
169:
170: /**
171: * The constructor.
172: */
173: private LogManager() {
174: mDebugLevel = 0;
175: mWriter = new PrintWriter(System.out, true);
176: mErrWriter = new PrintWriter(System.err, true);
177: LogManager.mFormatter = new Currently(
178: "yyyy.MM.dd HH:mm:ss.SSS zzz: ");
179: //by default we are logging only INFO
180: //and all message less than WARN
181: mMask = generateMask(WARNING_MESSAGE_LEVEL, true);
182: }
183:
184: /**
185: * To get a reference to the the object.
186: *
187: * @return a singleton access to the object.
188: */
189: public static LogManager getInstance() {
190: if (logger == null) {
191: logger = new LogManager();
192: }
193: return logger;
194: }
195:
196: /**
197: * Checks the destination location for existence, if it can
198: * be created, if it is writable etc.
199: *
200: * @param file is the file to write out to.
201: *
202: * @throws IOException in case of error while writing out files.
203: */
204: private static void sanityCheckOnFile(File file) throws IOException {
205: if (file.exists()) {
206: // location exists
207: if (file.isFile()) {
208: // ok, is a file
209: if (file.canWrite()) {
210: // can write, all is well
211: return;
212: } else {
213: // all is there, but I cannot write to file
214: throw new IOException(
215: "Cannot write to existing file "
216: + file.getAbsolutePath());
217: }
218: } else {
219: // exists but not a file
220: throw new IOException("File " + file.getAbsolutePath()
221: + " already " + "exists, but is not a file.");
222: }
223: } else {
224: // check to see if you can write to the parent directory
225: //could have tried to do just a make dir on parent directory.
226: sanityCheckOnDirectory(file.getParentFile());
227: }
228: }
229:
230: /**
231: * Checks the destination location for existence, if it can
232: * be created, if it is writable etc.
233: *
234: * @param dir is the new base directory to optionally create.
235: *
236: * @throws IOException in case of error while writing out files.
237: */
238: private static void sanityCheckOnDirectory(File dir)
239: throws IOException {
240: if (dir.exists()) {
241: // location exists
242: if (dir.isDirectory()) {
243: // ok, isa directory
244: if (dir.canWrite()) {
245: // can write, all is well
246: return;
247: } else {
248: // all is there, but I cannot write to dir
249: throw new IOException(
250: "Cannot write to existing directory "
251: + dir.getPath());
252: }
253: } else {
254: // exists but not a directory
255: throw new IOException("Destination " + dir.getPath()
256: + " already "
257: + "exists, but is not a directory.");
258: }
259: } else {
260: // does not exist, try to make it
261: if (!dir.mkdirs()) {
262: throw new IOException(
263: "Unable to create directory destination "
264: + dir.getPath());
265: }
266: }
267: }
268:
269: /**
270: * Sets the debug level. All those messages are logged which have a
271: * level less than equal to the debug level.
272: *
273: * @param level the level to which the debug level needs to be set to.
274: */
275: public void setLevel(Level level) {
276: int value = level.toInt();
277: switch (value) {
278: case Level.DEBUG_INT:
279: value = this .DEBUG_MESSAGE_LEVEL;
280: break;
281:
282: case Level.INFO_INT:
283: value = this .INFO_MESSAGE_LEVEL;
284: break;
285:
286: case Level.WARN_INT:
287: value = this .WARNING_MESSAGE_LEVEL;
288: break;
289:
290: case Level.ERROR_INT:
291: value = this .ERROR_MESSAGE_LEVEL;
292: break;
293:
294: default:
295: value = this .FATAL_MESSAGE_LEVEL;
296: break;
297: }
298: setLevel(value, false);
299: }
300:
301: /**
302: * Sets the debug level. All those messages are logged which have a
303: * level less than equal to the debug level. In addition the info messages
304: * are always logged.
305: *
306: * @param level the level to which the debug level needs to be set to.
307: */
308: public void setLevel(int level) {
309: setLevel(level, true);
310: }
311:
312: /**
313: * Sets the debug level. All those messages are logged which have a
314: * level less than equal to the debug level. In case the boolean info
315: * is set, all the info messages are also logged.
316: *
317: * @param level the level to which the debug level needs to be set to.
318: * @param info boolean denoting whether the INFO messages need to be
319: * logged or not.
320: */
321: public void setLevel(int level, boolean info) {
322: mDebugLevel = level;
323: mMask = generateMask(level, info);
324: }
325:
326: /**
327: * Returns the debug level.
328: *
329: * @return the level to which the debug level has been set to.
330: */
331: public int getLevel() {
332: return mDebugLevel;
333: }
334:
335: /**
336: * Sets both the output writer and the error writer to the same
337: * underlying writer.
338: *
339: * @param out is the name of a file to append to. Special names are
340: * <code>stdout</code> and <code>stderr</code>, which map to the
341: * system's respective streams.
342: *
343: * @see #setWriters(OutputStream)
344: */
345: public void setWriters(String out) {
346: try {
347: mWriter = (PrintWriter) getWriter(out);
348: mErrWriter = mWriter;
349: } catch (IOException e) {
350: //log on the existing streams !!!
351: log("Unable to set streams for logging ", e,
352: this .WARNING_MESSAGE_LEVEL);
353: }
354:
355: }
356:
357: /**
358: * Sets both the output writer and the error writer to the same
359: * underlying writer.
360: *
361: * Note: The previous stream is not closed automatically.
362: *
363: * @param err the stream to which error messages are to be logged.
364: */
365: public void setWriters(OutputStream err) {
366: mWriter = new PrintWriter(err, true);
367: mErrWriter = mWriter;
368: }
369:
370: /**
371: * Sets the writer associated with the class to the one specified for all
372: * type of messages other than error messages.
373: *
374: * @param out is the name of a file to append to. Special names are
375: * <code>stdout</code> and <code>stderr</code>, which map to the
376: * system's respective streams.
377: *
378: * @see #setOutputWriter(OutputStream)
379: */
380: public void setOutputWriter(String out) {
381: try {
382: mWriter = (PrintWriter) getWriter(out);
383: } catch (IOException e) {
384: //log on the existing streams !!!
385: log("Unable to set streams for logging ", e,
386: this .WARNING_MESSAGE_LEVEL);
387: }
388: }
389:
390: /**
391: * Sets the writer associated with the class to the one specified for all
392: * type of messages other than error messages.
393: * By default it is System.out.
394: *
395: * @param out the stream to which the messages are logged.
396: *
397: * @see #setErrorWriter(OutputStream)
398: */
399: public void setOutputWriter(OutputStream out) {
400: mWriter = new PrintWriter(out, true);
401: }
402:
403: /**
404: * Certains levels like FATAL, ERROR and WARN can be set to log to a
405: * different stream, than the default stream used for writing other messages.
406: * By default, these messages are logged to stderr.
407: * Note: The previous stream is not closed automatically.
408: *
409: * @param out is the name of a file to append to. Special names are
410: * <code>stdout</code> and <code>stderr</code>, which map to the
411: * system's respective streams.
412: *
413: * @see #setErrorWriter(OutputStream)
414: */
415: public void setErrorWriter(String out) {
416: try {
417: mErrWriter = (PrintWriter) getWriter(out);
418: } catch (IOException e) {
419: //log on the existing streams !!!
420: log("Unable to set streams for logging ", e,
421: this .WARNING_MESSAGE_LEVEL);
422: }
423:
424: }
425:
426: /**
427: * Certains levels like FATAL, ERROR and WARN can be set to log to a
428: * different stream, than the default stream used for writing other messages.
429: * By default, these messages are logged to stderr.
430: * Note: The previous stream is not closed automatically.
431: *
432: * @param err the stream to which error messages are to be logged.
433: */
434: public void setErrorWriter(OutputStream err) {
435: mErrWriter = new PrintWriter(err, true);
436: }
437:
438: /**
439: * Logs the exception on the appropriate queue if the level of the message
440: * is less than or equal to the level set for the Logger. For INFO level
441: * message, the boolean indicating that a completion message is to follow
442: * is set to true always.
443: *
444: * @param message the message to be logged.
445: * @param e the exception to be logged
446: * @param level the level on which the message has to be logged.
447: *
448: * @see #setLevel(int)
449: * @see #log(String,int)
450: */
451: public void log(String message, Exception e, int level) {
452: StringBuffer msg = new StringBuffer();
453: msg.append(message).append(" ").append(e.getClass()).append(
454: ": ").append(e.getMessage());
455: log(msg.toString(), level);
456: }
457:
458: /**
459: * Logs the message on the appropriate queue if the level of the message
460: * is less than or equal to the level set for the Logger. For INFO level
461: * message, the boolean indicating that a completion message is to follow
462: * is set to true always.
463: *
464: * @param message the message to be logged.
465: * @param level the level on which the message has to be logged.
466: *
467: * @see #setLevel(int)
468: * @see #log(String,int,boolean)
469: */
470: public void log(String message, int level) {
471: log(message, level, (level == this .INFO_MESSAGE_LEVEL) ? true
472: : false);
473: }
474:
475: /**
476: * Logs the message on the appropriate queue if the level of the message
477: * is less than or equal to the level set for the Logger.
478: *
479: * @param message the message to be logged.
480: * @param level the level on which the message has to be logged.
481: * @param comp boolean indicating whether a completion message
482: * follows or not.
483: *
484: * @see #setLevel(int)
485: */
486: private void log(String message, int level, boolean comp) {
487: int type = (int) Math.pow(2, level);
488: if ((type & mMask) != 0x0) {
489: //we need to log the message
490: //get hold of the writer to be used to logging the message.
491: PrintWriter writer = getWriter(level);
492: writer.print(LogManager.mFormatter.now());
493: String prefix = getPrefix(type);
494: message = prefix + " " + message;
495: /*
496: *uncomment if we want commpetion message for INFO
497: *on same line
498: if(comp){
499: if((mMask & INFO_MESSAGE_TYPE) == INFO_MESSAGE_TYPE){
500: //we need to just print the message
501: writer.print(message);
502: }
503: else{
504: //write out on a new line and
505: //push the message to the stack
506: writer.println(message);
507: // mMsgStack.push(message);
508: }
509: }
510: else{
511: writer.println(message);
512: }
513: */
514: writer.println(message);
515: writer.flush();
516: }
517: }
518:
519: /**
520: * Gets the timestamp nicely formatted. It generates the date-timestamp
521: * in extended ISO 8601 format. It generates the timestamp using
522: * the local timezone not the UTC. An example of the date-timestamp
523: * generated would be 2003-06-06T14:31:27-07:00 where -07:00 denotes the timezone
524: * offset of the local timezone from UTC.
525: *
526: * @return the formattted timestamp;
527: */
528: public String getTimeStamp() {
529: String st = LogManager.mFormatter.now();
530:
531: st = Currently.iso8601(false);
532:
533: return st;
534: }
535:
536: /**
537: * Logs the completion message on the basis of the debug level.
538: *
539: * @param message the message to be logged.
540: * @param level the debug level of the start message for whose completion
541: * you want.
542: */
543: public void logCompletion(String message, int level) {
544: int type = (int) Math.pow(2, level);
545: if ((type & mMask) != 0x0) {
546: PrintWriter writer = getWriter(level);
547: /*uncomment if we want commpetion message for INFO
548: on same line
549: if ( (mMask & INFO_MESSAGE_TYPE) == INFO_MESSAGE_TYPE) {
550: writer.println(" (completed)");
551: }
552: else {
553: writer.print(LogManager.mFormatter.now());
554: writer.println(message + " (completed)");
555: }
556: */
557: String prefix = getPrefix(type);
558: message = prefix + " " + message;
559: writer.print(LogManager.mFormatter.now());
560: writer.println(message + " (completed)");
561: }
562: }
563:
564: /**
565: * Generates the appropriate mask value, corresponding to the level
566: * passed.
567: *
568: * @param level the level to which the debug level needs to be set to.
569: * @param info boolean denoting whether the INFO messages need to be
570: * logged or not.
571: *
572: * @return mask corresponding to the debug level passed.
573: */
574: private int generateMask(int level, boolean info) {
575:
576: //construct the appropriate mask
577: int mask = 0x0;
578: for (int i = 0; i <= level; i++) {
579: mask |= (int) Math.pow(2, i);
580: }
581: if (info) {
582: mask |= INFO_MESSAGE_TYPE;
583: }
584: return mask;
585: }
586:
587: /**
588: * Returns the prefix that needs to be logged corresponding to a particular
589: * message type, when a message is being logged.
590: * Should be returning an enumerated data type.
591: *
592: * @param type the type for which prefix is required.
593: *
594: * @return the message type
595: */
596: private String getPrefix(int type) {
597: String result = null;
598: switch (type) {
599: case FATAL_MESSAGE_TYPE:
600: result = "[FATAL ERROR]";
601: break;
602:
603: case ERROR_MESSAGE_TYPE:
604: result = "[ERROR]";
605: break;
606:
607: case WARNING_MESSAGE_TYPE:
608: result = "[WARNING]";
609: break;
610:
611: case INFO_MESSAGE_TYPE:
612: result = "[INFO]";
613: break;
614:
615: case CONFIG_MESSAGE_TYPE:
616: result = "[CONFIG]";
617: break;
618:
619: case DEBUG_MESSAGE_TYPE:
620: result = "[DEBUG]";
621: break;
622:
623: default:
624: result = "[UNKNOWN]";
625: }
626: return result;
627: }
628:
629: /**
630: * Sets an internal writer to point to a particular stream.
631: *
632: * @param out is the name of a file to append to. Special names are
633: * <code>stdout</code> and <code>stderr</code>, which map to the
634: * system's respective streams.
635: *
636: * @return the corresponding writer.
637: *
638: * @throws IOException in case of being unable to open a stream.
639: */
640: private Writer getWriter(String out) throws IOException {
641: //check if value refers to any of the predefined streams
642: OutputStream stream;
643: if (out.equalsIgnoreCase("stdout")) {
644: stream = System.out;
645: } else if (out.equalsIgnoreCase("stderr")) {
646: stream = System.err;
647: } else {
648: //try to create an output stream to file specified
649: File f = new File(out);
650:
651: //do some sanity checks on file
652: sanityCheckOnFile(f);
653: stream = new FileOutputStream(f);
654: }
655: return new PrintWriter(stream);
656: }
657:
658: /**
659: * Returns a PrintWriter stream on which to log the message. Later on
660: * this, function would return the appropriate LOG4J queue on which
661: * the message needs to be logged.
662: *
663: * @param level the level
664: *
665: * @return PrintWriter for logging the message.
666: */
667: private PrintWriter getWriter(int level) {
668: return (level >= FATAL_MESSAGE_LEVEL && level <= WARNING_MESSAGE_LEVEL) ? mErrWriter
669: : mWriter;
670: }
671: }
|