001: /*
002: Copyright (C) 2007 Mobixess Inc. http://www.java-objects-database.com
003:
004: This file is part of the JODB (Java Objects Database) open source project.
005:
006: JODB is free software; you can redistribute it and/or modify it under
007: the terms of version 2 of the GNU General Public License as published
008: by the Free Software Foundation.
009:
010: JODB is distributed in the hope that it will be useful, but WITHOUT ANY
011: WARRANTY; without even the implied warranty of MERCHANTABILITY or
012: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
013: for more details.
014:
015: You should have received a copy of the GNU General Public License along
016: with this program; if not, write to the Free Software Foundation, Inc.,
017: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
018: */
019: package com.mobixess.jodb.util.logging;
020:
021: import java.io.PrintWriter;
022: import java.io.StringWriter;
023: import java.text.MessageFormat;
024: import java.util.Date;
025: import java.util.logging.ConsoleHandler;
026: import java.util.logging.Level;
027: import java.util.logging.LogRecord;
028: import java.util.logging.Logger;
029:
030: public class SimpleConsoleHandler extends ConsoleHandler {
031: private final static String format = "{0,date} {0,time}";
032: Date dat = new Date();
033: private MessageFormat formatter;
034: private Object args[] = new Object[1];
035: @SuppressWarnings("unchecked")
036: private String lineSeparator = (String) java.security.AccessController
037: .doPrivileged(new sun.security.action.GetPropertyAction(
038: "line.separator"));
039: private String[] LOGGING_CLASSES_FULL = new String[] { Logger.class
040: .getName() };
041: private String[] LOGGING_CLASSES_PREFIXES = new String[] {
042: "com.spsoft.msgboard.management.userBrowser.core.container.BrowserAppLogger",
043: "java.security.AccessController" };
044:
045: @Override
046: public void publish(LogRecord record) {
047: format(record);
048: }
049:
050: /**
051: * Format the given LogRecord.
052: *
053: * @param record
054: * the log record to be formatted.
055: * @return a formatted log record
056: */
057: public synchronized String format(LogRecord record) {
058: StringBuffer sb = new StringBuffer();
059: // Minimize memory allocations here.
060: dat.setTime(record.getMillis());
061: args[0] = dat;
062: StringBuffer text = new StringBuffer();
063: if (formatter == null) {
064: formatter = new MessageFormat(format);
065: }
066: formatter.format(args, text, null);
067: sb.append(text);
068: sb.append(" ");
069:
070: // if (record.getSourceClassName() != null) {
071: // sb.append(record.getSourceClassName());
072: // } else {
073: // sb.append(record.getLoggerName());
074: // }
075: // if (record.getSourceMethodName() != null) {
076: // sb.append(".");
077: // sb.append(record.getSourceMethodName());
078: // }
079:
080: sb.append(getLineAndSrc());
081:
082: sb.append(" Thread: " + record.getThreadID());
083:
084: sb.append(lineSeparator);
085: String message = formatMessage(record);
086: sb.append("\t" + record.getLevel().getLocalizedName());
087: sb.append(": ");
088: sb.append(message);
089: sb.append(lineSeparator);
090: if (record.getThrown() != null) {
091: try {
092: StringWriter sw = new StringWriter();
093: PrintWriter pw = new PrintWriter(sw);
094: record.getThrown().printStackTrace(pw);
095: pw.close();
096: sb.append(sw.toString());
097: } catch (Exception ex) {
098: }
099: sb.append(lineSeparator);
100: }
101: String result = sb.toString();
102:
103: if (record.getLevel().intValue() <= Level.INFO.intValue()) {
104: System.out.print(result);
105: } else {
106: System.err.print(result);
107: }
108: return result;
109: }
110:
111: // Private method to infer the caller's class and method names
112: protected String getLineAndSrc() {
113: // Get the stack trace.
114: StackTraceElement stack[] = (new Throwable()).getStackTrace();
115: // First, search back to a method in the Logger class.
116: int ix = 0;
117: while (ix < stack.length) {
118: StackTraceElement frame = stack[ix];
119: String cname = frame.getClassName();
120: if (isLoggingClass(cname)) {
121: break;
122: }
123: ix++;
124: }
125: // Now search for the first frame before the "Logger" class.
126: while (ix < stack.length) {
127: StackTraceElement frame = stack[ix];
128: String cname = frame.getClassName();
129: if (!isLoggingClass(cname)) {
130: // We've found the relevant frame.
131: //setSourceClassName(cname);
132: //setSourceMethodName(frame.getMethodName());
133: return frame.getClassName() + '.'
134: + frame.getMethodName() + '('
135: + frame.getFileName() + ':'
136: + frame.getLineNumber() + ')';
137:
138: }
139: ix++;
140: }
141: // We haven't found a suitable frame, so just punt. This is
142: // OK as we are only committed to making a "best effort" here.
143: return "";
144: }
145:
146: private boolean isLoggingClass(String name) {
147: for (int i = 0; i < LOGGING_CLASSES_FULL.length; i++) {
148: if (LOGGING_CLASSES_FULL[i].equals(name)) {
149: return true;
150: }
151: }
152: for (int i = 0; i < LOGGING_CLASSES_PREFIXES.length; i++) {
153: if (name.startsWith(LOGGING_CLASSES_PREFIXES[i])) {
154: return true;
155: }
156: }
157: return false;
158: }
159:
160: /**
161: * Localize and format the message string from a log record. This
162: * method is provided as a convenience for Formatter subclasses to
163: * use when they are performing formatting.
164: * <p>
165: * The message string is first localized to a format string using
166: * the record's ResourceBundle. (If there is no ResourceBundle,
167: * or if the message key is not found, then the key is used as the
168: * format string.) The format String uses java.text style
169: * formatting.
170: * <ul>
171: * <li>If there are no parameters, no formatter is used.
172: * <li>Otherwise, if the string contains "{0" then
173: * java.text.MessageFormat is used to format the string.
174: * <li>Otherwise no formatting is performed.
175: * </ul>
176: * <p>
177: *
178: * @param record the log record containing the raw message
179: * @return a localized and formatted message
180: */
181: public synchronized String formatMessage(LogRecord record) {
182: String format = record.getMessage();
183: java.util.ResourceBundle catalog = record.getResourceBundle();
184: if (catalog != null) {
185: // // We cache catalog lookups. This is mostly to avoid the
186: // // cost of exceptions for keys that are not in the catalog.
187: // if (catalogCache == null) {
188: // catalogCache = new HashMap();
189: // }
190: // format = (String)catalogCache.get(record.essage);
191: // if (format == null) {
192: try {
193: format = catalog.getString(record.getMessage());
194: } catch (java.util.MissingResourceException ex) {
195: // Drop through. Use record message as format
196: format = record.getMessage();
197: }
198: // catalogCache.put(record.message, format);
199: // }
200: }
201: // Do the formatting.
202: try {
203: Object parameters[] = record.getParameters();
204: if (parameters == null || parameters.length == 0) {
205: // No parameters. Just return format string.
206: return format;
207: }
208: // Is is a java.text style format?
209: // Ideally we could match with
210: // Pattern.compile("\\{\\d").matcher(format).find())
211: // However the cost is 14% higher, so we cheaply check for
212: // 1 of the first 4 parameters
213: if (format.indexOf("{0") >= 0 || format.indexOf("{1") >= 0
214: || format.indexOf("{2") >= 0
215: || format.indexOf("{3") >= 0) {
216: return java.text.MessageFormat.format(format,
217: parameters);
218: }
219: return format;
220:
221: } catch (Exception ex) {
222: // Formatting failed: use localized format string.
223: return format;
224: }
225: }
226: }
|