/* * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004, 2005, 2006], Hyperic, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. This program 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 General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.hq.product; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperic.util.StringUtil; import org.hyperic.util.config.BooleanConfigOption; import org.hyperic.util.config.ConfigOption; import org.hyperic.util.config.ConfigResponse; import org.hyperic.util.config.ConfigSchema; import org.hyperic.util.config.EnumerationConfigOption; import org.hyperic.util.config.StringConfigOption; public class LogTrackPlugin extends GenericPlugin { private static HashMap logLevelCache = new HashMap(); private static Log log = LogFactory.getLog(LogTrackPlugin.class.getName()); static final boolean debugLogging = "true".equals(System.getProperty("log_track.debug")); /* Log levels */ public static final int LOGLEVEL_ANY = -1; public static final int LOGLEVEL_ERROR = 3; public static final int LOGLEVEL_WARN = 4; public static final int LOGLEVEL_INFO = 6; public static final int LOGLEVEL_DEBUG = 7; public static final String LOGLEVEL_ERROR_LABEL = "Error"; public static final String LOGLEVEL_WARN_LABEL = "Warn"; public static final String LOGLEVEL_INFO_LABEL = "Info"; public static final String LOGLEVEL_DEBUG_LABEL = "Debug"; private static final int LOG_LEVELS[] = { LOGLEVEL_ERROR, LOGLEVEL_WARN, LOGLEVEL_INFO, LOGLEVEL_DEBUG }; private static final Integer[] LOG_LEVELS_INTEGER; static final String PROP_LEVEL = ProductPlugin.TYPE_LOG_TRACK + ".level"; static final String PROP_ENABLE = ProductPlugin.TYPE_LOG_TRACK + ".enable"; /** * @deprecated * @see PROP_INCLUDE */ private static final String PROP_PATTERN = ProductPlugin.TYPE_LOG_TRACK + ".pattern"; static final String PROP_INCLUDE = ProductPlugin.TYPE_LOG_TRACK + ".include"; static final String PROP_EXCLUDE = ProductPlugin.TYPE_LOG_TRACK + ".exclude"; private static final String[] ENABLE_PROPS = createTypeLabels(PROP_ENABLE); /** * @deprecated * @see INCLUDE_PROPS */ private static final String[] PATTERN_PROPS = createTypeLabels(PROP_PATTERN); private static final String[] INCLUDE_PROPS = createTypeLabels(PROP_INCLUDE); private static final String[] EXCLUDE_PROPS = createTypeLabels(PROP_EXCLUDE); private static final String[] LEVEL_PROPS = createTypeLabels(PROP_LEVEL); static { Integer[] levels = new Integer[LOG_LEVELS.length]; for (int i=0; i<levels.length; i++) { levels[i] = new Integer(LOG_LEVELS[i]); } LOG_LEVELS_INTEGER = levels; } private LogTrackPluginManager manager; private int logLevel; private StringMatcher matcher = null; private Map logLevelMap = null; private LogMessageFolder folder; public static int[] getLogLevels() { return LOG_LEVELS; } static String[] LOGLEVEL_LABELS = { LOGLEVEL_ERROR_LABEL, LOGLEVEL_WARN_LABEL, LOGLEVEL_INFO_LABEL, LOGLEVEL_DEBUG_LABEL }; public static String getLogLevelLabel(int level) { switch (level) { case LOGLEVEL_ERROR: return LOGLEVEL_ERROR_LABEL; case LOGLEVEL_WARN: return LOGLEVEL_WARN_LABEL; case LOGLEVEL_INFO: return LOGLEVEL_INFO_LABEL; case LOGLEVEL_DEBUG: return LOGLEVEL_DEBUG_LABEL; default: throw new IllegalArgumentException("Invalid log level: "+level); } } protected int getLogLevel(String label) { for (int x=0;x<LOGLEVEL_LABELS.length;x++) { if (LOGLEVEL_LABELS[x].equals(label)) { return LOG_LEVELS[x]; } } throw new IllegalArgumentException("Invalid log level label: " + label); } public String[] getLogLevelAliases() { return LOGLEVEL_LABELS; } protected Map getLogLevelMap() { return this.logLevelMap; } private static Map createLogLevelMap(String[] mapping) { Map map = new HashMap(); int len = LOGLEVEL_LABELS.length; if (mapping.length != len) { String msg = "mapping.length != " + len; throw new IllegalArgumentException(msg); } for (int i=0; i<len; i++) { Integer level = LOG_LEVELS_INTEGER[i]; StringTokenizer tok = new StringTokenizer(mapping[i], ","); while (tok.hasMoreTokens()) { map.put(tok.nextToken(), level); } } return map; } public static boolean isEnabled(ConfigResponse config, int type) { String option = config.getValue(ENABLE_PROPS[type]); if (option == null) { return false; } return option.equals("true"); } public static void setEnabled(ConfigResponse config, int type, int level) { if (level == -1) { return; } config.setValue(ENABLE_PROPS[type], "true"); config.setValue(LEVEL_PROPS[type], getLogLevelLabel(level)); } protected boolean supportsLogLevels() { return true; } protected boolean shouldDebugLog() { return true; } protected boolean shouldLog(int level) { if (supportsLogLevels()) { return level <= getLogLevel(); } else { if (debugLogging) { debugLog("Log levels not supported"); } return true; } } protected int getLogLevel() { return this.logLevel; } protected void setLogLevel(int level) { this.logLevel = level; } protected boolean messageMatches(TrackEvent event) { String message = event.getMessage(); if (this.matcher == null) { if (debugLogging) { debugLog(message, "No pattern match configured"); } return !this.folder.shouldFold(event, ".*"); } message = stripNewLines(message); boolean matches = this.matcher.matches(message); if (debugLogging) { String debugMsg; if (matches) { debugMsg = "Matches"; } else { debugMsg = "Does not match"; } debugLog(message, debugMsg + " '" + this.matcher + "'"); } if (matches) { List lastMatches = this.matcher.getLastMatches(); return !this.folder.shouldFold(event, lastMatches); } else { return false; } } protected TrackEvent newTrackEvent(long time, String level, String source, String message) { if (supportsLogLevels()) { Integer intLevel = (Integer)getLogLevelMap().get(level); if (intLevel == null) { if (debugLogging) { debugLog(message, "no level mapped to '" + level + "'"); } return null; } return newTrackEvent(time, intLevel.intValue(), source, message); } return newTrackEvent(time, 0, source, message); } void debugLog(String debugMsg) { debugLog(null, debugMsg); } void debugLog(String message, String debugMsg) { if (!debugLogging) { return; } if (!shouldDebugLog()) { return; } if (message != null) { message = " for message='" + message + "',"; } else { message = ""; } log.debug(debugMsg + message + " plugin=" + getName() + " [" + getTypeInfo().getName() + "]"); } protected String stripNewLines(String message) { message = StringUtil.replace(message, "\r", ""); message = StringUtil.replace(message, "\n", ""); return message; } protected TrackEvent newTrackEvent(long time, int level, String source, String message) { if (!shouldLog(level)) { if (debugLogging) { debugLog(message, "Ignoring, level " + getLogLevelLabel(level) + " > " + getLogLevelLabel(getLogLevel())); } return null; } else if (debugLogging) { debugLog(message, "Accepting, level " + getLogLevelLabel(level) + " <= " + getLogLevelLabel(getLogLevel())); } TrackEvent event = new TrackEvent(getName(), time, level, source, message); if (!messageMatches(event)) { return null; } if (debugLogging) { debugLog(message, "Reporting Event"); } return event; } public void reportEvent(long time, int level, String source, String message) { TrackEvent event = newTrackEvent(time, level, source, message); if (event != null) { getManager().reportEvent(event); } } public void configure(ConfigResponse config) throws PluginException { super.configure(config); if (supportsLogLevels()) { int type = getTypeInfo().getType(); String level = config.getValue(LEVEL_PROPS[type]); if (level == null) { setLogLevel(LOGLEVEL_ERROR); } else { setLogLevel(getLogLevel(level)); } if (debugLogging) { debugLog("Configured log level=" + getLogLevelLabel(getLogLevel())); } String[] logLevels = getLogLevelAliases(); this.logLevelMap = (Map)logLevelCache.get(logLevels); if (this.logLevelMap == null) { log.debug("Creating log level map for: " + getTypeInfo().getName()); this.logLevelMap = createLogLevelMap(logLevels); logLevelCache.put(logLevels, this.logLevelMap); } } int type = getTypeInfo().getType(); String includes = config.getValue(INCLUDE_PROPS[type]); if ("".equals(includes)) { includes = null; } if (includes == null) { //XXX for back-compat includes = config.getValue(PATTERN_PROPS[type]); if ("".equals(includes)) { includes = null; } } String excludes = config.getValue(EXCLUDE_PROPS[type]); if ("".equals(excludes)) { excludes = null; } if ((includes != null) || (excludes != null)) { this.matcher = new StringMatcher(); try { this.matcher.setIncludes(includes); } catch (Exception e) { String msg = "Invalid " + INCLUDE_PROPS[type] + ": " + e; throw new PluginException(msg); } try { this.matcher.setExcludes(excludes); } catch (Exception e) { String msg = "Invalid " + EXCLUDE_PROPS[type] + ": " + e; throw new PluginException(msg); } } else { // [HHQ-5053] force to null in case user cleared config field this.matcher = null; } if (debugLogging) { if (this.matcher == null) { debugLog("No pattern match configured"); } else { debugLog("Pattern match configured to '" + this.matcher + "'"); } } this.folder = new LogMessageFolder(this); if (this.matcher == null) { if (this.folder.getRepeatMax() == LogMessageFolder.DEFAULT_REPEAT_MAX) { this.folder.setRepeatMax(100); } } } protected boolean supportsPatternMatching() { return true; } protected ConfigOption getIncludeOption(TypeInfo info, ConfigResponse config) { if (supportsPatternMatching()) { String defaultValue = config.getValue(PATTERN_PROPS[info.getType()]); //XXX back-compat if (defaultValue == null) { defaultValue = getTypeProperty("DEFAULT_LOG_INCLUDE"); } if (defaultValue == null) { defaultValue = ""; } ConfigOption option = new StringConfigOption(INCLUDE_PROPS[info.getType()], "Log Pattern Match", defaultValue); option.setOptional(true); return option; } else { return null; } } protected ConfigOption getExcludeOption(TypeInfo info, ConfigResponse config) { if (supportsPatternMatching()) { String defaultValue = getTypeProperty("DEFAULT_LOG_EXCLUDE"); if (defaultValue == null) { defaultValue = ""; } ConfigOption option = new StringConfigOption(EXCLUDE_PROPS[info.getType()], "Log Pattern Exclude", defaultValue); option.setOptional(true); return option; } else { return null; } } protected ConfigOption getEnableOption(TypeInfo info, ConfigResponse config) { String defaultValue = getTypeProperty("DEFAULT_LOG_TRACK_ENABLE"); boolean enable = "true".equals(defaultValue); ConfigOption option = new BooleanConfigOption(ENABLE_PROPS[info.getType()], "Enable Log Tracking", enable); option.setOptional(true); return option; } public ConfigSchema getConfigSchema(TypeInfo info, ConfigResponse config) { int type = info.getType(); ConfigSchema schema = new ConfigSchema(); ConfigOption enableOption = getEnableOption(info, config); if (enableOption != null) { schema.addOption(enableOption); } if (supportsLogLevels()) { String defaultLevel = getTypeProperty("DEFAULT_LOG_LEVEL"); if (defaultLevel == null) { defaultLevel = LOGLEVEL_ERROR_LABEL; } EnumerationConfigOption option = new EnumerationConfigOption(LEVEL_PROPS[type], "Track event log level", defaultLevel, LOGLEVEL_LABELS); option.setOptional(true); schema.addOption(option); } ConfigOption includeOption = getIncludeOption(info, config); if (includeOption != null) { schema.addOption(includeOption); } ConfigOption excludesOption = getExcludeOption(info, config); if (excludesOption != null) { schema.addOption(excludesOption); } return schema; } public void init(PluginManager manager) throws PluginException { super.init(manager); this.manager = (LogTrackPluginManager)manager; } public LogTrackPluginManager getManager() { return this.manager; } }