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.vdl.util;
016:
017: import org.griphyn.common.util.Currently;
018: import java.io.*;
019: import java.util.*;
020:
021: /**
022: * Create a common interface to handle logging of messages,
023: * debugging and streaming. In order to avoid conflicts with
024: * JDK 1.4.*, this class is named Logging instead of Logger.<p>
025: *
026: * The logging mechanism works similar to syslog. There is an
027: * arbitrary number of user-named queues, and a "default" queue.
028: * Each queue has a level associated with it. The higher the level,
029: * the less important the message. If the message to be logged
030: * exceeds the level, it will not be logged. Level 0 will always
031: * be logged, if a queue exists for it.<p>
032: *
033: * Usage is simple. Each queue has to be registered before use. The
034: * registrations associated the output stream and maximum debug level.<p>
035: *
036: * Each log line will be prefixed by a time stamp. The logging class
037: * maintains internal state for each queue, if it requested a line feed
038: * to be printed. Thus, you are able to construct a message in several
039: * pieces, or a multi-line message by smuggling line feeds within the
040: * message.<p>
041: *
042: * @author Jens-S. Vöckler
043: * @author Yong Zhao
044: * @version $Revision: 50 $
045: *
046: */
047: public class Logging {
048: /**
049: * Keeper of the Singleton.
050: */
051: private static Logging m_instance = null;
052:
053: /**
054: * maintains the map with associated output streams.
055: */
056: private Hashtable m_queues = null;
057:
058: /**
059: * maintains the map with the maximum debug level per queue.
060: */
061: private Hashtable m_levels = null;
062:
063: /**
064: * maintains the line feed state for each queue.
065: */
066: private Hashtable m_newline = null;
067:
068: /**
069: * This is used to format the time stamp.
070: */
071: private static Currently m_formatter = null;
072:
073: /**
074: * This is the verbose option. Any queue will be protocolled up
075: * to the verbose level, iff the level is 0 or above. Verbose
076: * messages are dumped on the stream associated with "default",
077: * which defaults to stderr.
078: */
079: private int m_verbose = -1;
080:
081: /**
082: * implement the Singleton pattern
083: */
084: public static Logging instance() {
085: if (m_instance == null)
086: m_instance = new Logging();
087: return m_instance;
088: }
089:
090: /**
091: * Ctor.
092: */
093: private Logging() {
094: this .m_queues = new Hashtable();
095: this .m_levels = new Hashtable();
096: this .m_newline = new Hashtable();
097: // Logging.m_formatter = new Currently( "yyyy-MM-dd HH:mm:ss.SSSZZZZZ: " );
098: Logging.m_formatter = new Currently("yyyyMMdd'T'HHmmss.SSS: ");
099: register("default", System.err, 0);
100: }
101:
102: /**
103: * Accessor: Obtains the default timestamp format for all queues.
104: *
105: * @return the currently active timestamp prefix format.
106: */
107: public static Currently getDateFormat() {
108: return Logging.m_formatter;
109: }
110:
111: /**
112: * Accessor: Sets the default timestamp format for all queues.
113: *
114: * @param format is the new timestamp prefix format.
115: */
116: public static void setDateFormat(Currently format) {
117: if (format != null)
118: Logging.m_formatter = format;
119: }
120:
121: /**
122: * Registers a stream with a name to use for logging. The queue
123: * will be set up for maximum logging, e.g. virtually all levels
124: * for this queue are logged.
125: *
126: * @param handle is the queue identifier
127: * @param out is the name of a file to append to. Special names are
128: * <code>stdout</code> and <code>stderr</code>, which map to the
129: * system's respective streams.
130: * @see #register( String, OutputStream, int )
131: */
132: public void register(String handle, String out) {
133: if (out.equals("stdout")) {
134: this .register(handle, System.out, Integer.MAX_VALUE);
135: } else if (out.equals("stderr")) {
136: this .register(handle, System.err, Integer.MAX_VALUE);
137: } else {
138: try {
139: FileOutputStream fout = new FileOutputStream(out, true);
140: this .register(handle, new BufferedOutputStream(fout),
141: Integer.MAX_VALUE);
142: } catch (FileNotFoundException e) {
143: log("default", 0, "unable to append \"" + handle
144: + "\" to " + out + ": " + e.getMessage());
145: } catch (SecurityException e) {
146: log("default", 0, "unable to append \"" + handle
147: + "\" to " + out + ": " + e.getMessage());
148: }
149: }
150: }
151:
152: /**
153: * Registers a stream with a name to use for logging. The queue
154: * will be set up for maximum logging, e.g. virtually all levels
155: * for this queue are logged.
156: *
157: * @param handle is the queue identifier
158: * @param out is the new output stream
159: * @see #register( String, OutputStream, int )
160: */
161: public void register(String handle, OutputStream out) {
162: this .register(handle, out, Integer.MAX_VALUE);
163: }
164:
165: /**
166: * Registers a stream with a name to use for logging. The queue
167: * will be set up to use the output stream. If there was another
168: * stream previously registered, it will be closed!
169: *
170: * @param handle is the queue identifier
171: * @param out is the output stream associated with the queue
172: * @param level is the maximum debug level to put into the queue
173: */
174: public void register(String handle, OutputStream out, int level) {
175: PrintWriter previous = (PrintWriter) m_queues.put(handle,
176: new PrintWriter(out, true));
177: // don't close System.out nor System.err. So, rely on Java to close
178: // if ( previous != null ) previous.close();
179: m_levels.put(handle, new Integer(level));
180: m_newline.put(handle, new Boolean(true));
181: }
182:
183: /**
184: * Determines the maximum level up to which messages on the given
185: * queue are protocolled. The associated stream is unaffected.
186: *
187: * @param handle is the queue identifier
188: * @return the maximum inclusive log level, or -1 for error
189: * @see #setLevel( String, int )
190: */
191: public int getLevel(String handle) {
192: if (isUnset(handle))
193: return -1;
194: Integer i = (Integer) m_levels.get(handle);
195: return (i != null ? i.intValue() : -1);
196: }
197:
198: /**
199: * Set the maximum level up to which messages on the given queue are
200: * protocolled. The associated stream is unaffected.
201: *
202: * @param handle is the queue identifier
203: * @param level is the new maximum log level (non-negative integer)
204: * @see #setLevel( String, int )
205: */
206: public void setLevel(String handle, int level) {
207: if (isUnset(handle))
208: return;
209: if (level < 0)
210: return;
211: m_levels.put(handle, new Integer(level));
212: }
213:
214: /**
215: * Obtains the current verbosity level.
216: * @return -1 for no verbosity, or the level up to which messages are logged.
217: * @see #setVerbose( int )
218: */
219: public int getVerbose() {
220: return this .m_verbose;
221: }
222:
223: /**
224: * Sets the maximum verbosity.
225: * @see #resetVerbose()
226: */
227: public void setVerbose() {
228: this .m_verbose = Integer.MAX_VALUE;
229: }
230:
231: /**
232: * Deactivates any verbosity.
233: * @see #setVerbose()
234: */
235: public void resetVerbose() {
236: this .m_verbose = -1;
237: }
238:
239: /**
240: * Sets or resets the verbosity level.
241: * @param max is the maximum inclusive level to which messages on any
242: * queue should be logged. A value of -1 (or any negative value) will
243: * deactivate verbosity mode.
244: * @see #getVerbose()
245: */
246: public void setVerbose(int max) {
247: this .m_verbose = max;
248: }
249:
250: /**
251: * Prints a message on a previously registered stream.
252: *
253: * @param handle is the symbolic queue handle.
254: * @param level is a verbosity level. The higher the level, the
255: * more debug like the message. Messages of level 0 will always
256: * be printed.
257: * @param msg is the message to put onto the stream. Please note
258: * that this function will automatically add the line break.
259: */
260: public void log(String handle, int level, String msg) {
261: this .log(handle, level, msg, true);
262: }
263:
264: /**
265: * Checks if a queue is free to be set up. This is important for
266: * initialization to setup default queues, but allow user overrides.
267: *
268: * @param handle names the queue to check for a stream.
269: * @return true, if the queue is not yet connected.
270: */
271: public boolean isUnset(String handle) {
272: // sanity check
273: if (handle == null)
274: return false;
275:
276: return (this .m_queues.get(handle) == null);
277: }
278:
279: /**
280: * Prints a message on a previously registered stream.
281: *
282: * @param handle is the symbolic queue handle.
283: * @param level is a verbosity level. The higher the level, the
284: * more debug like the message. Messages of level 0 will always
285: * be printed.
286: * @param msg is the message to put onto the stream.
287: * @param newline is a boolean, which will call invoke the println
288: * method.
289: */
290: public void log(String handle, int level, String msg,
291: boolean newline) {
292: Integer maximum = (Integer) this .m_levels.get(handle);
293:
294: // do something, if verbosity if active
295: boolean verbose = this .m_verbose >= 0
296: && level <= this .m_verbose;
297:
298: // do nothing, if we don't know about this level
299: // do nothing, if the maximum level is below chosen debug level
300: if (verbose
301: || (maximum != null && (level == 0 || level <= maximum
302: .intValue()))) {
303: // determine stream to dump message upon
304: PrintWriter pw = (PrintWriter) this .m_queues
305: .get(verbose ? "default" : handle);
306:
307: // if stream is known and without fault, dump message
308: if (pw != null && !pw.checkError()) {
309: String prefix = new String();
310:
311: // determine state of last message
312: Boolean nl = (Boolean) this .m_newline.get(handle);
313:
314: // if last message had a newline attached, prefix with new timestamp
315: if (nl == null || nl.booleanValue())
316: prefix += Logging.m_formatter.now() + '[' + handle
317: + "] ";
318:
319: // print message
320: if (newline)
321: pw.println(prefix + msg);
322: else
323: pw.print(prefix + msg);
324:
325: // save new newline state for the stream.
326: this .m_newline.put(handle, new Boolean(newline));
327: }
328: }
329: }
330: }
|