/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- //------100-columns-wide------>|*/ /* * Copyright (c) 2003 Extreme! Lab, Indiana University. All rights reserved. * * This software is open source. See the bottom of this file for the licence. * * $Id: MLogger.java,v 1.5 2003/08/04 04:49:50 aslom Exp $ */ package mblog; import java.io.PrintStream; import java.io.PrintWriter; import java.io.StringWriter; import java.security.AccessControlException; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.StringTokenizer; import java.util.TreeMap; /** * This is very small implementation of logging for JDK 1.2 (or better) * that is self-contained (it is just one class!) and is both easy to use * and simple to configurable from command line by using system properties (-Dlog=...) * or from inside of your application (see setCmdNames() method). * To use in your application simply copy this file to your source tree, * chnage package name and in your code use: *
* private static final MLogger logger = Mlogger.getLogger();
* ...
* logger.fine("hellp from logger");
*
* The API is modelled after Log4J and JDK 1.4 logger so should be easy to switch.
*
* @version $Revision: 1.5 $ $Date: 2003/08/04 04:49:50 $ (GMT)
* @author Aleksander Slominski [http://www.extreme.indiana.edu/~aslom]
*/
public class MLogger {
public static final MLogger global = new MLogger("global", null);
public static final String PROPERTY_PREFIX = ""; //"logger.prefix"
public static final String PROPERTY_LOG = PROPERTY_PREFIX+"log";
public static final String PROPERTY_SHOWTIME = PROPERTY_PREFIX+"showtime";
public static final String PROPERTY_DEBUG = PROPERTY_PREFIX+"debug";
public static final String PROPERTY_WRAPCOL = PROPERTY_PREFIX+"log.wrapcol";
private static String PROPERTY_MULTILINE = PROPERTY_PREFIX+"log.multiline";
private static final boolean QUIET = false; // no configuration messages
private static final boolean DEBUG = false;
private static final boolean WARN = false;
public static final int DEFAULT_NESTING_LEVEL = 4;
// global state
private static PrintStream sink = System.err;
private static boolean guessFailed;
//private static List anchoredLoggers = new LinkedList();
private static boolean showTime = true;
private static int wrapCol;
private static boolean multiline;
//private static List cmdLoggers;
// per instance states
private Level myLevel;
private String myName;
// do magic reading -Dlog property, ex:
// -Dlog=wombat:INFO,tests:ALL,xpp:OFF
// -Dlog=:OFF
// -Ddebug=true
static {
if(Log.ON) {
//cmdLoggers = new ArrayList();
try {
//String names = System.getProperty("log");
String names = null;
try {
names = (String) AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return System.getProperty(PROPERTY_LOG);
}
});
} catch(AccessControlException ace) {
if(DEBUG) System.err.println("no access to log sytem property for log");
if(DEBUG) ace.printStackTrace();
}
if(DEBUG) System.err.println("SLogger log="+names);
// last resort
String debug = System.getProperty(PROPERTY_DEBUG);
if(names == null && debug != null) {
String s = debug.toLowerCase();
if(DEBUG) System.err.println("SLogger debug="+s);
if(!"false".equals(s) && !"off".equals(s))
names = ":ALL";
}
setCmdNames(names); //set list of loggers and levels
String requestedShowTime = System.getProperty(PROPERTY_SHOWTIME);
if(requestedShowTime != null) {
if(requestedShowTime.length() > 0) {
showTime = Boolean.getBoolean(requestedShowTime);
} else {
showTime = true; //when property is present and empty it is ON
}
}
String multilineProperty = System.getProperty(PROPERTY_MULTILINE);
if(multilineProperty != null) {
if(multilineProperty.length() > 0) {
multiline = Boolean.getBoolean(multilineProperty);
} else {
multiline = true; //when property is present and empty it is ON
}
}
String wrapColProperty = System.getProperty(PROPERTY_WRAPCOL);
if(wrapColProperty != null) {
wrapCol = Integer.parseInt(wrapColProperty);
}
} catch(IllegalArgumentException ex) {
// this is user error and that is why printing of it is not suppressed
System.err.println("can't set logging "+ex);
ex.printStackTrace();
} catch(java.security.AccessControlException ex) {
if(WARN ||DEBUG) {
System.err.println("can't set logging "+ex);
ex.printStackTrace();
}
}
}
}
protected MLogger(String name, String resourceBundleName) {
this.myName = name;
}
public static MLogger getAnonymousMLogger() {
return new MLogger(null, null);
}
public static MLogger getLogger() {
// determine log klass dynamically
String name = "";
Location loc = new Location();
if(guessLocation(loc, 2)) {
name = loc.klass;
}
if(DEBUG) System.err.println("SLogger.getLogger loc.klass="+loc.klass);
return getLogger(name);
}
public static MLogger getLogger(java.lang.String name) {
MLogger logger = LogManager.getLogManager().getLogger(name);
//anchorLogger(logger);
return logger;
}
public String getName() {
return myName;
}
public boolean isLoggable(Level level) {
return level.intValue() >= myLevel.intValue();
}
public final boolean isSevereEnabled() { return isLoggable(Level.SEVERE); }
public final boolean isWariningEnabled() { return isLoggable(Level.WARNING); }
public final boolean isInfoEnabled() { return isLoggable(Level.INFO); }
public final boolean isConfigEnabled() { return isLoggable(Level.CONFIG); }
public final boolean isFineEnabled() { return isLoggable(Level.FINE); }
public final boolean isFinerEnabled() { return isLoggable(Level.FINER); }
public final boolean isFinestEnabled() { return isLoggable(Level.FINEST); }
public void setLevel(Level newLevel) {
myLevel = newLevel;
if(DEBUG) {
boolean enabled = myLevel != Level.OFF;
System.err.println("SLogger.setLevel name='"+myName+"' level="+myLevel+" enabled="+enabled);
//if(DEBUG) System.err.println("SLogger name="+name+" is not enabled");
}
}
public Level getLevel() {
return myLevel;
}
public static String parametersToList(Object[] params) {
if(params == null) return " array with parameters is null";
StringBuffer sb = new StringBuffer();
sb.append('{');
for (int i = 0; i < params.length; i++)
{
sb.append(params[i]);
if(i < params.length - 1) sb.append(",");
}
sb.append('}');
return sb.toString();
}
/** Log a method entry with Level.FINER and message "ENTRY" */
public void entering() {
logg(Level.FINER, "ENTRY"); }
public void entering(String sourceClass, String sourceMethod) {
logg(Level.FINER, sourceClass+":"+sourceMethod+" ENTRY"); }
/** Log a method entry with Level.FINER and message "ENTRY" and parametr content appended */
public void entering(Object param1) {
logg(Level.FINER, "ENTRY "+param1); }
public void entering(String sourceClass, String sourceMethod, Object param1) {
logg(Level.FINER, sourceClass+":"+sourceMethod+" ENTRY "+param1); }
/** Log a method entry with Level.FINER and message "ENTRY" and parametrs content appended */
public void entering(Object[] params) {
logg(Level.FINER, "ENTRY "+parametersToList(params));
}
public void entering(String sourceClass, String sourceMethod, Object[] params) {
logg(Level.FINER, sourceClass+":"+sourceMethod+" ENTRY "+parametersToList(params));
}
/** Log a method return with Level.FINER and message "RETURN" */
public void exiting() {
logg(Level.FINER, "RETURN");
}
public void exiting(String sourceClass, String sourceMethod) {
logg(Level.FINER, sourceClass+":"+sourceMethod+" RETURN");
}
/** Log a method return with Level.FINER and message "RETURN" and return value appended */
public Object exiting(Object result) {
logg(Level.FINER, "RETURN "+result);
return result;
}
public Object exiting(String sourceClass, String sourceMethod, Object result) {
logg(Level.FINER, sourceClass+":"+sourceMethod+" RETURN "+result);
return result;
}
/** Log an exception thrown with Level.FINER and message "THROW" and exception appended*/
public Throwable throwing(Throwable thrown) {
logg(Level.SEVERE, "THROW", thrown);
return thrown;
}
public Throwable throwing(String sourceClass,
String sourceMethod,
Throwable thrown)
{
logg(Level.SEVERE, sourceClass+":"+sourceMethod+" THROW", thrown);
return thrown;
}
/** report that exception was caught with log message at FINER level and "CAUGHT" message */
public Throwable caught(Throwable thrown)
{
logg(Level.SEVERE, "CAUGHT", thrown);
return thrown;
}
public Throwable caught(String sourceClass,
String sourceMethod,
Throwable thrown)
{
logg(Level.SEVERE, sourceClass+":"+sourceMethod+" CAUGHT", thrown);
return thrown;
}
public void severe(String msg) { logg(Level.SEVERE, msg); }
public void warning(String msg) { logg(Level.WARNING, msg); }
public void info(String msg) { logg(Level.INFO, msg); }
public void config(String msg) { logg(Level.CONFIG, msg); }
public void fine(String msg) { logg(Level.FINE, msg); }
public void finer(String msg) { logg(Level.FINER, msg); }
public void finest(String msg) { logg(Level.FINEST, msg); }
public void log(Level level, String msg) { logg(level, msg); }
public void log(Level level, String msg, Throwable thrown)
{ logg(level, msg, thrown); }
public void log(Level level, String msg, Object param1)
{ logg(level, msg+" "+param1); }
public void log(Level level, String msg, Object[] params)
{ logg(level, msg+" "+parametersToList(params)); }
public void severe(String msg, Throwable thrown)
{ logg(Level.SEVERE, msg, thrown); }
public void warning(String msg, Throwable thrown)
{ logg(Level.WARNING, msg, thrown); }
public void info(String msg, Throwable thrown)
{ logg(Level.INFO, msg, thrown); }
public void config(String msg, Throwable thrown)
{ logg(Level.CONFIG, msg, thrown); }
public void fine(String msg, Throwable thrown)
{ logg(Level.FINE, msg, thrown); }
public void finer(String msg, Throwable thrown)
{ logg(Level.FINER, msg, thrown); }
public void finest(String msg, Throwable thrown)
{ logg(Level.FINEST, msg, thrown); }
public static synchronized PrintStream getSink() {
return sink;
}
public static synchronized void setSink(PrintStream ps) {
if(!QUIET) System.err.println("SLogger sink="+ps);
if(sink != System.err) {
String s = global.queryPrefix(Level.ALL, DEFAULT_NESTING_LEVEL)
+ "SLogger.setSink() can only be called once";
ps.println(s);
throw new IllegalArgumentException("sink can be only set once");
}
sink = ps;
}
// --- internal state
public void logg(Level level, String msg) {
// if(!isLoggable(level)) {
// return;
// }
// String s = queryPrefix(DEFAULT_NESTING_LEVEL) + msg;
// //"\n\tat zoo.Zoo.sayMoo(Zoo.java:15) "
// synchronized(getClass()) {
// sink.println(s);
// }
// if(!Log.ON) {
// throw new RuntimeException(
// "For efficiency reasons use code: if(Log.ON) log(..)");
// }
logg(level, msg, null, DEFAULT_NESTING_LEVEL + 1);
}
public void logg(Level level, String msg, Throwable thrown) {
logg(level, msg, thrown, DEFAULT_NESTING_LEVEL + 1);
}
public void logg(Level level, String msg, Throwable thrown, int nestingLevel) {
if(!isLoggable(level)) return;
String s = queryPrefix(level, nestingLevel) + (multiline ? "\n" : "") + msg;
if(wrapCol > 0) {
s = wrap(s, wrapCol);
}
synchronized(getClass()) {
if(thrown == null) {
sink.println(s);
} else {
sink.print(s+" exception: ");
thrown.printStackTrace(sink);
}
}
if(!Log.ON) {
throw new RuntimeException(
"For efficiency reasons use code: if(Log.ON) log(..)");
}
}
private static String wrap(String s, int wrapCol) {
int len = s.length();
if(len < wrapCol) {
return s;
}
int extraLines = (len / wrapCol) + 1;
StringBuffer buf = new StringBuffer(len + 2 * extraLines);
//wrapCol -= 8; //leave 8 characters off for tabulators
int count = 0; //-8; //first line has no tabs
int start = 0;
int pos = 0;
while(pos < len) {
char c = s.charAt(pos);
//buf.append(c);
if(c == '\n' || c == '\r') {
count = 0;
//buf.append('\t');
//count = 8;
//} else if(c == '\r') {
// count = 0;
} else if(c == '\t' ) {
count += 8;
} else if(count > wrapCol) {
int i = pos;
while(i > start) {
char backC = s.charAt(i);
if( Character.isWhitespace(backC) ) {
break;
}
--i;
}
if(i > start) {
buf.append(s.substring(start, i));
start = pos = i + 1; //pass over whitespace
} else {
buf.append(s.substring(start, pos));
start = pos;
}
buf.append("\n");
count = 0;
//buf.append("\n\t");
//count = 8;
} else {
++count;
}
++pos;
}
buf.append(s.substring(start));
return buf.toString();
}
private String queryPrefix(Level level, int nestingLevel) {
//TODO: is it faster than shared myLoc and synchronizing(myLoc) (considering printing...)?
Location myLoc = new Location();
StringBuffer buf = new StringBuffer(80);
buf.append("[ ");
if(guessLocation(myLoc, nestingLevel)) {
if(showTime) {
long now = System.currentTimeMillis();
formatTime(now, buf);
buf.append(' ');
}
String tname = Thread.currentThread().getName(); //toString();
//long ltid = Thread.currentThread().hashCode();
//String tid = Long.toString(ltid, 16);
buf.append(tname).append(": ");
//e.printStackTrace(System.out);
boolean addSpaceAfterLoggerName = true;
String loggerNameToPrint = myName;
if(myLoc.fileName != null) {
int i = myLoc.fileName.lastIndexOf(".java");
if(i > 0) {
String s = myLoc.fileName.substring(0, i);
if(myName.endsWith(s)) {
int j = myName.length() - s.length();
loggerNameToPrint = myName.substring(0, j); //+"%"+ myName.substring(j);
addSpaceAfterLoggerName = false;
}
}
}
buf.append(loggerNameToPrint);
if(addSpaceAfterLoggerName) {
buf.append(" ");
if(myLoc.packageName != null && false == myLoc.packageName.equals(myName)) {
buf.append(myLoc.packageName).append('.');
}
}
if(myLoc.line != -1) {
buf.append(myLoc.fileName).append(':')
.append(myLoc.line).append(' ');
} else {
buf.append(myLoc.fileName).append(' ')
.append(myLoc.klass).append(' ');
}
//buf.append(level);
buf.append(myLoc.method).append(' ');
if(Level.ALL.equals(level) || Level.FINEST.equals(level)) {
} else if(Level.FINER.equals(level)) {
buf.append('+');
} else if(Level.FINE.equals(level)) {
buf.append("#");
} else if(Level.CONFIG.equals(level)) {
buf.append('%');
} else if(Level.INFO.equals(level)) {
buf.append('?');
} else if(Level.SEVERE.equals(level)) {
buf.append("!");
} else {
buf.append(level);
}
} else {
buf.append(myName).append(' ');
}
buf.append("] ");
//}
return buf.toString();
}
//private static boolean guessLocation(Location loc, int nestingLevel) {
// return guessLocation(loc, nestingLevel);
//}
private static boolean guessLocation(Location loc, int nestingLevel) {
if(guessFailed) return false;
Throwable throwable = new Throwable();
StringWriter sw = new StringWriter();
throwable.printStackTrace(new PrintWriter(sw));
String s = sw.toString()+"\n";
String line = null;
try {
// skip two lines
int i = s.indexOf('\n');
for(int lines = 0; lines < nestingLevel; ++lines)
i = s.indexOf('\n', i+1);
int j = s.indexOf('\n', i+1);
// extract info from line ex.:
// " at soaprmi.soapenc.SoapEnc.readObject(SoapEnc.java:116)");
///System.err.println("s="+s+" i="+i+" j="+j);
line = s.substring(i+1, j);
if(DEBUG) System.err.println("line="+line);
i = line.indexOf('(');
if(i < 0) {
if(DEBUG) System.err.println("problem trace="+s+" line="+line);
line = "at