package edu.isi.ikcap.workflows.util.logging; /* * The following is the original copy right from gov.lbl.netlogger.LogMessage * Copyright (c) 2004, The Regents of the University of California, through * Lawrence Berkeley National Laboratory (subject to receipt of any required * approvals from the U.S. Dept. of Energy). All rights reserved. */ import java.text.SimpleDateFormat; import java.util.Date; import java.util.Enumeration; import java.util.GregorianCalendar; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TimeZone; import java.util.concurrent.locks.*; /** * This is a modification of gov.lbl.netlogger.LogMessage * * This class lets you easily construct a set of typed (name, value) pairs * that formats itself as a CEDPS Best Practices log message. *<p> * Name and value pairs are added into the record with add() methods * which, by virtue of returning the newly modified LogMessage instance, * can be chained together. * <p> * The user can set the timestamp to something other than the * time of the call by calling setTimeStamp{Millis,Nanos}() as part of the chain. *</p><p> * To format the message, call toString(). The output format is * <a href="http://www.cedps.net/wiki/index.php/LoggingBestPractices">CEDPS "Best Practices" format</a>. * </p><p> * Since the addition of the nanosecond timestamp (which is rounded * down to microseconds, and no I don't want to discuss it), this class * requires Java 1.5 * </p> * @author Dan Gunter dkgunter@lbl.gov * @author Wolfgang Hoschek whoschek@lbl.gov * @author Paul Groth pgroth@isi.edu */ public class EventLogMessage { // Variables private final StringBuffer buf = new StringBuffer(256); // set initial capacity for efficiency/memory trade-off private static long micro0, nano0, micro1; private long micro2; // Static Variables private static String timeString = null; private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); private static final GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC")); private static Lock timeStringLock = new ReentrantLock( ); // Constants public static final String APPENDER = "KEYVALUE"; public static final String EVENT_KW = "event"; public static final String DATE_KW = "ts"; public static final String FAKE_DATE = "1999-01-01T11:59:59.999999Z"; private final int dateStart = DATE_KW.length() + 1; private final int dateEnd = dateStart + FAKE_DATE.length() - 8; private final int usecStart = dateEnd + 1; @SuppressWarnings("unused") private final int usecEnd = dateEnd + 7; public static final String LEVEL_KW = "level="; private static final char[] DIGIT = {'0','1','2','3','4','5','6','7','8','9'}; private static Escape escape = new Escape( "\"" , '\\' , false ); /** * Create a new LogMessage at the current time with a given event name. * * The timestamp is set at creation time, but can be changed later * with <code>setTimeStampMillis</code> or <code>setTimeStampNanos</code>. * * @param eventName Name of this logging event. * @see #setTimeStampMillis * @see #setTimeStampNanos */ protected EventLogMessage(String eventName) { add(DATE_KW, FAKE_DATE); add(EVENT_KW, eventName); long nano1 = System.nanoTime(); /* calculate timestamp in microseconds */ micro2 = ( nano1 - nano0 ) / 1000 + micro0; } /** * Add a string. * @return Self-reference, so calls can be chained */ public EventLogMessage add(String key, String value) { buf.append(key); buf.append("="); buf.append(value); buf.append(" "); return this; } /** * Add a string. Strings automatically have quotes around them * and are escaped * @return Self-reference, so calls can be chained */ public EventLogMessage addWQ (String key, String value) { buf.append(key); buf.append("="); buf.append("\""); buf.append(escape.escape(value)); buf.append("\""); buf.append(" "); return this; } /** * Add an int. * @return Self-reference, so calls can be chained */ public EventLogMessage add(String key, int value) { buf.append(key); buf.append("="); buf.append(value); buf.append(" "); return this; } /** * Add a long. * @return Self-reference, so calls can be chained */ public EventLogMessage add(String key, long value) { buf.append(key); buf.append("="); buf.append(value); buf.append(" "); return this; } /** * Add a float. * @return Self-reference, so calls can be chained */ public EventLogMessage add(String key, float value) { buf.append(key); buf.append("="); buf.append(value); buf.append(" "); return this; } /** * Add a double. * @return Self-reference, so calls can be chained */ public EventLogMessage add(String key, double value) { buf.append(key); buf.append("="); buf.append(value); buf.append(" "); return this; } /** * Add a key,value pair * The result looks like: key = (pairKey, pairValue) * @param key * @param pairKey * @param pairValue */ public EventLogMessage addPair (String key, String pairKey, String pairValue) { buf.append(key); buf.append("="); buf.append("("); buf.append(pairKey); buf.append(","); buf.append(pairValue); buf.append(")"); buf.append(" "); return this; } public EventLogMessage addTime (String key, long timeInMillis) { buf.append(key); buf.append("="); buf.append(format.format(new Date(timeInMillis))); buf.append(" "); return this; } /** * Add a Map to the buffer. Represents maps as a series of (key, value) in quotes. * The method assumes that both keys and values toString method returns a "nice" string representation. * The method escapes all quotes * @param key * @param map * @return */ public EventLogMessage addMap (String key, Map map) { buf.append(key); buf.append("="); buf.append("\""); StringBuffer ps = new StringBuffer(); Set<Map.Entry> pairs = map.entrySet(); for (Map.Entry x : pairs) { ps.append("("); ps.append(x.getKey().toString()); if(x.getValue() != null) { ps.append(","); ps.append(x.getValue().toString()); } ps.append(")"); } buf.append(escape.escape(ps.toString())); buf.append("\""); buf.append(" "); return this; } public EventLogMessage addList (String key, List list) { buf.append(key); buf.append("="); buf.append("\""); int size = list.size(); for (int i = 0; i < size; i++) { buf.append(escape.escape(list.get(i).toString())); if (i != size-1) { buf.append(","); } } buf.append("\""); buf.append(" "); return this; } /** * Add an natural language message to the log message. * The string is put in quotes and is escaped * @param msg * @return */ public EventLogMessage addMsg (String msg) { buf.append(LoggingKeys.MSG); buf.append("="); buf.append("\""); buf.append(escape.escape(msg)); buf.append("\""); buf.append(" "); return this; } /** * Set the timestamp from milliseconds * returned by System.currentTimeMillis(). * * @return 'this' so we can chain */ public EventLogMessage setTimeStampMillis(long millis) { micro2 = millis * 1000; return this; } /** * Set the timestamp from nanoseconds * returned by System.nanoTime(). * * @return 'this' so we can chain */ public EventLogMessage setTimeStampNanos(long nano1) { micro2 = ( nano1 - nano0 ) / 1000 + micro0; return this; } /** * Format a message in CEDPS Best Practices format. * * @return Formatted message string * @see <a href="http://www.cedps.net/wiki/index.php/LoggingBestPractices">CEDPS "Best Practices" format</a> */ public String toString() { if (micro2 > 0) { addTimeStamp(); micro2 = 0; } return buf.toString(); } /** * Add a timestamp to the message. */ private void addTimeStamp() { // re-use or re-set whole seconds if (micro2 / 1000000L != micro1 / 1000000L) { timeStringLock.lock(); timeString = format.format(new Date(micro2 / 1000L)); timeStringLock.unlock(); micro1 = micro2; } buf.replace(dateStart, dateEnd, timeString); // add fractional time (microseconds) long div, frac; int i; frac = micro2 % 1000000L; for (i=0, div = 100000L; i < 6; div = div / 10, i++) { long n = frac / div; buf.setCharAt(usecStart + i, DIGIT[(int)n]); frac -= n * div; } } //=============================================================== // Log4J compatibility (contributed by Wolfgang Hoschek) //=============================================================== /** * Static class initializer. * * Make it so that log4j.jar is a compile time requirement, * but not a runtime requirement */ static { try { // check if log4j is present Class.forName("org.apache.log4j.spi.Filter"); // executed only if log4j is present Log4jFilter.init(); } catch (ClassNotFoundException e) { // This warning might mess up daemon processes, // so it's commented out by default System.err.println( "Warning: Cannot find log4j " + "(org.apache.log4j.spi.Filter), " + "continuing.."); } // set calendar of formatter: otherwise no UTC!! format.setCalendar(calendar); // init base nanosecond and millisecond time long ms = System.currentTimeMillis(); nano0 = System.nanoTime(); micro0 = micro1 = ms * 1000L; timeString = format.format(new Date(ms)); } /** * In log4j, ignore all messages not specifically directed * at this appender. */ private static final class Log4jFilter extends org.apache.log4j.spi.Filter { public static void init() { Enumeration loggers = org.apache.log4j.Logger .getRootLogger() .getLoggerRepository() .getCurrentLoggers(); while ( loggers.hasMoreElements() ) { org.apache.log4j.Logger logger = (org.apache.log4j.Logger) loggers.nextElement(); if (logger.getAppender(APPENDER) != null) { logger.getAppender(APPENDER).addFilter(new Log4jFilter()); } } } public int decide(org.apache.log4j.spi.LoggingEvent event) { if (event.getMessage() instanceof EventLogMessage) { return NEUTRAL; // let message pass through } else { return DENY; // ignore all non-netlogger messages } } } }