[metrics] Add Java Management Extensions (JMX) metrics exporter (#11249)

* Add Java Management Extensions (JMX) metrics exporter
* Use groups in metrics add-on configuration
* Improve null annotations
* Update documentation

Signed-off-by: Wouter Born <github@maindrain.net>
This commit is contained in:
Wouter Born 2021-09-16 09:11:12 +02:00 committed by GitHub
parent ba3a9bb01a
commit 30198e273a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 152 additions and 39 deletions

View File

@ -34,8 +34,9 @@ Support for push-based monitoring systems (e. g. InfluxDB) have to be enabled se
The following configuration parameters can be set: The following configuration parameters can be set:
| Config param | Description | Default value | | Config param | Description | Default value |
|--|--|--| |----------------------|-----------------------------------------------------------------------------------------------------------|---------------|
| influxMetricsEnabled | Enable the Influx (www.influxdata.com) metrics. Further configuration of the InfluxDB instance necessary. | false | | influxMetricsEnabled | Enable the Influx (www.influxdata.com) metrics. Further configuration of the InfluxDB instance necessary. | false |
| jmxMetricsEnabled | Enable the Java Management Extensions (JMX) metrics. | false |
Refer to the corresponding monitoring system sections for monitoring system specific configuration parameters. Refer to the corresponding monitoring system sections for monitoring system specific configuration parameters.
@ -65,7 +66,7 @@ Replace `openhab.local` by the openhab host.
#### Available configuration parameters #### Available configuration parameters
There are no Prometheus specific configuration paramters. There are no Prometheus specific configuration parameters.
### InfluxDB ### InfluxDB
@ -74,13 +75,21 @@ The InfluxDB exporter service will start as soon as the _influxMetricsEnabled_ c
#### Available configuration parameters #### Available configuration parameters
| Config param | Description | Default value | | Config param | Description | Default value |
|--|--|--| |-------------------------------|-----------------------------------------------------------------------------------|-----------------------|
| influxURL | The URL of the InfluxDB instance. Defaults to http://localhost:8086 | http://localhost:8086 | | influxURL | The URL of the InfluxDB instance. Defaults to http://localhost:8086 | http://localhost:8086 |
| influxDB | The name of the database to use. Defaults to "openhab". | openhab | | influxDB | The name of the database to use. Defaults to "openhab". | openhab |
| influxUsername | InfluxDB user name | n/a | | influxUsername | InfluxDB user name | n/a |
| influxPassword | The InfluxDB password (no default). | n/a | | influxPassword | The InfluxDB password (no default). | n/a |
| influxUpdateIntervalInSeconds | Controls how often metrics are exported to InfluxDB (in seconds). Defaults to 300 | 300 | | influxUpdateIntervalInSeconds | Controls how often metrics are exported to InfluxDB (in seconds). Defaults to 300 | 300 |
### JMX
The Java Management Extensions (JMX) exporter service will start as soon as the _jmxMetricsEnabled_ configuration parameter is set to true.
You can monitor the JMX metrics using a tool like [JConsole](https://docs.oracle.com/en/java/javase/11/management/using-jconsole.html) or [VisualVM](https://visualvm.github.io/) (after installing the VisualVM-MBeans plugin).
When the JMX exporter is enabled, the metrics will be available under the "metrics" MBean.
JConsole and VisualVM will only be able to connect using JMX when openHAB is started in debug mode (use `start_debug.sh` or `start_debug.bat`).
## Additional metric formats ## Additional metric formats
The metrics service was implemented using [Micrometer](https://micrometer.io), which supports a number of [monitoring systems](https://micrometer.io/docs) The metrics service was implemented using [Micrometer](https://micrometer.io), which supports a number of [monitoring systems](https://micrometer.io/docs)

View File

@ -44,6 +44,18 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-jmx</artifactId>
<version>4.0.7</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-jmx</artifactId>
<version>${micrometer.version}</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>io.micrometer</groupId> <groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId> <artifactId>micrometer-registry-prometheus</artifactId>

View File

@ -25,15 +25,17 @@ public class MetricsConfiguration {
public boolean influxMetricsEnabled = false; public boolean influxMetricsEnabled = false;
public String influxURL = "http://localhost:8086"; public String influxURL = "http://localhost:8086";
public String influxDB = "openhab"; public String influxDB = "openhab";
public @Nullable String influxPassword = null; public @Nullable String influxPassword;
public @Nullable String influxUsername = null; public @Nullable String influxUsername;
public Integer influxUpdateIntervalInSeconds = 300; public Integer influxUpdateIntervalInSeconds = 300;
public boolean jmxMetricsEnabled = false;
@Override @Override
public String toString() { public String toString() {
return "MetricsConfiguration{" + "influxMetricsEnabled=" + influxMetricsEnabled + ", influxURL='" + influxURL return "MetricsConfiguration{" + "influxMetricsEnabled=" + influxMetricsEnabled + ", influxURL='" + influxURL
+ '\'' + ", influxDB='" + influxDB + '\'' + ", influxPassword='" + influxPassword + '\'' + '\'' + ", influxDB='" + influxDB + '\'' + ", influxPassword='" + influxPassword + '\''
+ ", influxUsername='" + influxUsername + '\'' + ", influxUpdateIntervalInSeconds=" + ", influxUsername='" + influxUsername + '\'' + ", influxUpdateIntervalInSeconds="
+ influxUpdateIntervalInSeconds + '}'; + influxUpdateIntervalInSeconds + ", jmxMetricsEnabled=" + jmxMetricsEnabled + '}';
} }
} }

View File

@ -31,8 +31,8 @@ public abstract class MetricsExporter {
private final Logger logger = LoggerFactory.getLogger(MetricsExporter.class); private final Logger logger = LoggerFactory.getLogger(MetricsExporter.class);
private boolean active = false; private boolean active = false;
protected @Nullable CompositeMeterRegistry meterRegistry = null; protected @Nullable CompositeMeterRegistry meterRegistry;
protected @Nullable MetricsConfiguration config = null; protected @Nullable MetricsConfiguration config;
protected abstract void start(CompositeMeterRegistry meterRegistry, MetricsConfiguration metricsConfiguration); protected abstract void start(CompositeMeterRegistry meterRegistry, MetricsConfiguration metricsConfiguration);

View File

@ -14,7 +14,6 @@ package org.openhab.io.metrics;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import javax.annotation.security.RolesAllowed; import javax.annotation.security.RolesAllowed;
@ -31,6 +30,7 @@ import org.openhab.core.config.core.Configuration;
import org.openhab.core.io.monitor.MeterRegistryProvider; import org.openhab.core.io.monitor.MeterRegistryProvider;
import org.openhab.core.io.rest.RESTConstants; import org.openhab.core.io.rest.RESTConstants;
import org.openhab.io.metrics.exporters.InfluxMetricsExporter; import org.openhab.io.metrics.exporters.InfluxMetricsExporter;
import org.openhab.io.metrics.exporters.JmxMetricsExporter;
import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified; import org.osgi.service.component.annotations.Modified;
@ -68,7 +68,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
public class MetricsRestController { public class MetricsRestController {
private final Logger logger = LoggerFactory.getLogger(MetricsRestController.class); private final Logger logger = LoggerFactory.getLogger(MetricsRestController.class);
public static final String PATH_METRICS = "metrics"; public static final String PATH_METRICS = "metrics";
private @Nullable CompositeMeterRegistry meterRegistry = null; private @Nullable CompositeMeterRegistry meterRegistry;
private final PrometheusMeterRegistry prometheusMeterRegistry = new PrometheusMeterRegistry( private final PrometheusMeterRegistry prometheusMeterRegistry = new PrometheusMeterRegistry(
PrometheusConfig.DEFAULT); PrometheusConfig.DEFAULT);
private final Set<MetricsExporter> metricsExporters = new HashSet<>(); private final Set<MetricsExporter> metricsExporters = new HashSet<>();
@ -85,11 +85,13 @@ public class MetricsRestController {
@Reference @Reference
public void setMeterRegistryProvider(MeterRegistryProvider meterRegistryProvider) { public void setMeterRegistryProvider(MeterRegistryProvider meterRegistryProvider) {
CompositeMeterRegistry meterRegistry = this.meterRegistry;
if (meterRegistry != null) { if (meterRegistry != null) {
Objects.requireNonNull(meterRegistry).remove(prometheusMeterRegistry); meterRegistry.remove(prometheusMeterRegistry);
} }
meterRegistry = meterRegistryProvider.getOHMeterRegistry(); meterRegistry = meterRegistryProvider.getOHMeterRegistry();
Objects.requireNonNull(meterRegistry).add(prometheusMeterRegistry); meterRegistry.add(prometheusMeterRegistry);
this.meterRegistry = meterRegistry;
logger.debug("Core metrics registry retrieved and Prometheus registry added successfully."); logger.debug("Core metrics registry retrieved and Prometheus registry added successfully.");
updateMeterRegistry(); updateMeterRegistry();
} }
@ -98,6 +100,7 @@ public class MetricsRestController {
protected void activate(Map<@Nullable String, @Nullable Object> configuration) { protected void activate(Map<@Nullable String, @Nullable Object> configuration) {
logger.info("Metrics service activated, serving the following URL(s): /rest/metrics/prometheus"); logger.info("Metrics service activated, serving the following URL(s): /rest/metrics/prometheus");
metricsExporters.add(new InfluxMetricsExporter()); metricsExporters.add(new InfluxMetricsExporter());
metricsExporters.add(new JmxMetricsExporter());
updateConfig(configuration); updateConfig(configuration);
updateMeterRegistry(); updateMeterRegistry();
} }

View File

@ -13,7 +13,6 @@
package org.openhab.io.metrics.exporters; package org.openhab.io.metrics.exporters;
import java.time.Duration; import java.time.Duration;
import java.util.Objects;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
@ -26,15 +25,15 @@ import io.micrometer.influx.InfluxConfig;
import io.micrometer.influx.InfluxMeterRegistry; import io.micrometer.influx.InfluxMeterRegistry;
/** /**
* The {@link InfluxMetricsExporter} class implements a MetricsExporter for InfluxDB * The {@link InfluxMetricsExporter} class implements a MetricsExporter for InfluxDB.
* *
* @author Robert Bach - Initial contribution * @author Robert Bach - Initial contribution
*/ */
@NonNullByDefault @NonNullByDefault
public class InfluxMetricsExporter extends MetricsExporter { public class InfluxMetricsExporter extends MetricsExporter {
private @Nullable InfluxMeterRegistry influxMeterRegistry = null; private @Nullable InfluxMeterRegistry influxMeterRegistry;
private @Nullable CompositeMeterRegistry meterRegistry = null; private @Nullable CompositeMeterRegistry meterRegistry;
@Override @Override
public void start(CompositeMeterRegistry meterRegistry, MetricsConfiguration metricsConfiguration) { public void start(CompositeMeterRegistry meterRegistry, MetricsConfiguration metricsConfiguration) {
@ -44,14 +43,17 @@ public class InfluxMetricsExporter extends MetricsExporter {
@Override @Override
public void shutdown() { public void shutdown() {
InfluxMeterRegistry influxMeterRegistry = this.influxMeterRegistry;
if (influxMeterRegistry != null) { if (influxMeterRegistry != null) {
Objects.requireNonNull(influxMeterRegistry).stop(); influxMeterRegistry.stop();
this.influxMeterRegistry = null;
} }
CompositeMeterRegistry meterRegistry = this.meterRegistry;
if (meterRegistry != null) { if (meterRegistry != null) {
Objects.requireNonNull(meterRegistry).remove(influxMeterRegistry); meterRegistry.remove(influxMeterRegistry);
meterRegistry = null; this.meterRegistry = null;
} }
influxMeterRegistry = null;
} }
private InfluxConfig getInfluxConfig(MetricsConfiguration metricsConfiguration) { private InfluxConfig getInfluxConfig(MetricsConfiguration metricsConfiguration) {

View File

@ -0,0 +1,72 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.io.metrics.exporters;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.io.metrics.MetricsConfiguration;
import org.openhab.io.metrics.MetricsExporter;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import io.micrometer.jmx.JmxConfig;
import io.micrometer.jmx.JmxMeterRegistry;
/**
* The {@link JmxMetricsExporter} class implements a MetricsExporter for Java Management Extensions (JMX).
*
* @author Wouter Born - Initial contribution
*/
@NonNullByDefault
public class JmxMetricsExporter extends MetricsExporter {
private @Nullable JmxMeterRegistry jmxMeterRegistry;
private @Nullable CompositeMeterRegistry meterRegistry;
@Override
public void start(CompositeMeterRegistry meterRegistry, MetricsConfiguration metricsConfiguration) {
jmxMeterRegistry = new JmxMeterRegistry(getJmxConfig(), Clock.SYSTEM);
meterRegistry.add(jmxMeterRegistry);
}
@Override
public void shutdown() {
JmxMeterRegistry jmxMeterRegistry = this.jmxMeterRegistry;
if (jmxMeterRegistry != null) {
jmxMeterRegistry.stop();
this.jmxMeterRegistry = null;
}
CompositeMeterRegistry meterRegistry = this.meterRegistry;
if (meterRegistry != null) {
meterRegistry.remove(jmxMeterRegistry);
this.meterRegistry = null;
}
}
private JmxConfig getJmxConfig() {
return new JmxConfig() {
@Override
@io.micrometer.core.lang.Nullable
@Nullable
public String get(@Nullable String k) {
return null; // accept the rest of the defaults
}
};
}
@Override
protected boolean isEnabled(MetricsConfiguration config) {
return config.jmxMetricsEnabled;
}
}

View File

@ -5,35 +5,48 @@
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
https://openhab.org/schemas/config-description-1.0.0.xsd"> https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="io:metrics"> <config-description uri="io:metrics">
<parameter name="influxMetricsEnabled" type="boolean"> <parameter-group name="influx">
<label>Influx Metrics</label> <label>Influx Metrics</label>
</parameter-group>
<parameter-group name="jmx">
<label>JMX Metrics</label>
</parameter-group>
<parameter name="influxMetricsEnabled" type="boolean" groupName="influx">
<label>Enabled</label>
<description>Enable the Influx (www.influxdata.com) Metrics. Further Configuration of the InfluxDB Instance <description>Enable the Influx (www.influxdata.com) Metrics. Further Configuration of the InfluxDB Instance
Necessary.</description> Necessary.</description>
<default>false</default> <default>false</default>
</parameter> </parameter>
<parameter name="influxURL" type="text"> <parameter name="influxURL" type="text" groupName="influx">
<label>InfluxDB URL</label> <label>URL</label>
<description>The URL of the InfluxDB Instance. Defaults to http://localhost:8086</description> <description>The URL of the InfluxDB Instance. Defaults to http://localhost:8086</description>
<default>http://localhost:8086</default> <default>http://localhost:8086</default>
</parameter> </parameter>
<parameter name="influxDB" type="text"> <parameter name="influxDB" type="text" groupName="influx">
<label>InfluxDB Database Name</label> <label>Database Name</label>
<description>The Name of the Database to Use. Defaults to "openhab".</description> <description>The Name of the Database to Use. Defaults to "openhab".</description>
<default>openhab</default> <default>openhab</default>
</parameter> </parameter>
<parameter name="influxUsername" type="text"> <parameter name="influxUsername" type="text" groupName="influx">
<label>InfluxDB User Name</label> <label>User Name</label>
<description>The InfluxDB User Name (No Default).</description> <description>The InfluxDB User Name (No Default).</description>
</parameter> </parameter>
<parameter name="influxPassword" type="text"> <parameter name="influxPassword" type="text" groupName="influx">
<label>InfluxDB Password</label> <label>Password</label>
<description>The InfluxDB Password (No Default).</description> <description>The InfluxDB Password (No Default).</description>
<context>password</context> <context>password</context>
</parameter> </parameter>
<parameter name="influxUpdateIntervalInSeconds" type="integer" unit="s" min="1"> <parameter name="influxUpdateIntervalInSeconds" type="integer" unit="s" min="1" groupName="influx">
<label>InfluxDB Update Interval in Seconds</label> <label>Update Interval in Seconds</label>
<description>Controls How Often Metrics Are Exported to InfluxDB (in Seconds). Defaults to 300</description> <description>Controls How Often Metrics Are Exported to InfluxDB (in Seconds). Defaults to 300</description>
<default>300</default> <default>300</default>
</parameter> </parameter>
<parameter name="jmxMetricsEnabled" type="boolean" groupName="jmx">
<label>Enabled</label>
<description>Enable the Java Management Extensions (JMX) Metrics.</description>
<default>false</default>
</parameter>
</config-description> </config-description>
</config-description:config-descriptions> </config-description:config-descriptions>