diff --git a/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/healthcheck/HealthCheckCounter.java b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/healthcheck/HealthCheckCounter.java new file mode 100644 index 00000000000..97d3e02f561 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/healthcheck/HealthCheckCounter.java @@ -0,0 +1,96 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2022] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package fish.payara.microprofile.metrics.healthcheck; + +import fish.payara.nucleus.healthcheck.HealthCheckStatsProvider; +import org.eclipse.microprofile.metrics.Counter; + +/** + * Implementation of a counter based off an HealthCheck. As this is just a proxy + * for the HealthCheck calling the {@link #inc() } method will throw an + * {@link UnsupportedOperationException}. Just use the {@link #getCount()} + * method to get the value of the HealthCheck backing this. + */ +public class HealthCheckCounter implements Counter, HealthCheckStatsProvider { + + private final HealthCheckStatsProvider healthCheck; + + private final ServiceExpression expression; + + public HealthCheckCounter(HealthCheckStatsProvider healthCheck, ServiceExpression expression) { + this.healthCheck = healthCheck; + this.expression = expression; + } + + /** + * Throws {@link UnsupportedOperationException} - this is all dealt with by + * the backing HealthCheck + */ + @Override + public void inc() { + throw new UnsupportedOperationException("Not supported - use getCount() to get value from HealthCheck directly."); + } + + /** + * Throws {@link UnsupportedOperationException} - this is all dealt with by + * the backing HealthCheck + * @param n the increment value + */ + @Override + public void inc(long n) { + throw new UnsupportedOperationException("Not supported - use getCount() to get value from HealthCheck directly."); + } + + @Override + public long getCount() { + return getValue(Long.class, expression.getAttributeName()); + } + + @Override + public Long getValue(Class type, String attributeName) { + return healthCheck.getValue(type, attributeName); + } + + @Override + public boolean isEnabled() { + return healthCheck.isEnabled(); + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/healthcheck/HealthCheckGauge.java b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/healthcheck/HealthCheckGauge.java new file mode 100644 index 00000000000..c03b1a70cfc --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/healthcheck/HealthCheckGauge.java @@ -0,0 +1,75 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2022] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package fish.payara.microprofile.metrics.healthcheck; + +import fish.payara.nucleus.healthcheck.HealthCheckStatsProvider; +import org.eclipse.microprofile.metrics.Gauge; + +/** + * Implementation of a gauge based off an HealthCheck. + * + */ +public class HealthCheckGauge implements Gauge, HealthCheckStatsProvider { + + private final HealthCheckStatsProvider healthCheck; + + private final ServiceExpression expression; + + public HealthCheckGauge(HealthCheckStatsProvider healthCheck, ServiceExpression expression) { + this.healthCheck = healthCheck; + this.expression = expression; + } + + @Override + public Number getValue() { + return getValue(Number.class, expression.getAttributeName()); + } + + @Override + public boolean isEnabled() { + return healthCheck.isEnabled(); + } + + @Override + public Number getValue(Class type, String attributeName) { + return healthCheck.getValue(type, attributeName); + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/healthcheck/ServiceExpression.java b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/healthcheck/ServiceExpression.java new file mode 100644 index 00000000000..b1153d1a349 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/healthcheck/ServiceExpression.java @@ -0,0 +1,70 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2022] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package fish.payara.microprofile.metrics.healthcheck; + +public class ServiceExpression { + + private String service; + + private String attributeName; + + private static final String ATTRIBUTE_SEPARATOR = "#"; + + public ServiceExpression(String expression) { + if (expression == null || expression.trim().isEmpty()) { + throw new IllegalArgumentException("Service Expression is null"); + } + int slashIndex = expression.lastIndexOf(ATTRIBUTE_SEPARATOR); + if (slashIndex < 0) { + throw new IllegalArgumentException("MBean Expression is invalid : " + expression); + } + service = expression.substring(0, slashIndex); + attributeName = expression.substring(slashIndex + 1); + } + + public String getServiceId() { + return service; + } + + public String getAttributeName() { + return attributeName; + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/impl/MetricsServiceImpl.java b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/impl/MetricsServiceImpl.java index 5dd128cf623..42336a031ac 100644 --- a/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/impl/MetricsServiceImpl.java +++ b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/impl/MetricsServiceImpl.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) [2018-2021] Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) [2018-2022] Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -89,12 +89,14 @@ import fish.payara.microprofile.metrics.MetricsService; import fish.payara.microprofile.metrics.admin.MetricsServiceConfiguration; import fish.payara.microprofile.metrics.exception.NoSuchRegistryException; -import fish.payara.microprofile.metrics.jmx.MBeanMetadata; -import fish.payara.microprofile.metrics.jmx.MBeanMetadataConfig; -import fish.payara.microprofile.metrics.jmx.MBeanMetadataHelper; +import fish.payara.microprofile.metrics.jmx.MetricsMetadata; +import fish.payara.microprofile.metrics.jmx.MetricsMetadataConfig; +import fish.payara.microprofile.metrics.jmx.MetricsMetadataHelper; import fish.payara.monitoring.collect.MonitoringDataCollector; import fish.payara.monitoring.collect.MonitoringDataSource; import fish.payara.nucleus.executorservice.PayaraExecutorService; +import fish.payara.nucleus.healthcheck.HealthCheckService; +import fish.payara.nucleus.healthcheck.HealthCheckStatsProvider; import java.util.logging.Level; @Service(name = "microprofile-metrics-service") @@ -104,7 +106,10 @@ public class MetricsServiceImpl implements MetricsService, ConfigListener, Monit private static final Logger LOGGER = Logger.getLogger(MetricsService.class.getName()); @Inject - MetricsServiceConfiguration configuration; + private HealthCheckService healthCheckService; + + @Inject + private MetricsServiceConfiguration configuration; @Inject private ServerEnvironment serverEnv; @@ -113,7 +118,7 @@ public class MetricsServiceImpl implements MetricsService, ConfigListener, Monit ServiceLocator serviceLocator; @Inject - private MBeanMetadataHelper helper; + private MetricsMetadataHelper helper; private MetricsServiceConfiguration metricsServiceConfiguration; @@ -121,9 +126,9 @@ public class MetricsServiceImpl implements MetricsService, ConfigListener, Monit private Boolean metricsSecure; - private List unresolvedBaseMetadataList; + private List unresolvedBaseMetadataList; - private List unresolvedVendorMetadataList; + private List unresolvedVendorMetadataList; private static final class RegisteredMetric { @@ -245,7 +250,7 @@ public void collect(MonitoringDataCollector rootCollector) { } } - private static void collectRegistry(String contextName, MetricRegistry registry, MonitoringDataCollector collector) { + private void collectRegistry(String contextName, MetricRegistry registry, MonitoringDataCollector collector) { // OBS: this way of iterating the metrics in the registry is optimal because of its internal data organisation for (String name : registry.getNames()) { @@ -254,6 +259,10 @@ private static void collectRegistry(String contextName, MetricRegistry registry, Metric metric = entry.getValue(); try { MonitoringDataCollector metricCollector = tagCollector(contextName, metricID, collector); + if (metric instanceof HealthCheckStatsProvider + && (!((HealthCheckStatsProvider) metric).isEnabled() || !healthCheckService.isEnabled())) { + continue; + } if (metric instanceof Counting) { metricCollector.collect(toName(metricID, "Count"), ((Counting) metric).getCount()); } @@ -263,7 +272,7 @@ private static void collectRegistry(String contextName, MetricRegistry registry, if (metric instanceof Timer) { metricCollector.collect(toName(metricID, "MaxDuration"), ((Timer) metric).getSnapshot().getMax()); } - if (metric instanceof Gauge) { + if (metric instanceof Gauge) { Object value = ((Gauge) metric).getValue(); if (value instanceof Number) { metricCollector.collect(toName(metricID, @@ -271,7 +280,7 @@ private static void collectRegistry(String contextName, MetricRegistry registry, } } } catch (Exception ex) { - LOGGER.log(Level.SEVERE, "Failed to retrieve metric: " + metricID);; + LOGGER.log(Level.SEVERE, "Failed to retrieve metric: {0}", metricID); } } } @@ -378,17 +387,17 @@ private static MonitoringDataCollector tagCollector(String contextName, MetricID return collector.group(tag); } - private static void checkSystemCpuLoadIssue(MBeanMetadataConfig metadataConfig) { + private static void checkSystemCpuLoadIssue(MetricsMetadataConfig metadataConfig) { // Could be constant but placed it in method as it is a workaround until fixed in JVM. // TODO Make this check dependent on the JDK version (as it hopefully will get solved in the future) -> Azul fix request made. String mbeanSystemCPULoad = "java.lang:type=OperatingSystem/SystemCpuLoad"; long count = metadataConfig.getBaseMetadata().stream() - .map(MBeanMetadata::getMBean) + .map(MetricsMetadata::getMBean) .filter(mbeanSystemCPULoad::equalsIgnoreCase) .count(); count += metadataConfig.getVendorMetadata().stream() - .map(MBeanMetadata::getMBean) + .map(MetricsMetadata::getMBean) .filter(mbeanSystemCPULoad::equalsIgnoreCase) .count(); @@ -403,7 +412,7 @@ private static void checkSystemCpuLoadIssue(MBeanMetadataConfig metadataConfig) * * @param metadataConfig */ - private void initMetadataConfig(List baseMetadataList, List vendorMetadataList, boolean isRetry) { + private void initMetadataConfig(List baseMetadataList, List vendorMetadataList, boolean isRetry) { if (!baseMetadataList.isEmpty()) { unresolvedBaseMetadataList = helper.registerMetadata( getContext(MetricsContext.SERVER_CONTEXT_NAME).getBaseRegistry(), @@ -432,15 +441,15 @@ public void refresh() { } } - private MBeanMetadataConfig getConfig() { + private MetricsMetadataConfig getConfig() { InputStream defaultConfig = MetricsService.class.getResourceAsStream("/metrics.xml"); - MBeanMetadataConfig config = JAXB.unmarshal(defaultConfig, MBeanMetadataConfig.class); + MetricsMetadataConfig config = JAXB.unmarshal(defaultConfig, MetricsMetadataConfig.class); File metricsResource = new File(serverEnv.getConfigDirPath(), "metrics.xml"); if (metricsResource.exists()) { try { InputStream userMetrics = new FileInputStream(metricsResource); - MBeanMetadataConfig extraConfig = JAXB.unmarshal(userMetrics, MBeanMetadataConfig.class); + MetricsMetadataConfig extraConfig = JAXB.unmarshal(userMetrics, MetricsMetadataConfig.class); config.addBaseMetadata(extraConfig.getBaseMetadata()); config.addVendorMetadata(extraConfig.getVendorMetadata()); @@ -456,7 +465,7 @@ public boolean isEnabled() { if (metricsEnabled == null) { metricsEnabled = Boolean.valueOf(metricsServiceConfiguration.getEnabled()); } - return metricsEnabled.booleanValue(); + return metricsEnabled; } public void resetMetricsEnabledProperty() { @@ -516,11 +525,11 @@ public String getApplicationName() { } private void bootstrap() { - MBeanMetadataConfig metadataConfig = getConfig(); + MetricsMetadataConfig metadataConfig = getConfig(); checkSystemCpuLoadIssue(metadataConfig); // PAYARA 2938 initMetadataConfig(metadataConfig.getBaseMetadata(), metadataConfig.getVendorMetadata(), false); } - + @Override public UnprocessedChangeEvents changed(PropertyChangeEvent[] events) { List unchangedList = new ArrayList<>(); diff --git a/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MBeanExpression.java b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MBeanExpression.java index b2adc684fd8..b37c2d80dad 100644 --- a/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MBeanExpression.java +++ b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MBeanExpression.java @@ -40,8 +40,8 @@ package fish.payara.microprofile.metrics.jmx; -import static fish.payara.microprofile.metrics.jmx.MBeanMetadataHelper.ATTRIBUTE_SEPARATOR; -import static fish.payara.microprofile.metrics.jmx.MBeanMetadataHelper.SUB_ATTRIBUTE_SEPARATOR; +import static fish.payara.microprofile.metrics.jmx.MetricsMetadataHelper.ATTRIBUTE_SEPARATOR; +import static fish.payara.microprofile.metrics.jmx.MetricsMetadataHelper.SUB_ATTRIBUTE_SEPARATOR; import java.lang.management.ManagementFactory; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; diff --git a/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MBeanMetadata.java b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MetricsMetadata.java similarity index 87% rename from appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MBeanMetadata.java rename to appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MetricsMetadata.java index 37597ba4897..1ca5cb9e4e0 100644 --- a/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MBeanMetadata.java +++ b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MetricsMetadata.java @@ -40,10 +40,10 @@ package fish.payara.microprofile.metrics.jmx; -import static fish.payara.microprofile.metrics.jmx.MBeanMetadataHelper.ATTRIBUTE; -import static fish.payara.microprofile.metrics.jmx.MBeanMetadataHelper.KEY; -import static fish.payara.microprofile.metrics.jmx.MBeanMetadataHelper.SPECIFIER; -import static fish.payara.microprofile.metrics.jmx.MBeanMetadataHelper.SUB_ATTRIBUTE; +import static fish.payara.microprofile.metrics.jmx.MetricsMetadataHelper.ATTRIBUTE; +import static fish.payara.microprofile.metrics.jmx.MetricsMetadataHelper.KEY; +import static fish.payara.microprofile.metrics.jmx.MetricsMetadataHelper.SPECIFIER; +import static fish.payara.microprofile.metrics.jmx.MetricsMetadataHelper.SUB_ATTRIBUTE; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -64,13 +64,16 @@ import org.eclipse.microprofile.metrics.MetricUnits; @XmlAccessorType(XmlAccessType.FIELD) -public class MBeanMetadata implements Metadata { +public class MetricsMetadata implements Metadata { - private static final Logger LOGGER = Logger.getLogger(MBeanMetadata.class.getName()); + private static final Logger LOGGER = Logger.getLogger(MetricsMetadata.class.getName()); @XmlElement(name = "mbean") private String mBean; + @XmlElement(name = "service") + private String service; + @XmlElement private boolean dynamic = true; @@ -97,15 +100,15 @@ public class MBeanMetadata implements Metadata { private List tags; - public MBeanMetadata() { + public MetricsMetadata() { tags = new ArrayList<>(); } - public MBeanMetadata(Metadata metadata) { + public MetricsMetadata(Metadata metadata) { this(null, metadata.getName(), metadata.getDisplayName(), metadata.description().orElse(null), metadata.getTypeRaw(), metadata.unit().orElse(null)); } - public MBeanMetadata(String mBean, String name, String displayName, String description, MetricType typeRaw, String unit) { + public MetricsMetadata(String mBean, String name, String displayName, String description, MetricType typeRaw, String unit) { this(); this.mBean = mBean; this.name = name; @@ -123,6 +126,14 @@ public void setMBean(String mBean) { this.mBean = mBean; } + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + public boolean isDynamic() { return dynamic; } @@ -184,14 +195,14 @@ public String getType() { private boolean validateMetadata() { boolean validationResult = true; - MBeanMetadata metadata = this; + MetricsMetadata metadata = this; if (isNull(metadata.getName())) { LOGGER.log(WARNING, "'name' property not defined in " + metadata.getMBean() + " mbean metadata", new Exception()); validationResult = false; } - if (isNull(metadata.getMBean())) { - LOGGER.log(WARNING, "'mbean' property not defined in {0} metadata", metadata.getName()); + if (isNull(metadata.getMBean()) && isNull(metadata.getService())) { + LOGGER.log(WARNING, "'mbean' or 'service' property not defined in {0} metadata", metadata.getName()); validationResult = false; } if (isNull(metadata.getType())) { @@ -275,7 +286,7 @@ public boolean equals(Object o) { @Override public String toString() { - return new StringJoiner(", ", MBeanMetadata.class.getSimpleName() + "[", "]") + return new StringJoiner(", ", MetricsMetadata.class.getSimpleName() + "[", "]") .add("mBean='" + mBean + "'") .add("dynamic=" + dynamic) .add("name='" + name + "'") diff --git a/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MBeanMetadataConfig.java b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MetricsMetadataConfig.java similarity index 83% rename from appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MBeanMetadataConfig.java rename to appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MetricsMetadataConfig.java index 39378b71978..bab66dd3df3 100644 --- a/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MBeanMetadataConfig.java +++ b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MetricsMetadataConfig.java @@ -46,39 +46,39 @@ @XmlRootElement(name = "config") @XmlAccessorType(XmlAccessType.FIELD) -public class MBeanMetadataConfig { +public class MetricsMetadataConfig { @XmlElementWrapper(name = "base") @XmlElement(name = "metadata") - private final List baseMetadata = new CopyOnWriteArrayList<>(); + private final List baseMetadata = new CopyOnWriteArrayList<>(); @XmlElementWrapper(name = "vendor") @XmlElement(name = "metadata") - private final List vendorMetadata = new CopyOnWriteArrayList<>(); + private final List vendorMetadata = new CopyOnWriteArrayList<>(); - public List getBaseMetadata() { + public List getBaseMetadata() { return baseMetadata; } - public void setBaseMetadata(List baseMetadata) { + public void setBaseMetadata(List baseMetadata) { this.baseMetadata.clear(); this.addBaseMetadata(baseMetadata); } - public void addBaseMetadata(List baseMetadata) { + public void addBaseMetadata(List baseMetadata) { this.baseMetadata.addAll(baseMetadata); } - public List getVendorMetadata() { + public List getVendorMetadata() { return vendorMetadata; } - public void setVendorMetadata(List vendorMetadata) { + public void setVendorMetadata(List vendorMetadata) { this.vendorMetadata.clear(); this.addVendorMetadata(vendorMetadata); } - public void addVendorMetadata(List vendorMetadata) { + public void addVendorMetadata(List vendorMetadata) { this.vendorMetadata.addAll(vendorMetadata); } diff --git a/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MBeanMetadataHelper.java b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MetricsMetadataHelper.java similarity index 80% rename from appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MBeanMetadataHelper.java rename to appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MetricsMetadataHelper.java index 538644e33d1..ae61f840f6a 100644 --- a/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MBeanMetadataHelper.java +++ b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/jmx/MetricsMetadataHelper.java @@ -39,6 +39,10 @@ */ package fish.payara.microprofile.metrics.jmx; +import fish.payara.microprofile.metrics.healthcheck.HealthCheckCounter; +import fish.payara.microprofile.metrics.healthcheck.HealthCheckGauge; +import fish.payara.microprofile.metrics.healthcheck.ServiceExpression; +import fish.payara.nucleus.healthcheck.HealthCheckStatsProvider; import java.util.ArrayList; import java.util.List; import static java.util.Objects.nonNull; @@ -60,10 +64,11 @@ import org.eclipse.microprofile.metrics.MetricUnits; import org.eclipse.microprofile.metrics.Tag; import org.glassfish.api.admin.ServerEnvironment; +import org.glassfish.hk2.api.ServiceLocator; import org.jvnet.hk2.annotations.Service; @Service -public class MBeanMetadataHelper { +public class MetricsMetadataHelper { public static final String SPECIFIER = "%s"; // microprofile-metrics specification defined specifier public static final String KEY = "${key}"; @@ -73,31 +78,34 @@ public class MBeanMetadataHelper { public static final String SUB_ATTRIBUTE_SEPARATOR = "#"; public static final String INSTANCE = "${instance}"; - private static final Logger LOGGER = Logger.getLogger(MBeanMetadataHelper.class.getName()); + private static final Logger LOGGER = Logger.getLogger(MetricsMetadataHelper.class.getName()); @Inject private ServerEnvironment serverEnv; + @Inject + private ServiceLocator habitat; + /** * Registers metrics as MBeans * * @param metricRegistry Registry to add metrics to - * @param metadataList List of all {@link MBeanMetadata} representing a + * @param metadataList List of all {@link MetricsMetadata} representing a * {@link Metric} * @param globalTags * @param isRetry true if this is not initial registration, this is used to * register lazy-loaded MBeans * @return the list of unresolved MBean Metadata */ - public List registerMetadata(MetricRegistry metricRegistry, - List metadataList, boolean isRetry) { + public List registerMetadata(MetricRegistry metricRegistry, + List metadataList, boolean isRetry) { if (!metricRegistry.getNames().isEmpty() && !isRetry) { metricRegistry.removeMatching(MetricFilter.ALL); } - List unresolvedMetadataList = resolveDynamicMetadata(metadataList); - for (MBeanMetadata beanMetadata : metadataList) { + List unresolvedMetadataList = resolveDynamicMetadata(metadataList); + for (MetricsMetadata beanMetadata : metadataList) { List tags = new ArrayList<>(); for (XmlTag tag : beanMetadata.getTags()) { tags.add(new Tag(tag.getName(), tag.getValue())); @@ -108,18 +116,38 @@ public List registerMetadata(MetricRegistry metricRegistry, continue; } Metric type; - MBeanExpression mBeanExpression = new MBeanExpression(beanMetadata.getMBean()); - switch (beanMetadata.getTypeRaw()) { - case COUNTER: - type = new MBeanCounterImpl(mBeanExpression); - break; - case GAUGE: - type = new MBeanGuageImpl(mBeanExpression); - break; - default: - throw new IllegalStateException("Unsupported type : " + beanMetadata); + if (beanMetadata.getMBean() != null) { + MBeanExpression mBeanExpression = new MBeanExpression(beanMetadata.getMBean()); + switch (beanMetadata.getTypeRaw()) { + case COUNTER: + type = new MBeanCounterImpl(mBeanExpression); + break; + case GAUGE: + type = new MBeanGuageImpl(mBeanExpression); + break; + default: + throw new IllegalStateException("Unsupported type : " + beanMetadata); + } + metricRegistry.register(beanMetadata, type, tags.toArray(new Tag[tags.size()])); + } else { + ServiceExpression expression = new ServiceExpression(beanMetadata.getService()); + HealthCheckStatsProvider healthCheck = habitat.getService(HealthCheckStatsProvider.class, expression.getServiceId()); + if (healthCheck != null) { + switch (beanMetadata.getTypeRaw()) { + case COUNTER: + type = new HealthCheckCounter(healthCheck, expression); + break; + case GAUGE: + type = new HealthCheckGauge(healthCheck, expression); + break; + default: + throw new IllegalStateException("Unsupported type : " + beanMetadata); + } + metricRegistry.register(beanMetadata, type, tags.toArray(new Tag[tags.size()])); + } else { + throw new IllegalStateException("Health-Check service not found : " + beanMetadata.getService()); + } } - metricRegistry.register(beanMetadata, type, tags.toArray(new Tag[tags.size()])); } catch (IllegalArgumentException ex) { LOGGER.log(WARNING, ex.getMessage(), ex); } @@ -133,20 +161,21 @@ public List registerMetadata(MetricRegistry metricRegistry, * @param metadataList list of MBean Metadata * @return the list of unresolved MBean Metadata */ - public List resolveDynamicMetadata(List metadataList) { - List unresolvedMetadataList = new ArrayList<>(); - List resolvedMetadataList = new ArrayList<>(); + public List resolveDynamicMetadata(List metadataList) { + List unresolvedMetadataList = new ArrayList<>(); + List resolvedMetadataList = new ArrayList<>(); List removedMetadataList = new ArrayList<>(metadataList.size()); - for (MBeanMetadata metadata : metadataList) { + for (MetricsMetadata metadata : metadataList) { if (!metadata.isValid()) { removedMetadataList.add(metadata); continue; } - if (metadata.getMBean().contains(SPECIFIER) + if (metadata.getMBean() != null + && (metadata.getMBean().contains(SPECIFIER) || metadata.getMBean().contains(KEY) || metadata.getMBean().contains(ATTRIBUTE) || metadata.getMBean().contains(SUB_ATTRIBUTE) - || metadata.getMBean().contains(INSTANCE)) { + || metadata.getMBean().contains(INSTANCE))) { try { String instanceName = serverEnv.getInstanceName(); // set (optional) instance the query expression @@ -200,14 +229,14 @@ public List resolveDynamicMetadata(List metadataLi return unresolvedMetadataList; } - private static List loadAttribute( + private static List loadAttribute( ObjectName objName, MBeanExpression mBeanExpression, - MBeanMetadata metadata, + MetricsMetadata metadata, String key, String instanceName) { - List metadataList = new ArrayList<>(); + List metadataList = new ArrayList<>(); String attributeName; if (ATTRIBUTE.equals(mBeanExpression.getAttributeName())) { @@ -243,15 +272,15 @@ private static List loadAttribute( return metadataList; } - private static List loadSubAttribute( + private static List loadSubAttribute( ObjectName objName, MBeanExpression mBeanExpression, - MBeanMetadata metadata, + MetricsMetadata metadata, String key, String attribute, String instanceName, boolean isDynamicAttribute) { - List metadataList = new ArrayList<>(); + List metadataList = new ArrayList<>(); String exp = objName.getCanonicalName(); String subAttribute = mBeanExpression.getSubAttributeName(); if (subAttribute != null) { @@ -276,7 +305,7 @@ private static List loadSubAttribute( newMetadataBuilder = newMetadataBuilder.withUnit((String) compositeData.get(subAttribute)); } } - MBeanMetadata newMbeanMetadata = new MBeanMetadata(newMetadataBuilder.build()); + MetricsMetadata newMbeanMetadata = new MetricsMetadata(newMetadataBuilder.build()); newMbeanMetadata.addTags(metadata.getTags()); metadataList.add(createMetadata(newMbeanMetadata, exp, key, attribute, subAttribute, instanceName)); } @@ -297,8 +326,8 @@ private static List loadSubAttribute( return metadataList; } - private static MBeanMetadata createMetadata( - MBeanMetadata metadata, + private static MetricsMetadata createMetadata( + MetricsMetadata metadata, String exp, String key, String attribute, @@ -312,7 +341,7 @@ private static MBeanMetadata createMetadata( builder.append(SUB_ATTRIBUTE_SEPARATOR); builder.append(subAttribute); } - MBeanMetadata newMetaData = new MBeanMetadata(builder.toString(), + MetricsMetadata newMetaData = new MetricsMetadata(builder.toString(), formatMetadata( metadata.getName(), key, diff --git a/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/writer/MetricsWriterImpl.java b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/writer/MetricsWriterImpl.java index 2c4d95f2a95..f5b2765e44b 100644 --- a/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/writer/MetricsWriterImpl.java +++ b/appserver/payara-appserver-modules/microprofile/metrics/src/main/java/fish/payara/microprofile/metrics/writer/MetricsWriterImpl.java @@ -58,6 +58,9 @@ import fish.payara.microprofile.metrics.exception.NoSuchMetricException; import fish.payara.microprofile.metrics.exception.NoSuchRegistryException; import fish.payara.microprofile.metrics.impl.MetricRegistryImpl; +import fish.payara.nucleus.healthcheck.HealthCheckService; +import fish.payara.nucleus.healthcheck.HealthCheckStatsProvider; +import org.glassfish.internal.api.Globals; public class MetricsWriterImpl implements MetricsWriter { @@ -65,6 +68,7 @@ public class MetricsWriterImpl implements MetricsWriter { private final Set contextNames; private final Function getContextByName; private final Tag[] globalTags; + private final HealthCheckService healthCheckService; public MetricsWriterImpl(MetricExporter exporter, Set contextNames, Function getContextByName, Tag... globalTags) { @@ -72,6 +76,7 @@ public MetricsWriterImpl(MetricExporter exporter, Set contextNames, this.contextNames = contextNames; this.getContextByName = getContextByName; this.globalTags = globalTags; + this.healthCheckService = Globals.getDefaultBaseServiceLocator().getService(HealthCheckService.class); } @Override @@ -121,6 +126,10 @@ private void writeMetricFamily(MetricExporter exporter, String contextName, Stri Metadata metadata = registry.getMetadata(metricName); for (Entry metric : registry.getMetrics(metricName).entrySet()) { MetricID metricID = metric.getKey(); + if (metric.getValue() instanceof HealthCheckStatsProvider + && (!((HealthCheckStatsProvider) metric.getValue()).isEnabled() || !healthCheckService.isEnabled())) { + continue; + } if (globalTags.length > 0) { Tag[] tagsWithoutGlobal = metricID.getTagsAsArray(); Tag[] tags = new Tag[tagsWithoutGlobal.length + globalTags.length]; diff --git a/appserver/payara-appserver-modules/microprofile/metrics/src/main/resources/metrics.xml b/appserver/payara-appserver-modules/microprofile/metrics/src/main/resources/metrics.xml index 56372e15d1e..a300afbd28c 100644 --- a/appserver/payara-appserver-modules/microprofile/metrics/src/main/resources/metrics.xml +++ b/appserver/payara-appserver-modules/microprofile/metrics/src/main/resources/metrics.xml @@ -126,7 +126,7 @@ holder. gauge none System Load Average - Displays the system load average for the last minute. The system load average is the sum of the number of runnable entities queued to the available processors and the number of runnable entities running on the available processors averaged over a period of time. The way in which the load average is calculated is operating system specific but is typically a damped time-dependent average. If the load average is not available, a negative value is displayed. This attribute is designed to provide a hint about the system load and may be queried frequently. The load average may be unavailable on some platform where it is expensive to implement this method. + Displays the system load average for the last minute. The system load average is the sum of the number of runnable entities queued to the available processors and the number of runnable entities running on the available processors averaged over a period of time. The way in which the load average is calculated is operating system specific but is typically a damped time-dependent average. If the load average is not available, a negative value is displayed. This attribute is designed to provide a hint about the system load and may be queried frequently. The load average may be unavailable on some platforms where it is expensive to implement this method. jvm.uptime @@ -201,7 +201,23 @@ holder. gauge none System Cpu Load - Display the "recent cpu usage" for the whole system. This value is a double in the [0.0,1.0] interval. A value of 0.0 means that all CPUs were idle during the recent period of time observed, while a value of 1.0 means that all CPUs were actively running 100% of the time during the recent period being observed. All values betweens 0.0 and 1.0 are possible depending of the activities going on in the system. If the system recent cpu usage is not available, the method returns a negative value. + Display the "recent cpu usage" for the whole system. This value is a double in the [0.0,1.0] interval. A value of 0.0 means that all CPUs were idle during the recent period of time observed, while a value of 1.0 means that all CPUs were actively running 100% of the time during the recent period being observed. All values between 0.0 and 1.0 are possible depending of the activities going on in the system. If the system recent cpu usage is not available, the method returns a negative value. + + + thread.stuck.count + healthcheck-stuck#count + gauge + none + Stuck Thread Count + Displays the stuck thread count which is blocked, and can't return to the threadpool for a certain amount of time. + + + thread.stuck.maxDuration + healthcheck-stuck#maxDuration + gauge + none + Stuck Thread Max Duration + Displays the maximum duration of stuck thread which is blocked, and can't return to the threadpool for a certain amount of time. diff --git a/appserver/payara-appserver-modules/microprofile/metrics/src/test/java/fish/payara/microprofile/metrics/jmx/MBeanMetadataTest.java b/appserver/payara-appserver-modules/microprofile/metrics/src/test/java/fish/payara/microprofile/metrics/jmx/MetricsMetadataTest.java similarity index 83% rename from appserver/payara-appserver-modules/microprofile/metrics/src/test/java/fish/payara/microprofile/metrics/jmx/MBeanMetadataTest.java rename to appserver/payara-appserver-modules/microprofile/metrics/src/test/java/fish/payara/microprofile/metrics/jmx/MetricsMetadataTest.java index 424c5d139ea..d2a4db72667 100644 --- a/appserver/payara-appserver-modules/microprofile/metrics/src/test/java/fish/payara/microprofile/metrics/jmx/MBeanMetadataTest.java +++ b/appserver/payara-appserver-modules/microprofile/metrics/src/test/java/fish/payara/microprofile/metrics/jmx/MetricsMetadataTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) [2021] Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) [2022] Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -49,11 +49,11 @@ import java.util.ArrayList; import java.util.List; -public class MBeanMetadataTest { +public class MetricsMetadataTest { @Test public void isValid_basic() { - MBeanMetadata metadata = new MBeanMetadata("m", "name" + MetricsMetadata metadata = new MetricsMetadata("m", "name" , "Display", "description", MetricType.COUNTER, "none"); List tags = new ArrayList<>(); tags.add(new XmlTag("test", "JUnit")); @@ -63,7 +63,7 @@ public void isValid_basic() { @Test public void isValid_EmptyTag() { - MBeanMetadata metadata = new MBeanMetadata("m", "name", "Display", "description" + MetricsMetadata metadata = new MetricsMetadata("m", "name", "Display", "description" , MetricType.COUNTER, "none"); List tags = new ArrayList<>(); tags.add(new XmlTag("test", "JUnit")); @@ -73,7 +73,7 @@ public void isValid_EmptyTag() { @Test public void isValid_PlaceHolder() { - MBeanMetadata metadata = new MBeanMetadata("amx:type=jdbc-connection-pool-mon,pp=/mon/server-mon[local-instance],name=resources/%sPool/numconnfree#current" + MetricsMetadata metadata = new MetricsMetadata("amx:type=jdbc-connection-pool-mon,pp=/mon/server-mon[local-instance],name=resources/%sPool/numconnfree#current" , "jdbc.connection.pool.%s.pool.instance.free.connections" , "Free Connections (Instance)" , "The total number of free connections in the pool as of the last sampling" @@ -85,7 +85,7 @@ public void isValid_PlaceHolder() { @Test public void isValid_PlaceHolderSomeTag() { - MBeanMetadata metadata = new MBeanMetadata("amx:type=jdbc-connection-pool-mon,pp=/mon/server-mon[local-instance],name=resources/%sPool/numconnfree#current" + MetricsMetadata metadata = new MetricsMetadata("amx:type=jdbc-connection-pool-mon,pp=/mon/server-mon[local-instance],name=resources/%sPool/numconnfree#current" , "jdbc.connection.pool.%s.pool.instance.free.connections" , "Free Connections (Instance)" , "The total number of free connections in the pool as of the last sampling" @@ -98,7 +98,7 @@ public void isValid_PlaceHolderSomeTag() { @Test public void isValid_PlaceHolderPlaceHolderTag() { - MBeanMetadata metadata = new MBeanMetadata("amx:type=jdbc-connection-pool-mon,pp=/mon/server-mon[local-instance],name=resources/%sPool/numconnfree#current" + MetricsMetadata metadata = new MetricsMetadata("amx:type=jdbc-connection-pool-mon,pp=/mon/server-mon[local-instance],name=resources/%sPool/numconnfree#current" , "jdbc.connection.pool.instance.free.connections" , "Free Connections (Instance)" , "The total number of free connections in the pool as of the last sampling" @@ -112,7 +112,7 @@ public void isValid_PlaceHolderPlaceHolderTag() { @Test public void isValid_PlaceHolderEmptyTag() { // FISH-5801 - MBeanMetadata metadata = new MBeanMetadata("amx:type=jdbc-connection-pool-mon,pp=/mon/server-mon[local-instance],name=resources/%sPool/numconnfree#current" + MetricsMetadata metadata = new MetricsMetadata("amx:type=jdbc-connection-pool-mon,pp=/mon/server-mon[local-instance],name=resources/%sPool/numconnfree#current" , "jdbc.connection.pool.%s.pool.instance.free.connections" , "Free Connections (Instance)" , "The total number of free connections in the pool as of the last sampling" diff --git a/nucleus/payara-modules/healthcheck-core/src/main/java/fish/payara/nucleus/healthcheck/HealthCheckStatsProvider.java b/nucleus/payara-modules/healthcheck-core/src/main/java/fish/payara/nucleus/healthcheck/HealthCheckStatsProvider.java new file mode 100644 index 00000000000..36cd1ebe90f --- /dev/null +++ b/nucleus/payara-modules/healthcheck-core/src/main/java/fish/payara/nucleus/healthcheck/HealthCheckStatsProvider.java @@ -0,0 +1,50 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2022] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package fish.payara.nucleus.healthcheck; + +import org.jvnet.hk2.annotations.Contract; + +@Contract +public interface HealthCheckStatsProvider { + + public T getValue(Class type, String attributeName); + + boolean isEnabled(); +} diff --git a/nucleus/payara-modules/healthcheck-stuck/src/main/java/fish/payara/nucleus/healthcheck/stuck/StuckThreadsHealthCheck.java b/nucleus/payara-modules/healthcheck-stuck/src/main/java/fish/payara/nucleus/healthcheck/stuck/StuckThreadsHealthCheck.java index da9d57123f3..28618d4b9f9 100644 --- a/nucleus/payara-modules/healthcheck-stuck/src/main/java/fish/payara/nucleus/healthcheck/stuck/StuckThreadsHealthCheck.java +++ b/nucleus/payara-modules/healthcheck-stuck/src/main/java/fish/payara/nucleus/healthcheck/stuck/StuckThreadsHealthCheck.java @@ -39,6 +39,7 @@ */ package fish.payara.nucleus.healthcheck.stuck; +import fish.payara.nucleus.healthcheck.HealthCheckStatsProvider; import fish.payara.nucleus.healthcheck.HealthCheckResult; import fish.payara.monitoring.collect.MonitoringData; import fish.payara.monitoring.collect.MonitoringDataCollector; @@ -62,6 +63,8 @@ import jakarta.annotation.PostConstruct; import jakarta.inject.Inject; +import java.util.Map; +import java.util.Set; import org.glassfish.api.StartupRunLevel; import org.glassfish.hk2.runlevel.RunLevel; import org.jvnet.hk2.annotations.Service; @@ -75,7 +78,12 @@ @RunLevel(StartupRunLevel.VAL) public class StuckThreadsHealthCheck extends BaseHealthCheck - implements MonitoringDataSource, MonitoringWatchSource { + implements MonitoringDataSource, MonitoringWatchSource, HealthCheckStatsProvider { + + private final Map stuckThreadResult = new ConcurrentHashMap<>(); + private static final String STUCK_THREAD_COUNT = "count"; + private static final String STUCK_THREAD_MAX_DURATION = "maxDuration"; + private static final Set VALID_ATTRIBUTES = Set.of(STUCK_THREAD_COUNT, STUCK_THREAD_MAX_DURATION); @FunctionalInterface private interface StuckThreadConsumer { @@ -83,16 +91,35 @@ private interface StuckThreadConsumer { } @Inject - StuckThreadsStore stuckThreadsStore; + private StuckThreadsStore stuckThreadsStore; @Inject - StuckThreadsChecker checker; + private StuckThreadsChecker checker; @PostConstruct void postConstruct() { postConstruct(this, StuckThreadsChecker.class); } + @Override + public Object getValue(Class type, String attributeName) { + if (attributeName == null) { + throw new IllegalArgumentException("attribute name is required"); + } + if (!VALID_ATTRIBUTES.contains(attributeName)) { + throw new IllegalArgumentException("Invalid attribute name, supported attributes are " + VALID_ATTRIBUTES); + } + if (!Number.class.isAssignableFrom(type)) { + throw new IllegalArgumentException("attribute type must be number"); + } + return stuckThreadResult.getOrDefault(attributeName, 0); + } + + @Override + public boolean isEnabled() { + return this.getOptions() != null ? this.getOptions().isEnabled() : false; + } + @Override protected HealthCheckResult doCheckInternal() { HealthCheckResult result = new HealthCheckResult(); @@ -126,6 +153,8 @@ public void collect(MonitoringDataCollector collector) { }); collector.collect("StuckThreadDuration", maxDuration); collector.collect("StuckThreadCount", count); + stuckThreadResult.put(STUCK_THREAD_MAX_DURATION, maxDuration.get()); + stuckThreadResult.put(STUCK_THREAD_COUNT, count.get()); } @Override