patternform.java
// $Id: PatternFormatter.java,v 1.2 2009-06-08 17:47:18 mike Exp $ package org.faceless.util.log; import java.text.*; import java.util.logging.*; import java.util.logging.Formatter; import java.io.*; import java.util.*; /** * A simple, configurable formatter for the <code>java.util.logging</code> package. * It works like the * <a href="http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html"> * PatternLayout</a> class for Log4J - the codes are the closest analogous matches from the * <code>java.util.logging</code> package. See that class for full details. * <dl> * <dd>%%</dd><dt>the percent sign</dt> * <dd>%c</dd><dt>the name of the logger, which unlike Log4j may be null</dt> * <dd>%C</dd><dt>the class name of the class that created the LogRecord, which may be null</dt> * <dd>%d</dd><dt>the formatted date - eg %d, %d{dd-MMM-yyyy}</dt> * <dd>%m</dd><dt>the formatted message included in the LogRecord, which may be localized</dt> * <dd>%M</dd><dt>the method name that created the LogRecord, which unlike Log4j may be null</dt> * <dd>%n</dd><dt>the newline character</dt> * <dd>%p</dd><dt>the level of the message, eg FINEST, WARNING, SEVERE</dt> * <dd>%t</dd><dt>the Thread ID of the thread that created the message</dt> * <dd>%x</dd><dt>the Throwable object included with the LogRecord: %x{notrace} will just print the exception name and message</dt> * <dd>%i</dd><dt>the sequence number of the LogRecord (not part of Log4j</dt> * <dd>%<i>n</i>P</dd><dt>the specified parameter - %1p for the first, %2p for the second and so on (not part of Log4j)</dt> * </dl> * The format is initialized using the <code>logging.properties</code> file, and may be * set for each level. Here's an example: * <pre> * org.faceless.util.log.PatternFormatter.format = %d{yyyy-MM-dd} %p: %m %x * org.faceless.util.log.PatternFormatter.SEVERE.format = %d: ERROR: %s at %c(%m) * </pre> * <p> * Alternatively the {@link #setFormat setFormat()} methods may be called to set the message formats * </p><p> * This code was originally written 2009-06-08 by Mike Bremford and is in the public domain. * </p> */ public class PatternFormatter extends Formatter { private static final String[] LEVELS = { "FINEST", "FINER", "FINE", "CONFIG", "INFO", "WARNING", "SEVERE" }; private static final String DEFAULT = "%d %p: %m %x"; private Map levelformats, dateformats; private String format = DEFAULT; /** * Create a new PatternFormatter */ public PatternFormatter() { LogManager manager = LogManager.getLogManager(); this.format = DEFAULT; this.dateformats = Collections.synchronizedMap(new HashMap()); String s = manager.getProperty(getClass().getName()+".format"); if (s != null && s.trim().length() > 0) { setFormat(null, s); } for (int i=0;i<LEVELS.length;i++) { s = manager.getProperty(getClass().getName()+"."+LEVELS[i]+".format"); if (s != null && s.trim().length() > 0) { setFormat(LEVELS[i], s); } } } /** * Set the format for the specified level, or (if <code>level</code> is null) * for all levels * @param level one of FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE or <code>null</code> * @param format the format string */ public synchronized void setFormat(String level, String format) { if (level==null) { this.format = format; } else { if (levelformats==null) { levelformats = new HashMap(); } levelformats.put(level, format); } } public String format(LogRecord record) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); StringBuffer premod = new StringBuffer(); String thisformat = null; if (levelformats!=null) { thisformat = (String)levelformats.get(record.getLevel().getName()); } if (thisformat==null) { thisformat = this.format; } for (int i=0;i<thisformat.length();) { char c = thisformat.charAt(i++); if (c=='\\' && i<format.length()-1) { c = thisformat.charAt(i++); switch(c) { case 'r': pw.write('\r'); break; case 'n': pw.write('\n'); break; case 't': pw.write('\t'); break; case '\\': pw.write('\\'); break; } } else if (c=='%' && i<thisformat.length()) { c = thisformat.charAt(i++); while ((c=='-' || c=='.' || Character.isDigit(c)) && i<thisformat.length()) { premod.append(c); c = thisformat.charAt(i++); } String postmod = null; if (i<thisformat.length() && thisformat.charAt(i)=='{') { int start = i+1; int end = start+1; while (end < thisformat.length() && thisformat.charAt(end)!='}') { end++; } if (end != thisformat.length()) { postmod = thisformat.substring(start, end); i = end+1; } } String s; switch (c) { case 'c': s = record.getLoggerName(); if (s!=null && postmod!=null) { try { int num = Integer.parseInt(postmod); int p = s.length(); for (int j=0;j<num && p>=0;j++) { p = s.lastIndexOf(".", p-1); } if (p!=-1) { s = s.substring(p+1); } } catch (Exception e) { } } pw.print(s); break; case 'd': if (postmod==null) { postmod = "dd-MMM-yyyy HH:mm:ss"; } ThreadLocal tl = (ThreadLocal)dateformats.get(postmod); if (tl==null) { final String format = postmod; tl = new ThreadLocal() { protected synchronized Object initialValue() { return new SimpleDateFormat(format); } }; dateformats.put(postmod, tl); } DateFormat df = (DateFormat)tl.get(); pw.print(df.format(new Date(record.getMillis()))); break; case 'C': s = record.getSourceClassName(); if (s!=null) { if (postmod!=null) { try { int num = Integer.parseInt(postmod); int p = s.length(); for (int j=0;j<num && p>=0;j++) { p = s.lastIndexOf(".", p-1); } if (p!=-1) { s = s.substring(p+1); } } catch (Exception e) { } } pw.print(s); } break; case 'M': if (record.getSourceMethodName()!=null) { pw.print(record.getSourceMethodName()); } break; case 'm': pw.print(formatMessage(record)); break; case 'n': pw.print("\n"); break; case 'p': pw.print(record.getLevel().getName()); break; case 't': pw.print(record.getThreadID()); break; case 'x': if (record.getThrown()!=null) { if ("notrace".equals(postmod)) { pw.print(record.getThrown()); } else { record.getThrown().printStackTrace(pw); } } break; case '%': pw.print("%"); break; case 'i': pw.print(record.getSequenceNumber()); break; case 'P': Object[] params = record.getParameters(); try { if (postmod!=null) { int mod = Integer.parseInt(postmod); if (params!=null && params.length > mod) { pw.print(params[mod]); } } else if (params==null) { pw.print(params); } else { pw.print(Arrays.asList(params)); } } catch (Exception e) { } break; } premod.setLength(0); } else { pw.write(c); } } return sw.toString().trim() + "\n"; } }