package org.openntf.domino.logging; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Formatter; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.LogRecord; import java.util.logging.Logger; import org.openntf.domino.Database; import org.openntf.domino.ExceptionDetails; import org.openntf.domino.Session; import org.openntf.domino.exceptions.OpenNTFNotesException; import org.openntf.domino.utils.Factory; import org.openntf.domino.utils.Factory.SessionType; import org.openntf.formula.EvaluateException; import org.openntf.formula.FormulaContext; import org.openntf.formula.Formulas; public class LogFilterHandler extends Handler { private class L_HandlerEx { Handler _handler; boolean _mightPublish; boolean _invalidated; L_HandlerEx(final Handler h) { _handler = h; _mightPublish = true; _invalidated = false; } } private class L_HandlerUpdateEntry { LogHandlerUpdateIF _oldHandlerUIF; Map.Entry<LogConfig.L_LogHandler, L_HandlerEx> _newHandlerEnt; L_HandlerEx _oldHex; LogHandlerConfigIF _newHandlerConfig; LogHandlerConfigIF _oldHandlerConfig; boolean _useDefaultFormatter; Formatter _newFormatter; L_HandlerUpdateEntry(final LogHandlerUpdateIF oldHandlerUIF, final Map.Entry<LogConfig.L_LogHandler, L_HandlerEx> newHandlerEnt, final L_HandlerEx oldHex, final LogHandlerConfigIF newHandlerConfig, final LogHandlerConfigIF oldHandlerConfig, final boolean useDefaultFormatter, final Formatter newFormatter) { _oldHandlerUIF = oldHandlerUIF; _newHandlerEnt = newHandlerEnt; _oldHex = oldHex; _newHandlerConfig = newHandlerConfig; _oldHandlerConfig = oldHandlerConfig; _useDefaultFormatter = useDefaultFormatter; _newFormatter = newFormatter; } } private LogConfig.L_LogFilterHandler _myConfigLFH; private HashMap<LogConfig.L_LogHandler, L_HandlerEx> _myHandlers; private boolean _activated = false; private Set<L_HandlerUpdateEntry> _handlerUpdateSet = null; public LogFilterHandler() { super(); _myHandlers = new HashMap<LogConfig.L_LogHandler, L_HandlerEx>(); } public static LogFilterHandler getInitializedInstance(final LogConfig.L_LogFilterHandler lfh, final LogConfig oldConfig) throws Exception { LogFilterHandler ret = new LogFilterHandler(); ret.startUp(lfh, oldConfig); return ret; } public void startUp(final LogConfig.L_LogFilterHandler lfh, final LogConfig oldConfig) throws Exception { try { _myConfigLFH = lfh; _myConfigLFH._myHandler = this; setUpMyLevel(); getMyHandlers(); setUpMyHandlers(oldConfig); } catch (Exception e) { finishUp(); throw e; } } private void setUpMyLevel() { Level l = _myConfigLFH._defaultLevel; for (LogConfig.L_LogFilterHandler.L_LogFilterConfigEntry fce : _myConfigLFH._logFCEList) if (fce._level.intValue() < l.intValue()) l = fce._level; setLevel(l); } private void getMyHandlers() { _myHandlers.clear(); for (int i = 0; i < _myConfigLFH._defaultHandlers.length; i++) _myHandlers.put(_myConfigLFH._defaultHandlers[i], null); for (LogConfig.L_LogFilterHandler.L_LogFilterConfigEntry fce : _myConfigLFH._logFCEList) for (int i = 0; i < fce._logHandlerObjs.length; i++) _myHandlers.put(fce._logHandlerObjs[i], null); } private void setUpMyHandlers(final LogConfig oldConfig) throws Exception { Set<Map.Entry<LogConfig.L_LogHandler, L_HandlerEx>> handlerSet = _myHandlers.entrySet(); for (Map.Entry<LogConfig.L_LogHandler, L_HandlerEx> handlerEnt : handlerSet) { Map.Entry<LogConfig.L_LogHandler, L_HandlerEx> oldHandlerEnt = null; do { if (oldConfig == null) break; LogConfig.L_LogFilterHandler oldLLFH = oldConfig._logFilterHandlers.get(_myConfigLFH._myName); if (oldLLFH == null) break; LogConfig.L_LogHandler oldLH = oldConfig._logHandlers.get(handlerEnt.getKey()._handlerName); if (oldLH == null) break; Set<Map.Entry<LogConfig.L_LogHandler, L_HandlerEx>> oldHandEnts = oldLLFH._myHandler._myHandlers.entrySet(); for (Map.Entry<LogConfig.L_LogHandler, L_HandlerEx> me : oldHandEnts) if (me.getKey() == oldLH) { oldHandlerEnt = me; break; } } while (false); setUpHandler(handlerEnt, oldHandlerEnt); } } private void setUpHandler(final Map.Entry<LogConfig.L_LogHandler, L_HandlerEx> handlerEnt, final Map.Entry<LogConfig.L_LogHandler, L_HandlerEx> oldHandlerEnt) throws Exception { if (oldHandlerEnt != null) if (tryUpdateHandler(handlerEnt, oldHandlerEnt)) return; LogConfig.L_LogHandler handlerCfgEnt = handlerEnt.getKey(); boolean useDefaultFormatter = (handlerCfgEnt._formatterClass == null); try { Handler newHandler = (Handler) handlerCfgEnt._handlerGetInstance .invoke(null, handlerCfgEnt._handlerConfig, useDefaultFormatter); handlerEnt.setValue(new L_HandlerEx(newHandler)); if (!useDefaultFormatter) newHandler.setFormatter((Formatter) handlerCfgEnt._formatterGetInstance.invoke(null)); } catch (Exception e) { System.err.println("Logging: Error setting up Handler " + handlerCfgEnt._handlerClassName); throw e; } } private boolean tryUpdateHandler(final Map.Entry<LogConfig.L_LogHandler, L_HandlerEx> handlerEnt, final Map.Entry<LogConfig.L_LogHandler, L_HandlerEx> oldHandlerEnt) { LogConfig.L_LogHandler handlerCfgEnt = handlerEnt.getKey(); LogConfig.L_LogHandler oldHandlerCfgEnt = oldHandlerEnt.getKey(); if (handlerCfgEnt._handlerClass != oldHandlerCfgEnt._handlerClass) return false; L_HandlerEx oldHex = oldHandlerEnt.getValue(); if (!(oldHex._handler instanceof LogHandlerUpdateIF)) return false; LogHandlerUpdateIF oldHandlerUIF = (LogHandlerUpdateIF) oldHex._handler; if (!oldHandlerUIF.mayUpdateYourself(handlerCfgEnt._handlerConfig, oldHandlerCfgEnt._handlerConfig)) return false; boolean useDefaultFormatter = (handlerCfgEnt._formatterClass == null); Formatter formatter = null; if (!useDefaultFormatter && handlerCfgEnt._formatterClass != oldHandlerCfgEnt._formatterClass) try { formatter = (Formatter) handlerCfgEnt._formatterGetInstance.invoke(null); } catch (Exception e) { System.err.println("Unexpected Exception " + e.getClass().getName() + " in tryUpdateHandler:"); e.printStackTrace(); return false; } if (_handlerUpdateSet == null) _handlerUpdateSet = new HashSet<L_HandlerUpdateEntry>(); _handlerUpdateSet.add(new L_HandlerUpdateEntry(oldHandlerUIF, handlerEnt, oldHex, handlerCfgEnt._handlerConfig, oldHandlerCfgEnt._handlerConfig, useDefaultFormatter, formatter)); return true; } void activateYourself(final LogFilterHandler[] oldLFHs) { if (_handlerUpdateSet != null) { for (L_HandlerUpdateEntry hue : _handlerUpdateSet) { hue._oldHex._invalidated = true; L_HandlerEx newHex = new L_HandlerEx(hue._oldHex._handler); hue._newHandlerEnt.setValue(newHex); hue._oldHex._handler = null; hue._oldHandlerUIF.doUpdateYourself(hue._newHandlerConfig, hue._oldHandlerConfig, hue._useDefaultFormatter, hue._newFormatter); } _handlerUpdateSet = null; } _myConfigLFH._loggers = new Logger[_myConfigLFH._loggerNames.length]; for (int i = 0; i < _myConfigLFH._loggerNames.length; i++) _myConfigLFH._loggers[i] = activateOneLogger(_myConfigLFH._loggerNames[i], oldLFHs); _activated = true; } private Logger activateOneLogger(final String loggerName, final LogFilterHandler[] oldLFHs) { Logger l = Logger.getLogger(loggerName); l.setLevel(getLevel()); for (int i = 0; i < oldLFHs.length; i++) { oldLFHs[i].close(); l.removeHandler(oldLFHs[i]); } l.addHandler(this); l.setUseParentHandlers(false); LogManager.getLogManager().addLogger(l); return l; } public void finishUp() { if (_myConfigLFH._loggers != null) for (int i = 0; i < _myConfigLFH._loggers.length; i++) _myConfigLFH._loggers[i].removeHandler(this); close(); Set<Map.Entry<LogConfig.L_LogHandler, L_HandlerEx>> handlerSet = _myHandlers.entrySet(); for (Map.Entry<LogConfig.L_LogHandler, L_HandlerEx> handlerEnt : handlerSet) handlerEnt.setValue(null); _activated = false; } @Override public void close() { // if (!_startUpDone) // return; Collection<L_HandlerEx> collHand = _myHandlers.values(); for (L_HandlerEx h : collHand) if (h != null && h._handler != null) h._handler.close(); } @Override public void flush() { if (!_activated) return; Collection<L_HandlerEx> collHand = _myHandlers.values(); for (L_HandlerEx h : collHand) if (h != null && h._handler != null) h._handler.flush(); } @Override public synchronized void publish(final LogRecord logRec) { if (!_activated) return; if (publishing_.get() == Boolean.TRUE) // this prevents loopig return; publishing_.set(Boolean.TRUE); try { if (Logging._verbose) System.out.println("Logging: " + logRec.getLoggerName() + " | " + logRec.getLevel().getName() + ": " + logRec.getMessage()); resetMightPublish(logRec.getLevel()); publishDefault(logRec); Map<String, Object> contextMap = null; for (LogConfig.L_LogFilterHandler.L_LogFilterConfigEntry fce : _myConfigLFH._logFCEList) contextMap = publishFCE(fce, logRec, contextMap); } finally { publishing_.set(Boolean.FALSE); } } private static ThreadLocal<Boolean> publishing_ = new ThreadLocal<Boolean>() { @Override protected Boolean initialValue() { return Boolean.FALSE; } }; private void resetMightPublish(final Level l) { Set<Map.Entry<LogConfig.L_LogHandler, L_HandlerEx>> handlerSet = _myHandlers.entrySet(); for (Map.Entry<LogConfig.L_LogHandler, L_HandlerEx> handlerEnt : handlerSet) { Level handlerMinLevel = handlerEnt.getKey()._minimalLevel; handlerEnt.getValue()._mightPublish = (handlerMinLevel == null || l.intValue() >= handlerMinLevel.intValue()); } } private void publishDefault(final LogRecord logRec) { if (logRec.getLevel().intValue() < _myConfigLFH._defaultLevel.intValue()) return; for (int i = 0; i < _myConfigLFH._defaultHandlers.length; i++) publishConditionally(_myConfigLFH._defaultHandlers[i], logRec); } private void publishConditionally(final LogConfig.L_LogHandler lHandler, final LogRecord logRec) { L_HandlerEx hex = _myHandlers.get(lHandler); if (hex._mightPublish && !hex._invalidated) { if (Logging._verbose) System.out.println("Publishing with Handler " + lHandler._handlerName); hex._handler.publish(logRec); hex._mightPublish = false; } } private Map<String, Object> publishFCE(final LogConfig.L_LogFilterHandler.L_LogFilterConfigEntry fce, final LogRecord logRec, Map<String, Object> contextMap) { if (!logRec.getLoggerName().startsWith(fce._logPrefix) || // Trivial preconditions logRec.getLevel().intValue() < fce._level.intValue()) return contextMap; /* If FCE entry has expired meanwhile, let next config update throw it away. */ if (fce._validUntil != null && logRec.getMillis() > fce._validUntil.getTime()) return contextMap; /* Look for a handler that hasn't yet published the record in question */ int i; for (i = 0; i < fce._logHandlerObjs.length; i++) if (_myHandlers.get(fce._logHandlerObjs[i])._mightPublish) break; if (i >= fce._logHandlerObjs.length) return contextMap; /* Finally, inspect complex condition */ if (fce._parsedCond != null) { if (contextMap == null) { contextMap = new HashMap<String, Object>(); contextMap.put(LogConfig.cLoggerName, logRec.getLoggerName()); contextMap.put(LogConfig.cLogMessage, logRec.getMessage()); } if (fce._condContUserName || fce._condContDBPath) { if (!insertCurrentContext(fce, contextMap, logRec.getThrown())) return contextMap; } FormulaContext ctx = Formulas.createContext(contextMap, LogConfig.L_LogFilterHandler.L_LogFilterConfigEntry._myFormulaParser.get()); List<Object> result = null; try { result = fce._parsedCond.solve(ctx); } catch (EvaluateException e) { System.err.println("LogFilterHandler: Exception during condition check:"); e.printStackTrace(); } Object o = null; if (result != null && result.size() == 1) { o = result.get(0); if (!(o instanceof Boolean) || !((Boolean) o)) o = null; } if (o == null) // condition not fulfilled or result not size 1-boolean (or exception) return contextMap; } /* ... and now we are ready to publish */ for (i = 0; i < fce._logHandlerObjs.length; i++) publishConditionally(fce._logHandlerObjs[i], logRec); return contextMap; } /** * This method tries to insert the current context * * @param fce * @param publishDocMap * @param exception * @return */ private boolean insertCurrentContext(final LogConfig.L_LogFilterHandler.L_LogFilterConfigEntry fce, final Map<String, Object> publishDocMap, final Throwable exception) { if (contextComplete(fce, publishDocMap)) return true; /* Try first to get from ExceptionDetails - that's cheap */ if (exception instanceof OpenNTFNotesException) { List<ExceptionDetails.Entry> excds = ((OpenNTFNotesException) exception).getExceptionDetails(); if (excds != null) { contextFromExceptionDetails(publishDocMap, ((OpenNTFNotesException) exception).getExceptionDetails()); if (contextComplete(fce, publishDocMap)) return true; } } /* We have to ask Session - that's not cheap */ try { Session sess = Factory.getSession_unchecked(SessionType.CURRENT); if (sess == null) // then we can't evaluate the condition return false; if (fce._condContUserName && !publishDocMap.containsKey(LogConfig.cUserName)) publishDocMap.put(LogConfig.cUserName, sess.getEffectiveUserName()); if (fce._condContDBPath && !publishDocMap.containsKey(LogConfig.cDBPath)) publishDocMap.put(LogConfig.cDBPath, sess.getCurrentDatabase().getApiPath()); } catch (Exception e) { System.err.println("LogFilterHandler: Exception " + e.getClass().getName() + " in Session.getXX:"); e.printStackTrace(); return false; } return true; } /** * Returns TRUE, if the context is complete available for this FCE * * @param fce * the LogFilterConfigEntry * @param contextMap * @return */ private boolean contextComplete(final LogConfig.L_LogFilterHandler.L_LogFilterConfigEntry fce, final Map<String, Object> contextMap) { if (fce._condContUserName) if (!contextMap.containsKey(LogConfig.cUserName)) return false; if (fce._condContDBPath) if (!contextMap.containsKey(LogConfig.cDBPath)) return false; return true; } /** * reads the context from the exception details. Reads userName and database * * @param fce * the FilterConfigEntry * @param contextMap * the map used for the formula engine * @param exceptionDetails */ private void contextFromExceptionDetails(final Map<String, Object> contextMap, final List<ExceptionDetails.Entry> exceptionDetails) { for (ExceptionDetails.Entry detail : exceptionDetails) { if (Session.class.isAssignableFrom(detail.getSource())) { contextMap.put(LogConfig.cUserName, detail.getMessage()); } else if (Database.class.isAssignableFrom(detail.getSource())) { contextMap.put(LogConfig.cDBPath, detail.getMessage()); } } } }