001: package Shared.Logging.Internal;
002:
003: import java.io.*;
004: import java.io.PrintStream;
005: import java.text.*;
006: import java.util.Date;
007: import java.text.SimpleDateFormat;
008:
009: /**
010: * This is the basis class, which is extended by
011: * the other loggers and is responsible for the
012: * created log text, which then is treated differently
013: * by the child loggers:
014: * ConsoleLogger: writes it to the System.out stream
015: * FileLogger: writes it into log files
016: * SocketLogger: sends it over the internet
017: *
018: * Child loggers have to implement the abstract method
019: * publish( String levelName, String message ) and can get
020: * the log text by a call to the protected method
021: * getLogText( String levelName, String message ).
022: */
023: public abstract class GenericLogger {
024: private Date date = new Date();
025: private SimpleDateFormat dateFormat = new SimpleDateFormat(
026: "d MMM yyyy HH:mm:ss");
027: private StringBuffer text = new StringBuffer();
028:
029: private String sourceClassName = "";
030: private String sourceMethodName = "";
031: private String lineNumber = "";
032:
033: /**
034: * The log identifier is written at the beginning of any
035: * log text, before the level text.
036: * It is set in the constructor, but can be changed anytime later.
037: */
038: private String logIdentifier = "";
039:
040: public GenericLogger(String _logIdentifier) {
041: this .logIdentifier = _logIdentifier;
042: } // Constructor
043:
044: /**
045: * To be implemented by child loggers.
046: */
047: public abstract void publish(String levelName, String message);
048:
049: /**
050: * To be implemented by child loggers.
051: */
052: public abstract void publish(String levelName, Throwable throwable);
053:
054: /**
055: * To be implemented by child loggers.
056: * This one just logs the message directly without any formatting.
057: */
058: public abstract void publishDirectly(String message);
059:
060: /**
061: * This is called by the Logs shutdown hook.
062: * The logger must close any open streams and if required
063: * do further cleanup. The JVM will shutdown short time
064: * after this call.
065: */
066: public abstract void terminate();
067:
068: /**
069: * Called from Log for changing the log identifier.
070: */
071: public synchronized void setLogIdentifier(String newLogIdentifier) {
072: this .logIdentifier = newLogIdentifier;
073: }
074:
075: /**
076: * Builds the log text.
077: * It is supposed, that child loggers call this to get
078: * the log text, and then proceed differently with that.
079: *
080: * It is synchronized, because a member attribute is used
081: * as working variable, so that the same reference is used always,
082: * and the code does not create and set new references for each call.
083: */
084: protected synchronized String getLogText(String levelName,
085: String message) {
086: // Initialize work attributes:
087: this .text.setLength(0);
088: this .sourceClassName = "?";
089: this .sourceMethodName = "?";
090: this .lineNumber = "?";
091: // Set sourceClassName, sourceMethodName and lineNumber by
092: // exploring the calltrace:
093: this .setPropertiesFromStackTrace();
094: // output:
095:
096: if (this .logIdentifier.length() > 0) {
097: this .text.append(this .logIdentifier);
098: this .text.append(" ");
099: }
100:
101: this .text.append(levelName);
102: this .text.append(" ");
103:
104: this .date.setTime(System.currentTimeMillis());
105: this .text.append(this .dateFormat.format(this .date));
106:
107: this .text.append(" in ");
108: this .text.append(this .sourceClassName);
109: this .text.append(".");
110: this .text.append(this .sourceMethodName);
111: this .text.append(":");
112: this .text.append(this .lineNumber);
113:
114: if (message != null) {
115: this .text.append(" > ");
116: this .text.append(message);
117: }
118:
119: return this .text.toString();
120: }
121:
122: /**
123: * The same for throwables (Exceptions, Errors)
124: */
125: protected synchronized String getThrowableText(String levelName,
126: Throwable throwable) {
127: StringBuffer sb = new StringBuffer();
128: sb.append(this .getLogText(levelName, "Throwable"));
129: // type of throwable:
130: if (throwable != null) {
131: sb.append("\n");
132: sb.append(throwable.getClass().getName());
133: }
134: if (throwable != null) {
135: StackTraceElement[] elements = throwable.getStackTrace();
136: if (elements != null) {
137: for (int i = 0; i < elements.length; i++) {
138: sb.append("\n\tat ");
139: sb.append(elements[i].toString());
140: }
141: }
142: }
143: return sb.toString();
144: }
145:
146: /**
147: * Finds out the class name, method name and the lineNumber.
148: * Taken from the LogRecord class of the java.util.logging 1.5 api,
149: * where the method is called inferCaller().
150: */
151: private void setPropertiesFromStackTrace() {
152: // Get the stack trace.
153: StackTraceElement stack[] = (new Throwable()).getStackTrace();
154: // First, search back to a method in the Logger class.
155: int ix = 0;
156: while (ix < stack.length) {
157: StackTraceElement frame = stack[ix];
158: String cname = frame.getClassName();
159: if (cname.equals("Shared.Logging.Log")) {
160: break;
161: }
162: ix++;
163: }
164: // Now search for the first frame before the "Logger" class.
165: while (ix < stack.length) {
166: StackTraceElement frame = stack[ix];
167: String cname = frame.getClassName();
168: if (!cname.equals("Shared.Logging.Log")) {
169: // We've found the relevant frame.
170: this .sourceClassName = cname;
171: this .sourceMethodName = frame.getMethodName();
172: int lineNumberValue = frame.getLineNumber();
173: if (lineNumberValue >= 0) {
174: this .lineNumber = String.valueOf(lineNumberValue);
175: } else if (lineNumberValue == -1) {
176: this .lineNumber = "unknown";
177: } else if (lineNumberValue == -2) {
178: this .lineNumber = "native";
179: }
180: return;
181: }
182: ix++;
183: }
184: // We haven't found a suitable frame, so just punt. This is
185: // OK as we are only committed to making a "best effort" here.
186: }
187:
188: /**
189: * Changes the date format pattern.
190: * @param pattern the new pattern.
191: * See the SimpleDateFormat constructor comments for
192: * possible contents of pattern.
193: */
194: public synchronized void setDateFormatPattern(String pattern) {
195: this .dateFormat.applyPattern(pattern);
196: }
197:
198: } // GenericLogger
|