
it.jnrpe.plugin.tomcat.CheckTomcat Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright (c) 2007, 2014 Massimiliano Ziccardi
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package it.jnrpe.plugin.tomcat;
import it.jnrpe.ICommandLine;
import it.jnrpe.JNRPELogger;
import it.jnrpe.ReturnValue;
import it.jnrpe.Status;
import it.jnrpe.plugins.Metric;
import it.jnrpe.plugins.MetricBuilder;
import it.jnrpe.plugins.MetricGatheringException;
import it.jnrpe.plugins.PluginBase;
import it.jnrpe.plugins.annotations.Option;
import it.jnrpe.plugins.annotations.Plugin;
import it.jnrpe.plugins.annotations.PluginOptions;
import it.jnrpe.utils.BadThresholdException;
import it.jnrpe.utils.thresholds.Prefixes;
import it.jnrpe.utils.thresholds.ReturnValueBuilder;
import it.jnrpe.utils.thresholds.ThresholdsEvaluatorBuilder;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Plugin(name = "CHECK_TOMCAT", description = "Checks the tomcat /manager/status page for excessive memory usage or an excessive number of threads in use.\n"
+ "This plugin does a HTTP GET of the tomcat status page:\n"
+ "\n"
+ " /manager/status?XML=true\n"
+ "\n"
+ " It checks the resulting XML for:\n"
+ "\n"
+ " * Low free memory\n"
+ " * Excessive thread usage\n"
+ "\n"
+ " In order to use this, you must provide a username and password that has a 'manager-gui' role in the\n"
+ " CATALINA_HOME/conf/tomcat-users.xml file.\n")
@PluginOptions({
@Option(shortName = "h", longName = "hostname", description = "Hostname or IP address", required = true, hasArgs = true, argName = "hostname", optionalArgs = false, option = "hostname"),
@Option(shortName = "p", longName = "port", description = "Port number; default is 8080", required = false, hasArgs = true, argName = "port", optionalArgs = false, option = "port"),
@Option(shortName = "l", longName = "username", description = "Username for authentication", required = true, hasArgs = true, argName = "username", optionalArgs = false, option = "username"),
@Option(shortName = "a", longName = "password", description = "Password for authentication", required = true, hasArgs = true, argName = "password", optionalArgs = false, option = "password"),
@Option(shortName = "m", longName = "memory", description = "Check memory", required = false, hasArgs = false, argName = "memory", optionalArgs = false, option = "memory"),
@Option(shortName = "t", longName = "threads", description = "Check threads", required = false, hasArgs = false, argName = "threads", optionalArgs = false, option = "threads"),
@Option(shortName = "P", longName = "percent", description = "Check the usage as percen", required = false, hasArgs = false, argName = "percent", optionalArgs = false, option = "percent"),
@Option(shortName = "w", longName = "warning", description = "Warning threshold value for threads or memory (in MB). Must be used with either the 'memory' or 'threads' option.", required = false, hasArgs = true, argName = "warning", optionalArgs = false, option = "warning"),
@Option(shortName = "c", longName = "critical", description = "Critical threshold value for threads or memory (in MB). Must be used with either the 'memory' or 'threads' option.", required = false, hasArgs = true, argName = "critical", optionalArgs = false, option = "critical"),
@Option(shortName = "r", longName = "th", description = "Configure a threshold. Format : metric={metric},ok={range},warn={range},crit={range},unit={unit},prefix={SI prefix}", required = false, hasArgs = true, argName = "thresholds", optionalArgs = false, option = "thresholds"),
@Option(shortName = "S", longName = "ssl", description = "Use ssl", required = false, hasArgs = false, argName = "ssl", optionalArgs = false, option = "ssl"),
@Option(shortName = "T", longName = "timeout", description = "Connection timeout in seconds. Default is 10.", required = false, hasArgs = true, argName = "timeout", optionalArgs = false, option = "timeout"),
})
public class CheckTomcat extends PluginBase {
/**
* The dataprovider object. Used to retrieve the information from the
* Tomcat application server.
*/
private IAppServerDataProvider dataProvider = new TomcatDataProvider();
/**
* The logger object.
*/
protected final JNRPELogger LOG = new JNRPELogger(this);
/**
* Default Tomcat http port.
*/
private static final String DEFAULT_PORT = "8080";
/**
* Default Tomcat manager URL.
*/
private static final String DEFAULT_URI = "/manager/status?XML=true";
/**
* Default timeout.
*/
private static final String DEFAULT_TIMEOUT = "10";
@Override
protected String getPluginName() {
return "CHECK_TOMCAT";
}
/**
* Computes the percent value of the passed in value
.
*
* @param value the value to be converted to a percent value.
* @param maxValue the maximum value that value
can
* assume
*
* @return the value as percent
*/
private BigDecimal toPercent(BigDecimal value, BigDecimal maxValue) {
return value.divide(maxValue, 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100));
}
/**
* Computes the percent value of the passed in value
.
*
* @param value the value to be converted to a percent value.
* @param maxValue the maximum value that value
can
* assume
*
* @return the value as percent
*/
private BigDecimal toPercent(long value, long maxValue) {
return toPercent(new BigDecimal(value), new BigDecimal(maxValue));
}
/**
* Gathers the metrics about the Tomcat JVM memory usage
*
* @return the gathered metrics
* @throws MetricGatheringException
*/
private Collection gatherMemoryMetric() throws MetricGatheringException {
List res = new ArrayList();
MemoryData jvmMemory = dataProvider.getJVMMemoryUsage();
final BigDecimal freeMemory = Prefixes.RAW.convert(jvmMemory.getFreeMemory(), Prefixes.mega);
BigDecimal percent = toPercent(jvmMemory.getFreeMemory(), jvmMemory.getTotalMemory());
res.add(MetricBuilder.forMetric("memory")
.withMessage("Memory : {0,number,#.##}mb", freeMemory.doubleValue())
.withValue(jvmMemory.getFreeMemory(), "#.##")
.withMinValue(0, "#.##")
.withMaxValue(jvmMemory.getTotalMemory(), "#.##").build());
LOG.debug(getContext(), "Created metric : memory");
res.add(MetricBuilder.forMetric("memory%")
.withMessage("Used Memory : {0,number,#.##}%", percent.doubleValue())
.withValue(percent, "#.##")
.withMinValue(0, "#")
.withMaxValue(100, "#").build());
LOG.debug(getContext(), "Created metric : memory%");
for (MemoryPoolData mpd : dataProvider.getMemoryPoolData()) {
res.add(MetricBuilder
.forMetric(mpd.getPoolName() + "-memoryPool")
.withMessage("Memory Pool - usage init: {0}, usage committed: {1}, usage max: {2}, usage used: {3}",
mpd.getUsageInit(),
mpd.getUsageCommitted(),
mpd.getUsageMax(),
mpd.getUsageUsed())
.withValue(mpd.getUsageUsed(), "#.##")
.withMinValue(0, "#.##")
.withMaxValue(mpd.getUsageMax(), "#.##").build());
LOG.debug(getContext(), "Created metric : " + mpd.getPoolName() + "-memoryPool");
BigDecimal memoryPoolPercent = toPercent(mpd.getUsageUsed(), mpd.getUsageMax());
res.add(MetricBuilder
.forMetric(mpd.getPoolName() + "-memoryPool%")
.withMessage("Memory Pool - usage {0,number,#.##}%", memoryPoolPercent)
.withValue(memoryPoolPercent, "#.##")
.withMinValue(0, "#")
.withMaxValue(mpd.getUsageMax(), "#").build());
LOG.debug(getContext(), "Created metric : " + mpd.getPoolName() + "-memoryPool%");;
}
return res;
}
/**
* Gathers the metrics about the thread usage of each connector.
* The metric name will have the following format: [connector-name]-threadInfo.
* This way user will be able to configure checks against a single connector.
*
* @return the metrics
* @throws MetricGatheringException
*/
private Collection gatherConnectorMetrics() throws MetricGatheringException {
List res = new ArrayList();
for (ThreadData td : dataProvider.getThreadData()) {
final String metricName = td.getConnectorName().replace("\"", "") + "-threadInfo";
final String percentMetricName = td.getConnectorName().replace("\"", "") + "-threadInfo%";
res.add(MetricBuilder
.forMetric(metricName)
.withMessage("{0} - thread count: {1}, current thread busy: {2}, max thread: {3}", metricName, td.getCurrentThreadCount(),
td.getCurrentThreadBusy(), td.getMaxThreadCount())
.withValue(td.getCurrentThreadBusy()).withMinValue(0)
.withMaxValue(td.getMaxThreadCount()).build());
final BigDecimal threadUsagePercent = toPercent(td.getCurrentThreadBusy(), td.getMaxThreadCount());
res.add(MetricBuilder
.forMetric(percentMetricName)
.withMessage("{0} - thread usage: {1,number,#.##}%", metricName, threadUsagePercent)
.withValue(threadUsagePercent, "#.##")
.withMinValue(0, "#")
.withMaxValue(td.getMaxThreadCount(), "#")
.build());
LOG.debug(getContext(), "Created metric : " + metricName);
}
return res;
}
@Override
protected Collection gatherMetrics(ICommandLine cl) throws MetricGatheringException {
Collection metrics = new ArrayList();
metrics.addAll(gatherConnectorMetrics());
metrics.addAll(gatherMemoryMetric());
return metrics;
}
/**
* Internally used to initialize the Tomcat connector.
*
* @param cl the received command line
* @throws MetricGatheringException
*/
private void init(ICommandLine cl) throws MetricGatheringException {
String username = cl.getOptionValue("username");
String password = cl.getOptionValue("password");
int timeout = Integer.parseInt(cl.getOptionValue("timeout", DEFAULT_TIMEOUT));
String hostname = cl.getOptionValue("hostname");
String port = cl.getOptionValue("port", DEFAULT_PORT);
String uri = cl.getOptionValue("uri", DEFAULT_URI);
try {
this.dataProvider.init(hostname, uri, Integer.parseInt(port), username, password, cl.hasOption("ssl"), timeout);
} catch (Exception e) {
throw new MetricGatheringException(e.getMessage(), Status.UNKNOWN, e);
}
}
/**
* Return true
if the received metricName is about a memory
* metric.
*
* @param metricName the metric to be checked
*
* @return true
if the received metricName is about a memory
* metric
*/
private boolean isMemory(String metricName) {
String tmp = metricName.toLowerCase();
return tmp.equals("memory") || tmp.equals("memory%");
}
/**
* Return true
if the received metricName is about a threads
* metric.
*
* @param metricName the metric to be checked
*
* @return true
if the received metricName is about a threads
* metric
*/
private boolean isThread(String metricName) {
String tmp = metricName.toLowerCase();
return tmp.endsWith("-threadInfo") || tmp.endsWith("-threadInfo%");
}
/**
* Return true
if the received metricName is about a percent
* metric.
*
* @param metricName the metric to be checked
*
* @return true
if the received metricName is about a percent
* metric
*/
private boolean isPercent(String metricName) {
return metricName.endsWith("%");
}
/**
* Returns the prefix to be used to create the metrics.
*
* @param cl the received command line
*
* @return the prefix to be used. Memory metrics are produced as
* {@link Prefixes#mega}, while percent metrics are {@link Prefixes#RAW}.
*/
private Prefixes getPrefix(ICommandLine cl) {
if (cl.hasOption("memory") && !cl.hasOption("percent")) {
return Prefixes.mega;
}
return Prefixes.RAW;
}
/**
* Update the return value of this plugin.
*
* @param oldRet
* @param ret
* @return
*/
private ReturnValue updateRet(ReturnValue oldRet, ReturnValue ret) {
if (oldRet == null) {
return ret;
}
if (ret.getStatus().getSeverity() > oldRet.getStatus().getSeverity()) {
return ret;
}
return oldRet;
}
@Override
public ReturnValue execute(ICommandLine cl) throws BadThresholdException {
try{
init(cl);
// If we are not using the new threshold format, we have to manually handle the
// range checks.
if (!cl.hasOption("th")) {
String criticalThreshold = cl.getOptionValue("critical");
String warningThreshold = cl.getOptionValue("warning");
Collection metrics;
if (cl.hasOption("memory")) {
metrics = gatherMemoryMetric();
} else if (cl.hasOption("threads")) {
metrics = gatherConnectorMetrics();
} else {
return ReturnValueBuilder
.forPlugin(getPluginName())
.withStatus(Status.OK)
.create();
}
ReturnValue ret = null;
for (Metric metric : metrics) {
if (cl.hasOption("memory") && !isMemory(metric.getMetricName())) {
continue;
}
if (cl.hasOption("thread") && !isThread(metric.getMetricName())) {
continue;
}
if (cl.hasOption("percent") && !isPercent(metric.getMetricName())) {
continue;
}
ret = updateRet(ret, ReturnValueBuilder.forPlugin(
getPluginName(),
new ThresholdsEvaluatorBuilder()
.withLegacyThreshold(metric.getMetricName(), null, warningThreshold, criticalThreshold, getPrefix(cl))
.create())
.withValue(metric)
.create());
}
if (ret != null) {
return ret;
}
return ReturnValueBuilder.forPlugin(this.getPluginName())
.withForcedMessage("No metrics gathered")
.withStatus(Status.UNKNOWN).create();
} else {
// For the new threshold format, we don't have anything to do
return super.execute(cl);
}
} catch (MetricGatheringException mge) {
LOG.info(getContext(), "Plugin execution failed : " + mge.getMessage(), mge);
return ReturnValueBuilder.forPlugin(getPluginName()).withForcedMessage(mge.getMessage()).withStatus(mge.getStatus()).create();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy