package org.openntf.domino.logging;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.zip.CRC32;
import org.openntf.domino.utils.Factory;
/**
* Introduces a highly configurable new logging mechanism. (Details are to be found in logconfig.properties.) If no configuration property
* file is found, logging is statically initialized as before (as a fallback solution ). If the configuration is changed, logging will
* update itself within 1 minute.
*
* @author steinsiek
*
*/
public class Logging {
private static ThreadLocal<SimpleDateFormat> sdfISO = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static String dateToString(final Date value) {
return sdfISO.get().format(value);
}
private static Logging _theLogger = new Logging();
private Logging() {
}
public static Logging getInstance() {
return _theLogger;
}
private LogConfig _activeConfig = null;
private Timer _supervisor = null;
private static final long _supervisorInterval = 60000; // 1 minute
public void startUp() throws IOException {
if (!activateCfgFromPropFile()) {
System.err.println("Logging: Couldn't initialize from PropertyFile; activating fallback ...");
startUpFallback();
return;
}
System.out.println("Logging: LogConfig successfully initialized from " + _logConfigPropFile);
// TODO: if we want to access a Config-DB we can use @Tasklet here
// Xots.getService().schedule(new Runnable() {
// @Override
// public void run() {
// Logging.getInstance().lookForCfgChange();
// }
// }, _supervisorInterval, TimeUnit.MILLISECONDS);
_supervisor = new Timer("LoggingSupervisor", true);
_supervisor.schedule(new TimerTask() {
@Override
public void run() {
Logging.getInstance().lookForCfgChange();
}
}, _supervisorInterval, _supervisorInterval);
}
private boolean activateCfgFromPropFile() {
LogConfig logCfg;
if ((logCfg = loadCfgFromPropFile()) == null)
return false;
if (!createFilterHandlers(logCfg))
return false;
if (_activeConfig == null) // Necessary only if starting up
if (!getCfgPropFileNumbers())
return false;
activateFilterHandlers(logCfg);
_activeConfig = logCfg;
return true;
}
static String _logConfigPropFile = null;
private LogConfig loadCfgFromPropFile() {
File logConfigFile = logCfgFilePrecheck();
if (logConfigFile == null)
return null;
Properties props;
try {
FileInputStream fis = new FileInputStream(logConfigFile);
props = new Properties();
props.load(fis);
fis.close();
} catch (Exception e) {
System.err.println("Logging.loadFromPropFile: Exception " + e.getClass().getName() + ":");
e.printStackTrace();
return null;
}
return LogConfig.fromProperties(props);
}
private File logCfgFilePrecheck() {
if (_logConfigPropFile == null)
_logConfigPropFile = Factory.getDataPath() + "/IBM_TECHNICAL_SUPPORT/org.openntf.domino.logging.logconfig.properties";
File ret = new File(_logConfigPropFile);
String errMsg = null;
if (!ret.exists())
errMsg = "not found";
else if (!ret.isFile())
errMsg = "isn't a normal file";
if (errMsg == null)
return ret;
System.err.println("Logging.logCfgFilePrecheck: File '" + _logConfigPropFile + "' " + errMsg);
return null;
}
private boolean createFilterHandlers(final LogConfig logCfg) {
try {
for (LogConfig.L_LogFilterHandler llfh : logCfg._logFilterHandlers.values())
LogFilterHandler.getInitializedInstance(llfh, _activeConfig);
return true;
} catch (Exception e) {
e.printStackTrace();
for (LogConfig.L_LogFilterHandler llfh : logCfg._logFilterHandlers.values())
if (llfh._myHandler != null) {
llfh._myHandler.finishUp();
llfh._myHandler = null;
}
return false;
}
}
private void activateFilterHandlers(final LogConfig logCfg) {
int sz = (_activeConfig == null) ? 0 : _activeConfig._logFilterHandlers.size();
LogFilterHandler[] oldLFHs = new LogFilterHandler[sz];
if (_activeConfig != null) {
int i = 0;
for (LogConfig.L_LogFilterHandler llfh : _activeConfig._logFilterHandlers.values())
oldLFHs[i++] = llfh._myHandler;
}
for (LogConfig.L_LogFilterHandler llfh : logCfg._logFilterHandlers.values())
llfh._myHandler.activateYourself(oldLFHs);
}
private void startUpFallback() throws IOException {
String pattern = Factory.getDataPath() + "/IBM_TECHNICAL_SUPPORT/org.openntf.%u.%g.log";
Logger oodLogger = Logger.getLogger("org.openntf.domino");
oodLogger.setLevel(Level.WARNING);
DefaultFileHandler dfh = new DefaultFileHandler(pattern, 50000, 100, true);
dfh.setFormatter(new FileFormatter());
dfh.setLevel(Level.WARNING);
oodLogger.addHandler(dfh);
DefaultConsoleHandler dch = new DefaultConsoleHandler();
dch.setFormatter(new ConsoleFormatter());
dch.setLevel(Level.WARNING);
oodLogger.addHandler(dch);
OpenLogHandler olh = new OpenLogHandler();
olh.setLogDbPath("OpenLog.nsf");
olh.setLevel(Level.WARNING);
oodLogger.addHandler(olh);
LogManager.getLogManager().addLogger(oodLogger);
}
private long _propFileLh;
private long _propFileCRC;
private boolean getCfgPropFileNumbers() {
long fileLh = 0;
long fileCRC = 0;
File f = logCfgFilePrecheck();
if (f == null)
return false;
try {
FileInputStream fis = new FileInputStream(f);
CRC32 crc = new CRC32();
byte[] buff = new byte[16384];
int got;
while ((got = fis.read(buff)) > 0) {
fileLh += got;
crc.update(buff, 0, got);
}
fis.close();
fileCRC = crc.getValue();
} catch (Exception e) {
System.err.println("Logging.getCfgPropFileNumbers: " + "Exception " + e.getClass().getName() + ": " + e.getMessage());
return false;
}
_propFileLh = fileLh;
_propFileCRC = fileCRC;
return true;
}
private static enum ConfigChangeFlag {
CFG_UNCHANGED, CFG_UPDATED, CFG_ERROR;
}
private ConfigChangeFlag lookForCfgChange() {
long oldFileLh = _propFileLh;
long oldFileCRC = _propFileCRC;
if (!getCfgPropFileNumbers())
return ConfigChangeFlag.CFG_ERROR;
if (_propFileLh == oldFileLh && _propFileCRC == oldFileCRC)
return ConfigChangeFlag.CFG_UNCHANGED;
if (activateCfgFromPropFile()) {
System.out.println("Logging: Updated LogConfig from changed PropertyFile");
return ConfigChangeFlag.CFG_UPDATED;
}
_propFileLh = oldFileLh;
_propFileCRC = oldFileCRC;
System.err.println("Logging: Couldn't update LogConfig from changed PropertyFile because of errors");
return ConfigChangeFlag.CFG_ERROR;
}
static boolean _verbose = false; // For testing
public static void setVerbose(final boolean how) {
_verbose = how;
}
}