001: package uk.org.ponder.util;
002:
003: import java.io.FileOutputStream;
004: import java.io.PrintStream;
005: import java.io.PrintWriter;
006: import java.io.StringWriter;
007:
008: import uk.org.ponder.arrayutil.ArrayUtil;
009: import uk.org.ponder.intutil.intVector;
010: import uk.org.ponder.stringutil.CharWrap;
011:
012: /** A useful logging class that <ul>
013: * <li> Compactly annotates the output from different threads
014: * <li> Supports multiple levels of logging message priority, held in a stack
015: * <li> Renders unprintable Unicode characters in a detectable form
016: * <li> Can redirect output to console or to file </ul>
017: * Coming soon:
018: * <li>
019: * <ul> Some form of timestamping. </ul>
020: */
021:
022: public class Logger {
023: // The ponder Logger is now deprecated.
024: // All logging should be done using this standard Log4j logger.
025: public static org.apache.log4j.Logger log = org.apache.log4j.Logger
026: .getLogger("PonderUtilCore");
027: static {
028: //FineFormatter.configureLogger(log);
029: }
030: public static final boolean debugmode = true;
031: public static PrintStream logger = System.out;
032:
033: // the idea is that all codes less severe than ASSERTION will be stripped out before release,
034: // but during testing, at least codes more severe than INFORMATIONAL will be interesting.
035: public static final int DEBUG_ENTIRELY_CRITICAL = 0;
036: public static final int DEBUG_SEVERE = 5;
037: public static final int DEBUG_WARNING = 10;
038: public static final int DEBUG_QUITE_INTERESTING = 12;
039: public static final int DEBUG_ASSERTION = 13;
040: public static final int DEBUG_INFORMATIONAL = 15;
041: public static final int DEBUG_EXTRA_INFO = 17;
042: public static final int DEBUG_SUBATOMIC = 20;
043: public static final int DEBUG_PAINTING = 30;
044:
045: public static int DEBUG_LEVEL = DEBUG_INFORMATIONAL;
046:
047: private static String[] threadnames = new String[0];
048: private static intVector debuglevelstack = new intVector(10);
049:
050: /** Sets the destination for debugging messages to a file with the specified name.
051: * If this file cannot be opened, output will revert to the console.
052: * If <code>null</code> is supplied for the filename, the filename defaults to
053: * <code>javalog.txt</code>
054: * @param filename The filename to receive logging output.
055: */
056:
057: public static void setDestinationFile(String filename) {
058: if (filename == null)
059: filename = "javalog.txt";
060: try {
061: logger = new PrintStream(new FileOutputStream(filename));
062: } catch (Exception e) {
063: e.printStackTrace();
064: logger = System.out;
065: }
066: }
067:
068: /** Sets the destination for debugging messages to the console.
069: */
070:
071: public static void setConsoleOutput() {
072: if (logger != System.out) {
073: logger.close();
074: }
075: logger = System.out;
076: }
077:
078: /** Sets the current debugging level to the specified level. Messages with priorities
079: * lower than this level (i.e. with higher integer values) will be suppressed.
080: * @param debuglevel The new debugging level to operate at.
081: */
082:
083: public static void setDebugLevel(int debuglevel) {
084: DEBUG_LEVEL = debuglevel;
085: }
086:
087: /** Pushes the specified debugging level onto the stack, and sets the current debugging
088: * level to this level.
089: * @param debuglevel The new debugging level.
090: */
091: public static void pushDebugLevel(int debuglevel) {
092: debuglevelstack.addElement(DEBUG_LEVEL);
093: setDebugLevel(debuglevel);
094: }
095:
096: /** Pops the last debugging level that was pushed onto the stack, and sets the
097: * current debugging level to the new stack top level.
098: */
099:
100: public static void popDebugLevel() {
101: // can this actually have been wrong all this time? Looks like it leaves the
102: // debugging level at the old stack top!
103: setDebugLevel(debuglevelstack.popElement());
104: }
105:
106: /** Allows clients to detect whether a given debugging priority level passes the current
107: * threshold.
108: * @param debuglevel The debug level to be checked.
109: * @return <code>true</code> if a message of the specified priority level would be
110: * printed.
111: */
112:
113: public static boolean passDebugLevel(int debuglevel) {
114: return debuglevel <= DEBUG_LEVEL && debugmode;
115: }
116:
117: /** Prints a message with a particular priority level, followed by a newline.
118: * @param toprint The message to be printed.
119: * @param debuglevel The priority level of the message. If the current priority level
120: * is higher than the supplied level, printing of the message will be suppressed.
121: * If the current debug level is <code>DEBUG_ASSERTION</code>, a message of with
122: * priority <code>DEBUG_ASSERTION</code> will trigger an AssertionException.
123: */
124:
125: public static void println(String toprint, int debuglevel) {
126: if (debuglevel <= DEBUG_LEVEL && debugmode)
127: println(toprint);
128: // release mode is detected by debuglevel == DEBUG_ASSERTION
129: // this logic appears to be wrong!! I would say
130: // if (DEBUG_LEVEL == DEBUG_ASSERTION && debuglevel == DEBUG_ASSERTION) ??!
131: if (debuglevel == DEBUG_ASSERTION && debuglevel < DEBUG_LEVEL) {
132: throw new AssertionException(toprint);
133: }
134: }
135:
136: /** Prints a message with a particular priority level, without a newline.
137: * @param toprint The message to be printed.
138: * @param debuglevel The priority level of the message. If the current priority
139: * level is higher than the supplied level, printing of the message will be suppressed.
140: */
141:
142: public static void print(String toprint, int debuglevel) {
143: if (debuglevel <= DEBUG_LEVEL && debugmode)
144: print(toprint);
145: }
146:
147: /** Prints the supplied object to the debug stream followed by a newline.
148: * @param toprint The object to be printed.
149: */
150:
151: public static void println(Object toprint) {
152: printInternal(toprint.toString() + '\n');
153: }
154:
155: /** Prints the supplied object to the debug stream.
156: * @param toprint The object to be printed.
157: */
158:
159: public static void print(Object toprint) {
160: printInternal(toprint.toString());
161: }
162:
163: private static void printInternal(String toprint) {
164: CharWrap buildup = new CharWrap(toprint.length() + 16);
165: String prefix = null;
166: synchronized (threadnames) {
167: String threadname = Thread.currentThread().getName();
168: int threadindex = ArrayUtil
169: .indexOf(threadnames, threadname);
170: boolean newthread = false;
171: if (threadindex == -1) {
172: threadnames = (String[]) ArrayUtil.append(threadnames,
173: threadname);
174: threadindex = threadnames.length - 1;
175: newthread = true;
176: }
177:
178: buildup.append('[').append(Integer.toString(threadindex))
179: .append("] ");
180: prefix = buildup.toString();
181: if (newthread) {
182: logger
183: .println(prefix + " new thread "
184: + Thread.currentThread()
185: + " detected by Logger with name "
186: + threadname);
187: }
188: }
189: for (int i = 0; i < toprint.length(); ++i) {
190: char charat = toprint.charAt(i);
191: if (charat >= 32 && charat < 127 || charat == '\r'
192: || charat == '\n') {
193: buildup.append(charat);
194: } else if (charat == '\t') {
195: buildup.append(" ");
196: } else
197: buildup.appendHexChar(charat);
198: if (charat == '\n' && i != toprint.length() - 1) {
199: buildup.append(prefix);
200: }
201: }
202: logger.print(buildup.toString());
203: }
204:
205: // Den Bortkomne, by Hans-Ulrich Treichel?
206: /** Prints the supplied message to the debug stream, followed by a newline.
207: * @param The message to be printed.
208: */
209:
210: public static void println(String toprint) {
211: printInternal(toprint + '\n');
212: }
213:
214: /** Prints the supplied message to the debug stream.
215: * @param The message to be printed.
216: */
217:
218: public static void print(String toprint) {
219: printInternal(toprint);
220: }
221:
222: /** Prints a stack trace for the supplied exception to the debug stream, with the
223: * default priority level of <code>DEBUG_SEVERE</code>.
224: * @param t The throwable for which a stack trace is required.
225: */
226:
227: public static void printStackTrace(Throwable t) {
228: printStackTrace(t, DEBUG_SEVERE);
229: }
230:
231: /** Prints a stack trace for the supplied exception to the debug stream, with the
232: * specified priority level.
233: * @param t The throwable for which a stack trace is required.
234: * @param debuglevel The priority level for the stack trace.
235: */
236:
237: public static void printStackTrace(Throwable t, int debuglevel) {
238: if (debuglevel <= DEBUG_LEVEL && debugmode) {
239: StringWriter sw = new StringWriter();
240: PrintWriter pw = new PrintWriter(sw);
241: t.printStackTrace(pw);
242: printInternal(sw.toString());
243: }
244: }
245: }
|