/*
* Part of the CCNx Java Library.
*
* Copyright (C) 2008-2012 Palo Alto Research Center, Inc.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. You should have received
* a copy of the GNU Lesser General Public License along with this library;
* if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301 USA.
*/
/**
* PD org.ccnx.ccn.impl.support
*/
package org.ccnx.ccn.impl.support;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.ccnx.ccn.config.SystemConfiguration;
/**
* Wrapper for the standard java.util Logging classes.
*
* This allows log messages which will not actually be output due to being at a lower
* level than the current logging level to not affect performance by performing expensive calculations to
* compute their parameters.
*
* To send log entries to file, specify the log output directory using either the system property
* org.ccnx.ccn.LogDir or the environment variable CCN_LOG_DIR. To override the default
* log level for whatever program you are running, set the system property org.ccnx.ccn.LogLevel.
*/
public class Log {
/**
* Allow override on command line or from configuration file.
*/
public static final String DEFAULT_APPLICATION_CLASS =
"ccnx";
public static final String DEFAULT_LOG_FILE = "ccn_";
public static final String DEFAULT_LOG_SUFFIX = ".log";
protected static final int offValue = Level.OFF.intValue();
/**
* Properties and environment variables to set log parameters.
*/
public static final String DEFAULT_LOG_LEVEL_PROPERTY = "org.ccnx.ccn.LogLevel";
public static final String DEFAULT_LOG_LEVEL_ENV = "CCN_LOG_LEVEL";
public static final String LOG_DIR_PROPERTY = "org.ccnx.ccn.LogDir";
public static final String LOG_DIR_ENV = "CCN_LOG_DIR";
protected static Logger _systemLogger = null;
protected static Logger[] _facilityLoggers = null;
//static int _level;
//static boolean useDefaultLevel = true; // reset if an external override of the default level was specified
// ==========================================================
// Facility based logging.
// To add a new facility:
// 1) Add a public final static int for it
// 2) Add a facility name to FAC_LOG_LEVEL_PROPERTY array
// 3) Add a facility name to FAC_LOG_LEVEL_ENV array
// 4) Set the default log level in FAC_DEFAULT_LOG_LEVEL array
// The facilities FAC_ALL and FAC_DEFAULT must be the values 0 and 1 respectively
// Definition of logging facilities
public static final int FAC_ALL = 0;
public static final int FAC_DEFAULT = 1;
public static final int FAC_PIPELINE = 2;
public static final int FAC_NETMANAGER = 3;
public static final int FAC_USER0 = 4;
public static final int FAC_USER1 = 5;
public static final int FAC_USER2 = 6;
public static final int FAC_USER3 = 7;
public static final int FAC_ACCESSCONTROL = 8;
public static final int FAC_REPO = 9;
public static final int FAC_TIMING = 10; // includes a timestamp
public static final int FAC_TRUST = 11; // trust enforcement
public static final int FAC_KEYS = 12; // key publishing/retrieval
public static final int FAC_ENCODING = 13;
public static final int FAC_IO = 14;
public static final int FAC_SIGNING = 15;
public static final int FAC_VERIFY = 16;
public static final int FAC_USER4 = 17;
public static final int FAC_USER5 = 18;
public static final int FAC_USER6 = 19;
public static final int FAC_USER7 = 20;
public static final int FAC_USER8 = 21;
public static final int FAC_USER9 = 22;
public static final int FAC_USER10 = 23;
public static final int FAC_USER11 = 24;
public static final int FAC_USER12 = 25;
public static final int FAC_USER13 = 26;
public static final int FAC_USER14 = 27;
public static final int FAC_USER15 = 28;
public static final int FAC_TEST = 29; // For Junit tests
public static final int FAC_SEARCH = 30; // name enumeration, path finder
public static final int FAC_SYNC = 31; // sync api and sync control traffic processing
protected static final String [] FAC_NAME = {
"ccnx.All", // should never show up
"ccnx.Default",
"ccnx.Pipeline",
"ccnx.NetManager",
"ccnx.User0",
"ccnx.User1",
"ccnx.User2",
"ccnx.User3",
"ccnx.AccessControl",
"ccnx.Repo",
"ccnx.Timing",
"ccnx.Trust",
"ccnx.Keys",
"ccnx.Encoding",
"ccnx.IO",
"ccnx.Signing",
"ccnx.Verify",
"ccnx.User4",
"ccnx.User5",
"ccnx.User6",
"ccnx.User7",
"ccnx.User8",
"ccnx.User9",
"ccnx.User10",
"ccnx.User11",
"ccnx.User12",
"ccnx.User13",
"ccnx.User14",
"ccnx.User15",
"ccnx.Test",
"ccnx.Search",
"ccnx.Sync",
};
// The System property name for each Facility
public static final String [] FAC_LOG_LEVEL_PROPERTY = {
DEFAULT_LOG_LEVEL_PROPERTY + ".All",
DEFAULT_LOG_LEVEL_PROPERTY,
DEFAULT_LOG_LEVEL_PROPERTY + ".Pipeline",
DEFAULT_LOG_LEVEL_PROPERTY + ".NetManager",
DEFAULT_LOG_LEVEL_PROPERTY + ".User0",
DEFAULT_LOG_LEVEL_PROPERTY + ".User1",
DEFAULT_LOG_LEVEL_PROPERTY + ".User2",
DEFAULT_LOG_LEVEL_PROPERTY + ".User3",
DEFAULT_LOG_LEVEL_PROPERTY + ".AccessControl",
DEFAULT_LOG_LEVEL_PROPERTY + ".Repo",
DEFAULT_LOG_LEVEL_PROPERTY + ".Timing",
DEFAULT_LOG_LEVEL_PROPERTY + ".Trust",
DEFAULT_LOG_LEVEL_PROPERTY + ".Keys",
DEFAULT_LOG_LEVEL_PROPERTY + ".Encoding",
DEFAULT_LOG_LEVEL_PROPERTY + ".IO",
DEFAULT_LOG_LEVEL_PROPERTY + ".Signing",
DEFAULT_LOG_LEVEL_PROPERTY + ".Verify",
DEFAULT_LOG_LEVEL_PROPERTY + ".User4",
DEFAULT_LOG_LEVEL_PROPERTY + ".User5",
DEFAULT_LOG_LEVEL_PROPERTY + ".User6",
DEFAULT_LOG_LEVEL_PROPERTY + ".User7",
DEFAULT_LOG_LEVEL_PROPERTY + ".User8",
DEFAULT_LOG_LEVEL_PROPERTY + ".User9",
DEFAULT_LOG_LEVEL_PROPERTY + ".User10",
DEFAULT_LOG_LEVEL_PROPERTY + ".User11",
DEFAULT_LOG_LEVEL_PROPERTY + ".User12",
DEFAULT_LOG_LEVEL_PROPERTY + ".User13",
DEFAULT_LOG_LEVEL_PROPERTY + ".User14",
DEFAULT_LOG_LEVEL_PROPERTY + ".User15",
DEFAULT_LOG_LEVEL_PROPERTY + ".Test",
DEFAULT_LOG_LEVEL_PROPERTY + ".Search",
DEFAULT_LOG_LEVEL_PROPERTY + ".Sync",
};
// The environment variable for each facility
public static final String [] FAC_LOG_LEVEL_ENV = {
DEFAULT_LOG_LEVEL_ENV + "_ALL",
DEFAULT_LOG_LEVEL_ENV,
DEFAULT_LOG_LEVEL_ENV + "_PIPELINE",
DEFAULT_LOG_LEVEL_ENV + "_NETMANAGER",
DEFAULT_LOG_LEVEL_ENV + "_USER0",
DEFAULT_LOG_LEVEL_ENV + "_USER1",
DEFAULT_LOG_LEVEL_ENV + "_USER2",
DEFAULT_LOG_LEVEL_ENV + "_USER3",
DEFAULT_LOG_LEVEL_ENV + "_ACCESSCONTROL",
DEFAULT_LOG_LEVEL_ENV + "_REPO",
DEFAULT_LOG_LEVEL_ENV + "_TIMING",
DEFAULT_LOG_LEVEL_ENV + "_TRUST",
DEFAULT_LOG_LEVEL_ENV + "_KEYS",
DEFAULT_LOG_LEVEL_ENV + "_ENCODING",
DEFAULT_LOG_LEVEL_ENV + "_IO",
DEFAULT_LOG_LEVEL_ENV + "_SIGNING",
DEFAULT_LOG_LEVEL_ENV + "_VERIFY",
DEFAULT_LOG_LEVEL_ENV + "_USER4",
DEFAULT_LOG_LEVEL_ENV + "_USER5",
DEFAULT_LOG_LEVEL_ENV + "_USER6",
DEFAULT_LOG_LEVEL_ENV + "_USER7",
DEFAULT_LOG_LEVEL_ENV + "_USER8",
DEFAULT_LOG_LEVEL_ENV + "_USER9",
DEFAULT_LOG_LEVEL_ENV + "_USER10",
DEFAULT_LOG_LEVEL_ENV + "_USER11",
DEFAULT_LOG_LEVEL_ENV + "_USER12",
DEFAULT_LOG_LEVEL_ENV + "_USER13",
DEFAULT_LOG_LEVEL_ENV + "_USER14",
DEFAULT_LOG_LEVEL_ENV + "_USER15",
DEFAULT_LOG_LEVEL_ENV + "_TEST",
DEFAULT_LOG_LEVEL_ENV + "_SEARCH",
DEFAULT_LOG_LEVEL_ENV + "_SYNC",
};
public static final Level [] FAC_LOG_LEVEL_DEFAULT = {
Level.OFF, // value has no meaning for All
Level.INFO, // Default
Level.WARNING, // Pipelining
Level.INFO, // NetManager
Level.INFO, // User0
Level.INFO, // User1
Level.INFO, // User2
Level.INFO, // User3
Level.INFO, // Access control
Level.INFO, // Repo
Level.INFO, // Timing
Level.INFO, // Trust
Level.INFO, // Keys
Level.INFO, // Encoding
Level.INFO, // IO
Level.INFO, // Signing
Level.INFO, // Verify
Level.INFO, // User4
Level.INFO, // User5
Level.INFO, // User6
Level.INFO, // User7
Level.INFO, // User8
Level.INFO, // User9
Level.INFO, // User10
Level.INFO, // User11
Level.INFO, // User12
Level.INFO, // User13
Level.INFO, // User14
Level.INFO, // User15
Level.INFO, // Test
Level.INFO, // Search
Level.INFO, // Sync
};
protected static Level [] _fac_level = new Level[FAC_LOG_LEVEL_PROPERTY.length];
protected static int [] _fac_value = new int[FAC_LOG_LEVEL_PROPERTY.length];
protected static boolean _timestamp = false;
// ==========================================================
static {
// Can add an append=true argument to generate appending behavior.
int nFac = FAC_NAME.length;
Handler theHandler = null;
_systemLogger = Logger.getLogger(DEFAULT_APPLICATION_CLASS);
_facilityLoggers = new Logger[nFac];
if (FAC_LOG_LEVEL_PROPERTY.length != nFac ||
FAC_LOG_LEVEL_ENV.length != nFac ||
FAC_LOG_LEVEL_DEFAULT.length != nFac) {
System.err.println("Log facility consistency check failed");
System.exit(1);
}
// We restrict logging based on our _fac_level, not on the system logger
_systemLogger.setLevel(Level.ALL);
for (int i=FAC_DEFAULT; i < nFac; i++) {
_facilityLoggers[i] = Logger.getLogger(FAC_NAME[i]);
_facilityLoggers[i].setLevel(Level.ALL);
}
// _systemLogger.info("Initializing CCNX Logging");
String logdir = System.getProperty(LOG_DIR_PROPERTY);
if (null == logdir) {
logdir = System.getenv(LOG_DIR_ENV);
}
// Only set up file handler if log directory is set
if (null != logdir) {
StringBuffer logFileName = new StringBuffer();
try {
// See if log dir exists, if not make it.
File dir = new File(logdir);
if (!dir.exists() || !dir.isDirectory()) {
if (!dir.mkdir()) {
System.err.println("Cannot open log directory "
+ logdir);
throw new IOException("Cannot open log directory "
+ logdir);
}
}
String sep = System.getProperty("file.separator");
logFileName.append(logdir + sep + DEFAULT_LOG_FILE);
Date theDate = new Date();
SimpleDateFormat df = new SimpleDateFormat("yy-MM-dd-HHmmss");
logFileName.append(df.format(theDate));
String pid = SystemConfiguration.getPID();
if (null != pid) {
logFileName.append("-" + pid);
} else {
logFileName.append("-R" + new Random().nextInt(1000));
}
logFileName.append(DEFAULT_LOG_SUFFIX);
theHandler = new FileHandler(logFileName.toString());
// Force a standard XML encoding (avoids unusual ones like MacRoman in XML)
theHandler.setEncoding("UTF-8");
System.out.println("Writing log records to " + logFileName);
} catch (IOException e) {
// Can't open that file
System.err.println("Cannot open log file: " + logFileName);
e.printStackTrace();
theHandler = new ConsoleHandler();
}
}
if (null != theHandler) {
_systemLogger.addHandler(theHandler);
}
// Could just do a console handler if the file won't open.
// Do that eventually, for debugging put in both.
// Actually, right now, seem to get a console handler by default.
// This move is anti-social, but a starting point. We don't want
// any handlers to be more restrictive then the level set for
// our _systemLevel
Handler[] handlers = Logger.getLogger( "" ).getHandlers();
for (int index = 0; index < handlers.length; index++) {
handlers[index].setLevel(Level.ALL);
// TODO Enabling the following by default seems to cause ccn_repo to
// hang when run from the command line, at least on Leopard.
// Not sure why, so make it a special option.
if (SystemConfiguration.hasLoggingConfigurationProperty(SystemConfiguration.DETAILED_LOGGER)) {
if (handlers[index] instanceof ConsoleHandler) {
handlers[index].setFormatter(new DetailedFormatter());
}
}
}
// Allow override of default log level.
setLogLevels();
}
// deprecated
public static String getApplicationClass() {
return DEFAULT_APPLICATION_CLASS;
}
public static String getApplicationClass(int facility) {
return FAC_NAME[facility];
}
public static void exitApplication() {
// Clean up and get out, we've had an unrecovereable error.
_facilityLoggers[FAC_DEFAULT].severe("Exiting application.");
System.exit(-1);
}
public static void abort() {
_facilityLoggers[FAC_DEFAULT].warning("Unrecoverable error. Exiting data collection.");
exitApplication(); // save partial results?
}
// These following methods duplicate methods provided by java.util.Logger
// but add varargs functionality which allows args to only have .toString()
// called when logging is enabled.
/**
* Logs message with level = info
* @see Log#log(Level, String, Object...)
*/
public static void info(String msg, Object... params) {
doLog(FAC_DEFAULT, Level.INFO, msg, params);
}
public static void info(int facility, String msg, Object... params) {
if (FAC_DEFAULT <= facility && facility < _fac_level.length)
doLog(facility, Level.INFO, msg, params);
}
/**
* Logs message with level = warning
* @see Log#log(Level, String, Object...)
*/
public static void warning(String msg, Object... params) {
doLog(FAC_DEFAULT, Level.WARNING, msg, params);
}
public static void warning(int facility, String msg, Object... params) {
if (FAC_DEFAULT <= facility && facility < _fac_level.length)
doLog(facility, Level.WARNING, msg, params);
}
/**
* Logs message with level = severe
* @see Log#log(Level, String, Object...)
*/
public static void severe(String msg, Object... params) {
doLog(FAC_DEFAULT, Level.SEVERE, msg, params);
}
public static void severe(int facility, String msg, Object... params) {
if (FAC_DEFAULT <= facility && facility < _fac_level.length)
doLog(facility, Level.SEVERE, msg, params);
}
/**
* Logs message with level = fine
* @see Log#log(Level, String, Object...)
*/
public static void fine(String msg, Object... params) {
doLog(FAC_DEFAULT, Level.FINE, msg, params);
}
public static void fine(int facility, String msg, Object... params) {
if (FAC_DEFAULT <= facility && facility < _fac_level.length)
doLog(facility, Level.FINE, msg, params);
}
/**
* Logs message with level = finer
* @see Log#log(Level, String, Object...)
*/
public static void finer(String msg, Object... params) {
doLog(FAC_DEFAULT, Level.FINER, msg, params);
}
public static void finer(int facility, String msg, Object... params) {
if (FAC_DEFAULT <= facility && facility < _fac_level.length)
doLog(facility, Level.FINER, msg, params);
}
/**
* Logs message with level = finest
* @see Log#log(Level, String, Object...)
*/
public static void finest(String msg, Object... params) {
doLog(FAC_DEFAULT, Level.FINEST, msg, params);
}
public static void finest(int facility, String msg, Object... params) {
if (FAC_DEFAULT <= facility && facility < _fac_level.length)
doLog(facility, Level.FINEST, msg, params);
}
// pass these methods on to the java.util.Logger for convenience
public static void setLevel(Level l) {
_fac_level[FAC_DEFAULT] = l;
_fac_value[FAC_DEFAULT] = l.intValue();
}
// pass these methods on to the java.util.Logger for convenience
public static void setLevel(int facility, Level l) {
if (FAC_DEFAULT <= facility && facility < _fac_level.length) {
_fac_level[facility] = l;
_fac_value[facility] = l.intValue();
// System.out.println(String.format("Log.setLevel(%d, %s)", facility, l));
} else if (facility == FAC_ALL) {
for (int i=FAC_DEFAULT; i < _fac_level.length; i++) {
_fac_level[i] = l;
_fac_value[i] = l.intValue();
// System.out.println(String.format("Log.setLevel(%d, %s)", facility, l));
}
}
}
public static void setLevels(Level [] levels) {
if( levels.length != _fac_level.length ) {
doLog(FAC_DEFAULT, Level.SEVERE, "setLevels array incorrect size");
return;
}
// The 0 element (FAC_ALL) is always null
for( int i = 1; i < levels.length; i++ )
setLevel(i, levels[i]);
}
/**
* Set the default log level that will be in effect unless overridden by
* the system property. Use of this method allows a program to change the
* default logging level while still allowing external override by the user
* at runtime.
*
* This must be called before using setLevel(). Calling this method will
* reset all log levels to the default or to the system property level.
* @param l the new default level
*/
public static void setDefaultLevel(Level l) {
for (int i = FAC_DEFAULT; i < FAC_LOG_LEVEL_DEFAULT.length; i++ )
FAC_LOG_LEVEL_DEFAULT[i] = l;
setLogLevels();
}
public static void setDefaultLevel(int facility, Level l) {
if (FAC_DEFAULT <= facility && facility < FAC_LOG_LEVEL_DEFAULT.length) {
FAC_LOG_LEVEL_DEFAULT[facility] = l;
} else if (facility == FAC_ALL) {
for (int i = FAC_DEFAULT; i < FAC_LOG_LEVEL_DEFAULT.length; i++ ) {
FAC_LOG_LEVEL_DEFAULT[i] = l;
}
}
setLogLevels();
}
/**
* Set the facility log levels based on the defaults and system overrides
*/
public static void setLogLevels() {
String logLevelName;
Level logLevel;
// First get the FAC_ALL value, and if set use it to set the default log level for all facilities
logLevelName = SystemConfiguration.retrievePropertyOrEnvironmentVariable(
FAC_LOG_LEVEL_PROPERTY[FAC_ALL],
FAC_LOG_LEVEL_ENV[FAC_ALL],
null);
if (logLevelName != null) {
try {
logLevel = Level.parse(logLevelName);
for (int i = FAC_DEFAULT; i < FAC_LOG_LEVEL_DEFAULT.length; i++ ) {
FAC_LOG_LEVEL_DEFAULT[i] = logLevel;
}
} catch (IllegalArgumentException e) {
doLog(FAC_DEFAULT, Level.SEVERE, String.format("Error parsing property %s=%s",
FAC_LOG_LEVEL_PROPERTY[FAC_ALL], logLevelName));
e.printStackTrace();
}
}
// Then get the individual facility's log level from property/environment, or the default
for (int i = FAC_DEFAULT; i < FAC_LOG_LEVEL_PROPERTY.length; i++ ) {
logLevelName = SystemConfiguration.retrievePropertyOrEnvironmentVariable(
FAC_LOG_LEVEL_PROPERTY[i],
FAC_LOG_LEVEL_ENV[i],
FAC_LOG_LEVEL_DEFAULT[i].getName());
try {
logLevel = Level.parse(logLevelName);
} catch(IllegalArgumentException e) {
doLog(FAC_DEFAULT, Level.SEVERE, String.format("Error parsing property %s=%s", FAC_LOG_LEVEL_PROPERTY[i], logLevelName));
e.printStackTrace();
logLevel = FAC_LOG_LEVEL_DEFAULT[i];
}
if (logLevel.intValue() != FAC_LOG_LEVEL_DEFAULT[i].intValue())
doLog(FAC_DEFAULT, Level.INFO, String.format("Set log level for facility %s to %s",
FAC_LOG_LEVEL_PROPERTY[i], logLevel));
setLevel(i, logLevel);
}
}
/**
* Gets the current log level
* @return
*/
public static Level getLevel() {
return _fac_level[FAC_DEFAULT];
}
/**
* Gets the current log level
* @return may be null if invalid facility number
*/
public static Level getLevel(int facility) {
if (0 <= facility && facility < _fac_level.length)
return _fac_level[facility];
else
return null;
}
/**
* Returns a copy of the array of all log levels. The 0 element (FAC_ALL) will be null.
*/
public static Level [] getLevels() {
Level [] copy = new Level[_fac_level.length];
for(int i = 0; i < _fac_level.length; i++)
copy[i] = _fac_level[i];
return copy;
}
/**
* Would the given log level write to the log?
* @param level
* @return true means would write log
*/
public static boolean isLoggable(Level level) {
if (level.intValue() < _fac_value[FAC_DEFAULT] || _fac_value[FAC_DEFAULT] == offValue) {
return false;
}
return true;
}
/**
* Would the given log level write to the log?
* @param level
* @return true means would write log
*/
public static boolean isLoggable(int facility, Level level) {
if (FAC_DEFAULT <= facility && facility < _fac_level.length) {
if (level.intValue() < _fac_value[facility] || _fac_value[facility] == offValue) {
return false;
}
return true;
}
return false;
}
/**
* Set flag for enabling/disabling timestamps on all messages
*/
public static boolean setTimestamp(boolean enableTimestamp) {
boolean previous = _timestamp;
_timestamp = enableTimestamp;
return previous;
}
/**
* The main logging wrapper. Allows for variable parameters to the message.
* Using the variable parameters here rather then constructing the message
* yourself helps reduce CPU load when logging is disabled. (Since the
* params do not have their .toString() methods called if the message is not
* logged).
* @param l Log level.
* @param msg Message or format string. Note that to improve performance, only
* the simplest form of of MessageFormat, i.e. {0}, {1}, {2}... is supported
* @see java.text.MessageFormat
* @param params
*/
public static void log(Level l, String msg, Object... params) {
// we must call doLog() to ensure caller is in right place on stack
doLog(FAC_DEFAULT, l, msg, params);
}
public static void log(int facility, Level l, String msg, Object... params) {
// we must call doLog() to ensure caller is in right place on stack
if (0 <= facility && facility < _fac_level.length)
doLog(facility, l, msg, params);
}
protected static void doLog(int facility, Level l, String msg, Object... params) {
if (!isLoggable(facility, l))
return;
// Do this at the top of doLog to get timestamp closest to event
// Use %.6f to get format same as ccnd, even though we only have
// msec precision.
if( (facility == FAC_TIMING) || _timestamp) {
long now = System.currentTimeMillis();
double d = now / 1000.0;
msg = String.format("%.6f %s", d, msg);
}
StackTraceElement ste = Thread.currentThread().getStackTrace()[3];
Class<?> c;
try {
c = Class.forName(ste.getClassName());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
// Some loggers e.g. the XML logger do not substitute parameters correctly
// Therefore we do our own parameter substitution here and do not rely
// on the system logger's ability to do it.
int i = 0;
for(Object o : params) {
if (o == null) {
o = "(null)";
}
int index = msg.indexOf("{" + i++ + "}");
if (index >= 0) {
StringBuffer sb = new StringBuffer(msg.substring(0, index));
sb.append(o);
sb.append(msg.substring(index + 3));
msg = sb.toString();
}
}
_facilityLoggers[facility].logp(l, c.getCanonicalName(), ste.getMethodName(), msg);
}
public static void flush() {
Handler [] handlers = _facilityLoggers[FAC_DEFAULT].getHandlers();
for (int i=0; i < handlers.length; ++i) {
handlers[i].flush();
}
}
public static void warningStackTrace(Throwable t) {
logStackTrace(Level.WARNING, t);
}
public static void warningStackTrace(int facility, Throwable t) {
logStackTrace(facility, Level.WARNING, t);
}
public static void severeStackTrace(int facility, Throwable t) {
logStackTrace(facility, Level.WARNING, t);
}
public static void infoStackTrace(Throwable t) {
logStackTrace(Level.INFO, t);
}
public static void infoStackTrace(int facility, Throwable t) {
logStackTrace(facility, Level.INFO, t);
}
public static void logStackTrace(Level level, Throwable t) {
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
_facilityLoggers[FAC_DEFAULT].log(level, sw.toString());
}
public static void logStackTrace(int facility, Level level, Throwable t) {
if (!isLoggable(facility, level))
return;
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
_facilityLoggers[facility].log(level, sw.toString());
}
public static void logException(String message, Exception e) {
_facilityLoggers[FAC_DEFAULT].warning(message);
Log.warningStackTrace(e);
}
public static void logException(int facility, Level level, String message, Exception e) {
if (!isLoggable(facility, level))
return;
_facilityLoggers[facility].log(level, message);
logStackTrace(facility, level, e);
}
}