[io.metrics] initial contribution (#9890)
Signed-off-by: Robert Bach <openhab@mortalsilence.net>
This commit is contained in:
11
bundles/org.openhab.io.metrics/src/main/feature/feature.xml
Normal file
11
bundles/org.openhab.io.metrics/src/main/feature/feature.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.io.metrics-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-misc-metrics" description="Metrics Service" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<feature>openhab-core-model-item</feature>
|
||||
<feature>openhab-core-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.io.metrics/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link MetricsConfiguration} class holds the configuration for the metrics service
|
||||
*
|
||||
* @author Robert Bach - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MetricsConfiguration {
|
||||
public boolean influxMetricsEnabled = false;
|
||||
public String influxURL = "http://localhost:8086";
|
||||
public String influxDB = "openhab";
|
||||
public @Nullable String influxPassword = null;
|
||||
public @Nullable String influxUsername = null;
|
||||
public Integer influxUpdateIntervalInSeconds = 300;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MetricsConfiguration{" + "influxMetricsEnabled=" + influxMetricsEnabled + ", influxURL='" + influxURL
|
||||
+ '\'' + ", influxDB='" + influxDB + '\'' + ", influxPassword='" + influxPassword + '\''
|
||||
+ ", influxUsername='" + influxUsername + '\'' + ", influxUpdateIntervalInSeconds="
|
||||
+ influxUpdateIntervalInSeconds + '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
|
||||
|
||||
/**
|
||||
* {@link MetricsExporter} provides the interface for components exporting metrics to push based monitoring systems.
|
||||
*
|
||||
* @author Robert Bach - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class MetricsExporter {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MetricsExporter.class);
|
||||
private boolean active = false;
|
||||
protected @Nullable CompositeMeterRegistry meterRegistry = null;
|
||||
protected @Nullable MetricsConfiguration config = null;
|
||||
|
||||
protected abstract void start(CompositeMeterRegistry meterRegistry, MetricsConfiguration metricsConfiguration);
|
||||
|
||||
protected abstract void shutdown();
|
||||
|
||||
protected abstract boolean isEnabled(MetricsConfiguration config);
|
||||
|
||||
public void updateExporterState(@Nullable MetricsConfiguration config) {
|
||||
this.config = config;
|
||||
if (config != null && isEnabled(config) && meterRegistry != null) {
|
||||
if (!active) {
|
||||
logger.debug("Activating exporter {} ", this.getClass().getSimpleName());
|
||||
active = true;
|
||||
start(Objects.requireNonNull(meterRegistry), config);
|
||||
} else {
|
||||
logger.trace("Exporter {} already active.", this.getClass().getSimpleName());
|
||||
}
|
||||
} else {
|
||||
if (active) {
|
||||
logger.debug("Shutting down exporter {} ", this.getClass().getSimpleName());
|
||||
shutdown();
|
||||
active = false;
|
||||
} else {
|
||||
logger.trace("Exporter {} already shut down.", this.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setMeterRegistry(@Nullable CompositeMeterRegistry meterRegistry) {
|
||||
this.meterRegistry = meterRegistry;
|
||||
updateExporterState(config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.annotation.security.RolesAllowed;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.auth.Role;
|
||||
import org.openhab.core.io.monitor.MeterRegistryProvider;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JSONRequired;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
|
||||
import io.micrometer.prometheus.PrometheusConfig;
|
||||
import io.micrometer.prometheus.PrometheusMeterRegistry;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
/**
|
||||
* * The {@link MetricsRestController} class implements the REST endpoints for all pull based monitoring systems.
|
||||
*
|
||||
* @author Robert Bach - Initial contribution
|
||||
*/
|
||||
@Component(immediate = true, service = MetricsRestController.class)
|
||||
@JaxrsResource
|
||||
@JaxrsApplicationSelect("(" + JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + MetricsService.METRICS_APP_NAME + ")")
|
||||
@Path("")
|
||||
@JSONRequired
|
||||
@RolesAllowed({ Role.USER, Role.ADMIN })
|
||||
@Tag(name = MetricsRestController.PATH_METRICS)
|
||||
@NonNullByDefault
|
||||
public class MetricsRestController {
|
||||
private final Logger logger = LoggerFactory.getLogger(MetricsRestController.class);
|
||||
public static final String PATH_METRICS = "metrics";
|
||||
private @Nullable CompositeMeterRegistry meterRegistry = null;
|
||||
private final PrometheusMeterRegistry prometheusMeterRegistry = new PrometheusMeterRegistry(
|
||||
PrometheusConfig.DEFAULT);
|
||||
|
||||
@GET
|
||||
@Path("/prometheus")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Operation(operationId = "getPrometheusMetrics", summary = "Gets openHAB system and core metrics in a Prometheus compatible format.", responses = {
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = String.class))) })
|
||||
public String getPrometheusMetrics() {
|
||||
return prometheusMeterRegistry.scrape();
|
||||
}
|
||||
|
||||
@Reference
|
||||
public void setMeterRegistryProvider(MeterRegistryProvider meterRegistryProvider) {
|
||||
if (meterRegistry != null) {
|
||||
Objects.requireNonNull(meterRegistry).remove(prometheusMeterRegistry);
|
||||
}
|
||||
meterRegistry = meterRegistryProvider.getOHMeterRegistry();
|
||||
Objects.requireNonNull(meterRegistry).add(prometheusMeterRegistry);
|
||||
logger.debug("Core metrics registry retrieved and Prometheus registry added successfully.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import java.util.Dictionary;
|
||||
import java.util.HashSet;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.ws.rs.core.Application;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.config.core.ConfigurableService;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.io.monitor.MeterRegistryProvider;
|
||||
import org.openhab.io.metrics.exporters.InfluxMetricsExporter;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.FrameworkUtil;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
import org.osgi.service.component.annotations.Modified;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
|
||||
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
|
||||
|
||||
/**
|
||||
* The {@link MetricsService} class implements the central component controlling the different metric endpoints/syncers.
|
||||
*
|
||||
* @author Robert Bach - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(immediate = true, service = MetricsService.class)
|
||||
@ConfigurableService(category = "io", label = "Metrics service", description_uri = "io:metrics")
|
||||
public class MetricsService {
|
||||
public static final String METRICS_APP_NAME = "Metrics";
|
||||
public static final String ROOT = "/metrics";
|
||||
private final Logger logger = LoggerFactory.getLogger(MetricsService.class);
|
||||
private @Nullable ServiceRegistration<Application> restService = null;
|
||||
private @Nullable MetricsConfiguration config;
|
||||
@Reference
|
||||
protected @NonNullByDefault({}) MetricsRestController metrics;
|
||||
private Set<MetricsExporter> metricsExporters = new HashSet<>();
|
||||
private @Nullable CompositeMeterRegistry meterRegistry = null;
|
||||
|
||||
@Activate
|
||||
protected void activate(Map<@Nullable String, @Nullable Object> configuration) {
|
||||
MetricsRestApplication app = new MetricsRestApplication();
|
||||
BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext();
|
||||
restService = context.registerService(Application.class, app, getServiceProperties());
|
||||
logger.info("Metrics service available under {}.", ROOT);
|
||||
metricsExporters.add(new InfluxMetricsExporter());
|
||||
updateConfig(configuration);
|
||||
updateMeterRegistry();
|
||||
}
|
||||
|
||||
@Modified
|
||||
protected synchronized void modified(Map<@Nullable String, @Nullable Object> configuration) {
|
||||
updateConfig(configuration);
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
protected void deactivate() {
|
||||
if (restService != null) {
|
||||
Objects.requireNonNull(restService).unregister();
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<@Nullable String, @Nullable String> getServiceProperties() {
|
||||
Dictionary<@Nullable String, @Nullable String> dict = new Hashtable<>();
|
||||
dict.put(JaxrsWhiteboardConstants.JAX_RS_APPLICATION_BASE, ROOT);
|
||||
return dict;
|
||||
}
|
||||
|
||||
@JaxrsName(METRICS_APP_NAME)
|
||||
private class MetricsRestApplication extends Application {
|
||||
@NonNullByDefault({})
|
||||
@Override
|
||||
public Set<Object> getSingletons() {
|
||||
return Set.of(metrics);
|
||||
}
|
||||
}
|
||||
|
||||
@Reference
|
||||
public void setMeterRegistryProvider(MeterRegistryProvider meterRegistryProvider) {
|
||||
meterRegistry = meterRegistryProvider.getOHMeterRegistry();
|
||||
updateMeterRegistry();
|
||||
}
|
||||
|
||||
private void updateConfig(@Nullable Map<@Nullable String, @Nullable Object> configuration) {
|
||||
this.config = new Configuration(configuration).as(MetricsConfiguration.class);
|
||||
logger.debug("Configuration: {}", this.config);
|
||||
this.metricsExporters.forEach(e -> e.updateExporterState(config));
|
||||
}
|
||||
|
||||
private void updateMeterRegistry() {
|
||||
this.metricsExporters.forEach(e -> e.setMeterRegistry(meterRegistry));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* 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 java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
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.influx.InfluxConfig;
|
||||
import io.micrometer.influx.InfluxMeterRegistry;
|
||||
|
||||
/**
|
||||
* The {@link InfluxMetricsExporter} class implements a MetricsExporter for InfluxDB
|
||||
*
|
||||
* @author Robert Bach - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class InfluxMetricsExporter extends MetricsExporter {
|
||||
|
||||
private @Nullable InfluxMeterRegistry influxMeterRegistry = null;
|
||||
private @Nullable CompositeMeterRegistry meterRegistry = null;
|
||||
|
||||
@Override
|
||||
public void start(CompositeMeterRegistry meterRegistry, MetricsConfiguration metricsConfiguration) {
|
||||
influxMeterRegistry = new InfluxMeterRegistry(getInfluxConfig(metricsConfiguration), Clock.SYSTEM);
|
||||
meterRegistry.add(influxMeterRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if (influxMeterRegistry != null) {
|
||||
Objects.requireNonNull(influxMeterRegistry).stop();
|
||||
}
|
||||
if (meterRegistry != null) {
|
||||
Objects.requireNonNull(meterRegistry).remove(influxMeterRegistry);
|
||||
meterRegistry = null;
|
||||
}
|
||||
influxMeterRegistry = null;
|
||||
}
|
||||
|
||||
private InfluxConfig getInfluxConfig(MetricsConfiguration metricsConfiguration) {
|
||||
return new InfluxConfig() {
|
||||
@Override
|
||||
public Duration step() {
|
||||
return Duration.ofSeconds(metricsConfiguration.influxUpdateIntervalInSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uri() {
|
||||
return metricsConfiguration.influxURL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String db() {
|
||||
return metricsConfiguration.influxDB;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String userName() {
|
||||
return metricsConfiguration.influxUsername;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String password() {
|
||||
return metricsConfiguration.influxPassword;
|
||||
}
|
||||
|
||||
@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.influxMetricsEnabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="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">
|
||||
<config-description uri="io:metrics">
|
||||
<parameter name="influxMetricsEnabled" type="boolean">
|
||||
<label>Influx Metrics</label>
|
||||
<description>Enable the Influx (www.influxdata.com) Metrics. Further Configuration of the InfluxDB Instance
|
||||
Necessary.</description>
|
||||
<default>false</default>
|
||||
</parameter>
|
||||
<parameter name="influxURL" type="text">
|
||||
<label>InfluxDB URL</label>
|
||||
<description>The URL of the InfluxDB Instance. Defaults to http://localhost:8086</description>
|
||||
<default>http://localhost:8086</default>
|
||||
</parameter>
|
||||
<parameter name="influxDB" type="text">
|
||||
<label>InfluxDB Database Name</label>
|
||||
<description>The Name of the Database to Use. Defaults to "openhab".</description>
|
||||
<default>openhab</default>
|
||||
</parameter>
|
||||
<parameter name="influxUsername" type="text">
|
||||
<label>InfluxDB User Name</label>
|
||||
<description>The InfluxDB User Name (No Default).</description>
|
||||
</parameter>
|
||||
<parameter name="influxPassword" type="text">
|
||||
<label>InfluxDB Password</label>
|
||||
<description>The InfluxDB Password (No Default).</description>
|
||||
<context>password</context>
|
||||
</parameter>
|
||||
<parameter name="influxUpdateIntervalInSeconds" type="integer" unit="s" min="1">
|
||||
<label>InfluxDB Update Interval in Seconds</label>
|
||||
<description>Controls How Often Metrics Are Exported to InfluxDB (in Seconds). Defaults to 300</description>
|
||||
<default>300</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</config-description:config-descriptions>
|
||||
Reference in New Issue
Block a user