/*
* RHQ Management Platform
* Copyright (C) 2005-2014 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.core.pluginapi.event.log;
import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.configuration.PropertyList;
import org.rhq.core.domain.configuration.PropertyMap;
import org.rhq.core.domain.event.EventSeverity;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pluginapi.event.EventContext;
import org.rhq.core.pluginapi.event.EventPoller;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.system.SystemInfoFactory;
/**
* A helper class that plugins can use to start and stop {@link LogFileEventPoller}s.
*
* @since 1.3
* @author Ian Springer
*/
public class LogFileEventResourceComponentHelper {
private static final Log LOG = LogFactory.getLog(LogFileEventResourceComponentHelper.class);
public static final String LOG_ENTRY_EVENT_TYPE = "logEntry";
public static final String LOG_EVENT_SOURCES_CONFIG_PROP = "logEventSources";
public static final String LOG_EVENT_SOURCE_CONFIG_PROP = "logEventSource";
public abstract static class LogEventSourcePropertyNames {
public static final String LOG_FILE_PATH = "logFilePath"; // required
public static final String ENABLED = "enabled"; // required
public static final String DATE_FORMAT = "dateFormat"; // optional
public static final String INCLUDES_PATTERN = "includesPattern"; // optional
public static final String MINIMUM_SEVERITY = "minimumSeverity"; // optional
}
// TODO: Make this configurable via a plugin config prop.
private static final int POLLING_INTERVAL_IN_SECONDS = 60;
private ResourceContext<?> resourceContext;
private List<PropertyMap> startedEventSources = new ArrayList<PropertyMap>();
public LogFileEventResourceComponentHelper(ResourceContext<?> resourceContext) {
this.resourceContext = resourceContext;
}
public void startLogFileEventPollers() {
// Grab the list-o-maps of event sources from the plugin config.
Configuration pluginConfig = this.resourceContext.getPluginConfiguration();
PropertyList logEventSources = pluginConfig.getList(LOG_EVENT_SOURCES_CONFIG_PROP);
if (logEventSources == null) {
throw new IllegalStateException("List property [" + LOG_EVENT_SOURCES_CONFIG_PROP
+ "] not defined in plugin configuration for " + this.resourceContext.getResourceType().getName()
+ " Resource with key [" + this.resourceContext.getResourceKey() + "].");
}
// Build a new list containing the event sources that are enabled.
List<PropertyMap> enabledEventSources = new ArrayList<PropertyMap>();
for (Property prop : logEventSources.getList()) {
PropertyMap logEventSource = (PropertyMap) prop;
String enabled = logEventSource.getSimpleValue(LogEventSourcePropertyNames.ENABLED, null);
if (enabled == null) {
throw new IllegalStateException("Required property [" + LogEventSourcePropertyNames.ENABLED
+ "] is not defined in map.");
}
if (Boolean.valueOf(enabled)) {
enabledEventSources.add(logEventSource);
}
}
// Log a warning then return if SIGAR isn't available, since LogFileEventPoller depends on it. We only log this
// warning if at least one event source is enabled, since otherwise the user probably doesn't care.
boolean sigarAvailable = this.resourceContext.getSystemInformation().isNative();
if (!sigarAvailable && !enabledEventSources.isEmpty()) {
boolean nativeSystemInfoDisabled = SystemInfoFactory.isNativeSystemInfoDisabled();
ResourceType resourceType = this.resourceContext.getResourceType();
List<String> logFilePaths = getLogFilePaths(enabledEventSources);
LOG.warn("Log files " + logFilePaths + " for [" + resourceType.getPlugin() + ":"
+ resourceType.getName() + "] Resource with key [" + this.resourceContext.getResourceKey()
+ "] cannot be polled, because log file polling requires RHQ native support, which "
+ ((nativeSystemInfoDisabled) ? "has been disabled for this Agent" : "is not available on this platform") + ".");
return;
}
// Start up log file pollers for each of the enabled event sources.
for (PropertyMap logEventSource : enabledEventSources) {
String logFilePath = logEventSource.getSimpleValue(LogEventSourcePropertyNames.LOG_FILE_PATH, null);
if (logFilePath == null) {
throw new IllegalStateException("Required property [" + LogEventSourcePropertyNames.LOG_FILE_PATH
+ "] is not defined in map.");
}
File logFile = new File(logFilePath);
if (!logFile.canRead()) {
LOG.warn("LOGFILE: Logfile at location " + logFilePath + " does not exist or is not readable. "
+ "The poller will be started but no events will be polled until the file is created.");
}
Log4JLogEntryProcessor processor = new Log4JLogEntryProcessor(LOG_ENTRY_EVENT_TYPE, logFile);
String dateFormatString = logEventSource.getSimpleValue(LogEventSourcePropertyNames.DATE_FORMAT, null);
if (dateFormatString != null) {
try {
DateFormat dateFormat = new SimpleDateFormat(dateFormatString); // TODO locale specific ?
processor.setDateFormat(dateFormat);
} catch (IllegalArgumentException e) {
throw new InvalidPluginConfigurationException("Date format [" + dateFormatString
+ "] is not a valid simple date format.");
}
}
String includesPatternString = logEventSource.getSimpleValue(
LogEventSourcePropertyNames.INCLUDES_PATTERN, null);
if (includesPatternString != null) {
try {
Pattern includesPattern = Pattern.compile(includesPatternString);
processor.setIncludesPattern(includesPattern);
} catch (PatternSyntaxException e) {
throw new InvalidPluginConfigurationException("Includes pattern [" + includesPatternString
+ "] is not a valid regular expression.");
}
}
String minimumSeverityString = logEventSource.getSimpleValue(
LogEventSourcePropertyNames.MINIMUM_SEVERITY, null);
if (minimumSeverityString != null) {
EventSeverity minimumSeverity = EventSeverity.valueOf(minimumSeverityString.toUpperCase());
processor.setMinimumSeverity(minimumSeverity);
}
EventContext eventContext = this.resourceContext.getEventContext();
EventPoller poller = new LogFileEventPoller(eventContext, LOG_ENTRY_EVENT_TYPE, logFile, processor);
eventContext.registerEventPoller(poller, POLLING_INTERVAL_IN_SECONDS, logFile.getPath());
this.startedEventSources.add(logEventSource);
}
}
public void stopLogFileEventPollers() {
boolean sigarAvailable = this.resourceContext.getSystemInformation().isNative();
if (!sigarAvailable) {
return;
}
for (Iterator<PropertyMap> iterator = this.startedEventSources.iterator(); iterator.hasNext(); ) {
PropertyMap logEventSource = iterator.next();
EventContext eventContext = this.resourceContext.getEventContext();
String logFilePath = logEventSource.getSimpleValue(LogEventSourcePropertyNames.LOG_FILE_PATH, null);
eventContext.unregisterEventPoller(LOG_ENTRY_EVENT_TYPE, logFilePath);
iterator.remove();
}
}
private List<String> getLogFilePaths(List<PropertyMap> enabledEventSources) {
List<String> logFilePaths = new ArrayList<String>(enabledEventSources.size());
for (PropertyMap logEventSource : enabledEventSources) {
String logFilePath = logEventSource.getSimpleValue(LogEventSourcePropertyNames.LOG_FILE_PATH, null);
if (logFilePath == null) {
throw new IllegalStateException("Required property [" + LogEventSourcePropertyNames.LOG_FILE_PATH
+ "] is not defined in map.");
}
logFilePaths.add(logFilePath);
}
return logFilePaths;
}
}