Integrated micrometer metering #774 (#2133)

Signed-off-by: Robert Bach <openhab@mortalsilence.net>
This commit is contained in:
pravussum 2021-02-11 21:12:58 +01:00 committed by GitHub
parent 5a4e04cb2c
commit f061512dd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 833 additions and 1 deletions

View File

@ -1024,7 +1024,6 @@
<version>0.9.12</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,9 @@
Bundle-SymbolicName: ${project.artifactId}
Import-Package: \
org.eclipse.jdt.annotation.*;resolution:=optional,\
org.openhab.*;version=!,\
org.osgi.framework,\
org.osgi.service.*,\
org.slf4j.*,\
com.google.gson.*;version="[2.8,3)"
Export-Package: io.micrometer.core.*

View File

@ -20,6 +20,44 @@
<artifactId>org.openhab.core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.core.bundles</groupId>
<artifactId>org.openhab.core.automation</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>1.6.3</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<id>embed-dependencies</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeScope>runtime</includeScope>
<includeTypes>jar</includeTypes>
<excludeGroupIds>org.apache.karaf.features,org.openhab.core.bundles</excludeGroupIds>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
<excludeTransitive>true</excludeTransitive>
<type>jar</type>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,28 @@
/**
* 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.core.io.monitor;
import org.eclipse.jdt.annotation.NonNullByDefault;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
/**
* The {@link MeterRegistryProvider} interface provides a means to retrieve the default OH meter registry instance
*
* @author Robert Bach - Initial contribution
*/
@NonNullByDefault
public interface MeterRegistryProvider {
CompositeMeterRegistry getOHMeterRegistry();
}

View File

@ -0,0 +1,114 @@
/**
* 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.core.io.monitor.internal;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.io.monitor.MeterRegistryProvider;
import org.openhab.core.io.monitor.internal.metrics.BundleStateMetric;
import org.openhab.core.io.monitor.internal.metrics.EventCountMetric;
import org.openhab.core.io.monitor.internal.metrics.JVMMetric;
import org.openhab.core.io.monitor.internal.metrics.OpenhabCoreMeterBinder;
import org.openhab.core.io.monitor.internal.metrics.RuleMetric;
import org.openhab.core.io.monitor.internal.metrics.ThingStateMetric;
import org.openhab.core.io.monitor.internal.metrics.ThreadPoolMetric;
import org.openhab.core.service.ReadyMarker;
import org.openhab.core.service.ReadyMarkerFilter;
import org.openhab.core.service.ReadyService;
import org.openhab.core.service.StartLevelService;
import org.openhab.core.thing.ThingRegistry;
import org.osgi.framework.BundleContext;
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.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
/**
* The {@link DefaultMetricsRegistration} class registers all openHAB internal metrics with the global MeterRegistry.
*
* @author Robert Bach - Initial contribution
*/
@Component(immediate = true, service = MeterRegistryProvider.class)
@NonNullByDefault
public class DefaultMetricsRegistration implements ReadyService.ReadyTracker, MeterRegistryProvider {
private final Logger logger = LoggerFactory.getLogger(DefaultMetricsRegistration.class);
public static final Tag OH_CORE_METRIC_TAG = Tag.of("openhab_core_metric", "true");
private final BundleContext bundleContext;
private final Set<OpenhabCoreMeterBinder> meters = new HashSet<>();
private final CompositeMeterRegistry registry = Metrics.globalRegistry;
private final ReadyService readyService;
private final ThingRegistry thingRegistry;
@Activate
public DefaultMetricsRegistration(BundleContext bundleContext, final @Reference ReadyService readyService,
final @Reference ThingRegistry thingRegistry) {
this.bundleContext = bundleContext;
this.readyService = readyService;
this.thingRegistry = thingRegistry;
}
@Activate
protected void activate() {
logger.trace("Activating DefaultMetricsRegistration...");
readyService.registerTracker(this, new ReadyMarkerFilter().withType(StartLevelService.STARTLEVEL_MARKER_TYPE)
.withIdentifier(Integer.toString(StartLevelService.STARTLEVEL_RULES)));
}
@Deactivate
public void deactivate() {
unregisterMeters();
readyService.unregisterTracker(this);
}
private void registerMeters() {
logger.debug("Registering meters...");
Set<Tag> tags = Set.of(OH_CORE_METRIC_TAG);
meters.add(new JVMMetric(tags));
meters.add(new ThreadPoolMetric(tags));
meters.add(new BundleStateMetric(bundleContext, tags));
meters.add(new ThingStateMetric(bundleContext, thingRegistry, tags));
meters.add(new EventCountMetric(bundleContext, tags));
meters.add(new RuleMetric(bundleContext, tags));
meters.add(new ThreadPoolMetric(tags));
meters.forEach(m -> m.bindTo(registry));
}
private void unregisterMeters() {
this.meters.forEach(OpenhabCoreMeterBinder::unbind);
}
@Override
public void onReadyMarkerAdded(ReadyMarker readyMarker) {
registerMeters();
}
@Override
public void onReadyMarkerRemoved(ReadyMarker readyMarker) {
unregisterMeters();
}
@Override
public CompositeMeterRegistry getOHMeterRegistry() {
return registry;
}
}

View File

@ -17,6 +17,8 @@ import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventFilter;
import org.openhab.core.events.EventSubscriber;
@ -37,6 +39,7 @@ import org.slf4j.LoggerFactory;
* @author Kai Kreuzer - Initial contribution
*/
@Component
@NonNullByDefault
public class EventLogger implements EventSubscriber, ReadyTracker {
private final Map<String, Logger> eventLoggers = new HashMap<>();
@ -63,6 +66,7 @@ public class EventLogger implements EventSubscriber, ReadyTracker {
}
@Override
@Nullable
public EventFilter getEventFilter() {
return null;
}

View File

@ -0,0 +1,98 @@
/**
* 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.core.io.monitor.internal.metrics;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.Nullable;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
/**
* The {@link BundleStateMetric} class implements a set of gauge metrics for the OSGI bundles states
*
* @author Robert Bach - Initial contribution
*/
public class BundleStateMetric implements OpenhabCoreMeterBinder, BundleListener {
private final Logger logger = LoggerFactory.getLogger(BundleStateMetric.class);
public static final String METRIC_NAME = "openhab.bundle.state";
private static final String BUNDLE_TAG_NAME = "bundle";
private final Meter.Id commonMeterId;
private final Map<Meter.Id, AtomicInteger> registeredMeters = new HashMap<>();
@Nullable
private MeterRegistry meterRegistry = null;
private final BundleContext bundleContext;
public BundleStateMetric(BundleContext bundleContext, Collection<Tag> tags) {
commonMeterId = new Meter.Id(METRIC_NAME, Tags.of(tags), "state", "openHAB OSGi bundles state",
Meter.Type.GAUGE);
this.bundleContext = bundleContext;
}
@Override
public void bindTo(@io.micrometer.core.lang.NonNull MeterRegistry meterRegistry) {
unbind();
logger.debug("BundleStateMetric is being bound...");
this.meterRegistry = meterRegistry;
Stream.of(bundleContext.getBundles()).forEach(bundle -> {
createOrUpdateMetricForBundleState(bundle.getSymbolicName(), bundle.getState());
});
bundleContext.addBundleListener(this);
}
@Override
public void bundleChanged(BundleEvent bundleEvent) {
if (meterRegistry == null) {
return;
}
String bundleName = bundleEvent.getBundle().getSymbolicName();
int state = bundleEvent.getBundle().getState();
createOrUpdateMetricForBundleState(bundleName, state);
}
private void createOrUpdateMetricForBundleState(String bundleName, int state) {
Meter.Id uniqueId = commonMeterId.withTag(Tag.of(BUNDLE_TAG_NAME, bundleName));
AtomicInteger bundleStateHolder = registeredMeters.get(uniqueId);
if (bundleStateHolder == null) {
bundleStateHolder = new AtomicInteger();
Gauge.builder(uniqueId.getName(), bundleStateHolder, AtomicInteger::get).baseUnit(uniqueId.getBaseUnit())
.description(uniqueId.getDescription()).tags(uniqueId.getTags()).register(meterRegistry);
registeredMeters.put(uniqueId, bundleStateHolder);
}
bundleStateHolder.set(state);
}
@Override
public void unbind() {
if (meterRegistry == null) {
return;
}
bundleContext.removeBundleListener(this);
registeredMeters.keySet().forEach(meterRegistry::remove);
registeredMeters.clear();
meterRegistry = null;
}
}

View File

@ -0,0 +1,113 @@
/**
* 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.core.io.monitor.internal.metrics;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventFilter;
import org.openhab.core.events.EventSubscriber;
import org.openhab.core.items.events.ItemCommandEvent;
import org.openhab.core.items.events.ItemStateEvent;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
/**
* The {@link EventCountMetric} class implements a gauge metric for the openHAB events count (per topic)
* topic.
*
* @author Robert Bach - Initial contribution
*/
public class EventCountMetric implements OpenhabCoreMeterBinder, EventSubscriber {
public static final String METRIC_NAME = "event_count";
private final Logger logger = LoggerFactory.getLogger(EventCountMetric.class);
private static final Tag CORE_EVENT_COUNT_METRIC_TAG = Tag.of("metric", "openhab.core.metric.eventcount");
private static final String TOPIC_TAG_NAME = "topic";
@Nullable
private MeterRegistry meterRegistry;
private final Set<Tag> tags = new HashSet<>();
private ServiceRegistration<?> eventSubscriberRegistration;
private BundleContext bundleContext;
public EventCountMetric(BundleContext bundleContext, Collection<Tag> tags) {
this.tags.addAll(tags);
this.tags.add(CORE_EVENT_COUNT_METRIC_TAG);
this.bundleContext = bundleContext;
}
@Override
public void bindTo(MeterRegistry meterRegistry) {
unbind();
logger.debug("EventCountMetric is being bound...");
this.meterRegistry = meterRegistry;
Dictionary<String, Object> properties = new Hashtable<>();
properties.put("event.topics", "openhab/*");
this.eventSubscriberRegistration = this.bundleContext.registerService(EventSubscriber.class.getName(), this,
properties);
}
@Override
public void unbind() {
if (meterRegistry == null) {
return;
}
for (Meter meter : meterRegistry.getMeters()) {
if (meter.getId().getTags().contains(CORE_EVENT_COUNT_METRIC_TAG)) {
meterRegistry.remove(meter);
}
}
meterRegistry = null;
if (this.eventSubscriberRegistration != null) {
this.eventSubscriberRegistration.unregister();
this.eventSubscriberRegistration = null;
}
}
@Override
public Set<String> getSubscribedEventTypes() {
HashSet<String> subscribedEvents = new HashSet<>();
subscribedEvents.add(ItemCommandEvent.TYPE);
subscribedEvents.add(ItemStateEvent.TYPE);
return subscribedEvents;
}
@Override
public @Nullable EventFilter getEventFilter() {
return null;
}
@Override
public void receive(Event event) {
if (meterRegistry == null) {
logger.trace("Measurement not started. Skipping event processing");
return;
}
String topic = event.getTopic();
logger.debug("Received event on topic {}.", topic);
Set<Tag> tagsWithTopic = new HashSet<>(tags);
tagsWithTopic.add(Tag.of(TOPIC_TAG_NAME, topic));
meterRegistry.counter(METRIC_NAME, tagsWithTopic).increment();
}
}

View File

@ -0,0 +1,74 @@
/**
* 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.core.io.monitor.internal.metrics;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
/**
* The {@link JVMMetric} class implements JVM related metrics like class loading, memory, GC and thread figures
*
* @author Robert Bach - Initial contribution
*/
public class JVMMetric implements OpenhabCoreMeterBinder {
private final Logger logger = LoggerFactory.getLogger(JVMMetric.class);
private static final Tag CORE_JVM_METRIC_TAG = Tag.of("metric", "openhab.core.metric.jvm");
private final Set<Tag> tags = new HashSet<>();
@Nullable
private MeterRegistry meterRegistry;
public JVMMetric(Collection<Tag> tags) {
this.tags.addAll(tags);
this.tags.add(CORE_JVM_METRIC_TAG);
}
@Override
public void bindTo(MeterRegistry registry) {
unbind();
logger.debug("JVMMetric is being bound...");
this.meterRegistry = registry;
new ClassLoaderMetrics(tags).bindTo(meterRegistry);
new JvmMemoryMetrics(tags).bindTo(meterRegistry);
new JvmGcMetrics(tags).bindTo(meterRegistry);
new ProcessorMetrics(tags).bindTo(meterRegistry);
new JvmThreadMetrics(tags).bindTo(meterRegistry);
}
@Override
public void unbind() {
if (meterRegistry == null) {
return;
}
for (Meter meter : meterRegistry.getMeters()) {
if (meter.getId().getTags().contains(CORE_JVM_METRIC_TAG)) {
meterRegistry.remove(meter);
}
}
meterRegistry = null;
}
}

View File

@ -0,0 +1,27 @@
/**
* 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.core.io.monitor.internal.metrics;
import org.eclipse.jdt.annotation.NonNullByDefault;
import io.micrometer.core.instrument.binder.MeterBinder;
/**
* The {@link OpenhabCoreMeterBinder} interface provides an abstraction of the OH default metrics
*
* @author Robert Bach - Initial contribution
*/
@NonNullByDefault
public interface OpenhabCoreMeterBinder extends MeterBinder {
void unbind();
}

View File

@ -0,0 +1,120 @@
/**
* 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.core.io.monitor.internal.metrics;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.RuleStatus;
import org.openhab.core.automation.events.RuleStatusInfoEvent;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventFilter;
import org.openhab.core.events.EventSubscriber;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
/**
* The {@link RuleMetric} class implements a gauge metric for rules RUNNING events (per rule)
*
* @author Robert Bach - Initial contribution
*/
public class RuleMetric implements OpenhabCoreMeterBinder, EventSubscriber {
public static final String METRIC_NAME = "openhab.rule.runs";
public static final String RULES_TOPIC_PREFIX = "openhab/rules/";
public static final String RULES_TOPIC_SUFFIX = "/state";
public static final String SUBSCRIPTION_PROPERTY_TOPIC = "event.topics";
public static final String RULES_TOPIC_FILTER = "openhab/rules/*";
private final Logger logger = LoggerFactory.getLogger(RuleMetric.class);
private static final Tag CORE_RULE_METRIC_TAG = Tag.of("metric", "openhab.core.metric.rules");
private static final String RULE_TAG_NAME = "rule";
private @Nullable MeterRegistry meterRegistry;
private final Set<Tag> tags = new HashSet<>();
private ServiceRegistration<?> eventSubscriberRegistration;
private BundleContext bundleContext;
public RuleMetric(BundleContext bundleContext, Collection<Tag> tags) {
this.tags.addAll(tags);
this.tags.add(CORE_RULE_METRIC_TAG);
this.bundleContext = bundleContext;
}
@Override
public void bindTo(@io.micrometer.core.lang.NonNull MeterRegistry meterRegistry) {
unbind();
logger.debug("RuleMetric is being bound...");
this.meterRegistry = meterRegistry;
Dictionary<String, Object> properties = new Hashtable<>();
properties.put(SUBSCRIPTION_PROPERTY_TOPIC, RULES_TOPIC_FILTER);
this.eventSubscriberRegistration = this.bundleContext.registerService(EventSubscriber.class.getName(), this,
properties);
}
@Override
public void unbind() {
if (meterRegistry == null) {
return;
}
for (Meter meter : meterRegistry.getMeters()) {
if (meter.getId().getTags().contains(CORE_RULE_METRIC_TAG)) {
meterRegistry.remove(meter);
}
}
meterRegistry = null;
if (this.eventSubscriberRegistration != null) {
this.eventSubscriberRegistration.unregister();
this.eventSubscriberRegistration = null;
}
}
@Override
public Set<String> getSubscribedEventTypes() {
HashSet<String> subscribedEvents = new HashSet<>();
subscribedEvents.add(RuleStatusInfoEvent.TYPE);
return subscribedEvents;
}
@Override
public @Nullable EventFilter getEventFilter() {
return null;
}
@Override
public void receive(Event event) {
if (meterRegistry == null) {
logger.trace("Measurement not started. Skipping rule event processing");
return;
}
String topic = event.getTopic();
String rule = topic.substring(RULES_TOPIC_PREFIX.length(), topic.indexOf(RULES_TOPIC_SUFFIX));
if (!event.getPayload().contains(RuleStatus.RUNNING.name())) {
logger.trace("Skipping rule status info with status other than RUNNING {}", event.getPayload());
return;
}
logger.debug("Rule {} RUNNING - updating metric.", rule);
Set<Tag> tagsWithRule = new HashSet<>(tags);
tagsWithRule.add(Tag.of(RULE_TAG_NAME, rule));
meterRegistry.counter(METRIC_NAME, tagsWithRule).increment();
}
}

View File

@ -0,0 +1,125 @@
/**
* 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.core.io.monitor.internal.metrics;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventFilter;
import org.openhab.core.events.EventSubscriber;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.events.ThingStatusInfoEvent;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
/**
* The {@link ThingStateMetric} class implements a metric for the openHAB things states.
*
* @author Robert Bach - Initial contribution
*/
public class ThingStateMetric implements OpenhabCoreMeterBinder, EventSubscriber {
private final Logger logger = LoggerFactory.getLogger(ThingStateMetric.class);
public static final String METRIC_NAME = "openhab.thing.state";
private static final String THING_TAG_NAME = "thing";
private static final String THINGSTATUS_TOPIC_PREFIX = "openhab/things/";
private final ThingRegistry thingRegistry;
private final Meter.Id commonMeterId;
private final Map<Meter.Id, AtomicInteger> registeredMeters = new HashMap<>();
private @Nullable MeterRegistry meterRegistry;
private @Nullable ServiceRegistration<?> eventSubscriberRegistration;
private final BundleContext bundleContext;
private final Gson gson = new Gson();
public ThingStateMetric(BundleContext bundleContext, ThingRegistry thingRegistry, Collection<Tag> tags) {
this.bundleContext = bundleContext;
this.thingRegistry = thingRegistry;
commonMeterId = new Meter.Id(METRIC_NAME, Tags.of(tags), "state", "openHAB thing state", Meter.Type.GAUGE);
}
@Override
public void bindTo(@io.micrometer.core.lang.NonNull MeterRegistry meterRegistry) {
unbind();
logger.debug("ThingStateMetric is being bound...");
this.meterRegistry = meterRegistry;
thingRegistry.getAll().forEach(
thing -> createOrUpdateMetricForBundleState(thing.getUID().getId(), thing.getStatus().ordinal()));
Dictionary<String, Object> properties = new Hashtable<>();
properties.put("event.topics", "openhab/things/*");
this.eventSubscriberRegistration = this.bundleContext.registerService(EventSubscriber.class.getName(), this,
properties);
}
private void createOrUpdateMetricForBundleState(String thingUid, int thingStatus) {
Meter.Id uniqueId = commonMeterId.withTag(Tag.of(THING_TAG_NAME, thingUid));
AtomicInteger thingStateHolder = registeredMeters.get(uniqueId);
if (thingStateHolder == null) {
thingStateHolder = new AtomicInteger();
Gauge.builder(uniqueId.getName(), thingStateHolder, AtomicInteger::get).baseUnit(uniqueId.getBaseUnit())
.description(uniqueId.getDescription()).tags(uniqueId.getTags()).register(meterRegistry);
registeredMeters.put(uniqueId, thingStateHolder);
}
thingStateHolder.set(thingStatus);
}
@Override
public void unbind() {
if (meterRegistry == null) {
return;
}
if (eventSubscriberRegistration != null) {
eventSubscriberRegistration.unregister();
eventSubscriberRegistration = null;
}
registeredMeters.keySet().forEach(meterRegistry::remove);
registeredMeters.clear();
meterRegistry = null;
}
@Override
public Set<String> getSubscribedEventTypes() {
return Set.of(ThingStatusInfoEvent.TYPE);
}
@Override
public @Nullable EventFilter getEventFilter() {
return null;
}
@Override
public void receive(Event event) {
logger.trace("Received ThingStatusInfo(Changed)Event...");
String thingId = event.getTopic().substring(THINGSTATUS_TOPIC_PREFIX.length(),
event.getTopic().lastIndexOf('/'));
ThingStatus status = gson.fromJson(event.getPayload(), ThingStatusInfo.class).getStatus();
createOrUpdateMetricForBundleState(thingId, status.ordinal());
}
}

View File

@ -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.core.io.monitor.internal.metrics;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.common.ThreadPoolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics;
/**
* The {@link ThreadPoolMetric} class implements a set of metrics for ThreadManager thread pool stats
*
* @author Robert Bach - Initial contribution
*/
public class ThreadPoolMetric implements OpenhabCoreMeterBinder {
private final Logger logger = LoggerFactory.getLogger(ThreadPoolMetric.class);
public static final Tag CORE_THREADPOOL_METRIC_TAG = Tag.of("metric", "openhab.core.metric.threadpools");
private static final String POOLNAME_TAG_NAME = "pool";
private final Set<Tag> tags = new HashSet<>();
@Nullable
private MeterRegistry meterRegistry;
public ThreadPoolMetric(Collection<Tag> tags) {
this.tags.addAll(tags);
this.tags.add(CORE_THREADPOOL_METRIC_TAG);
}
@Override
public void bindTo(MeterRegistry registry) {
unbind();
logger.debug("ThreadPoolMetric is being bound...");
this.meterRegistry = registry;
try {
ThreadPoolManager.getPoolNames().forEach(this::addPoolMetrics);
} catch (NoSuchMethodError nsme) {
logger.info("A newer version of openHAB is required for thread pool metrics to work.");
}
}
private void addPoolMetrics(String poolName) {
ExecutorService es = ThreadPoolManager.getPool(poolName);
if (es == null) {
return;
}
Set<Tag> tagsWithPoolname = new HashSet<>(tags);
tagsWithPoolname.add(Tag.of(POOLNAME_TAG_NAME, poolName));
new ExecutorServiceMetrics(es, poolName, tagsWithPoolname).bindTo(meterRegistry);
}
@Override
public void unbind() {
if (meterRegistry == null) {
return;
}
for (Meter meter : meterRegistry.getMeters()) {
if (meter.getId().getTags().contains(CORE_THREADPOOL_METRIC_TAG)) {
meterRegistry.remove(meter);
}
}
meterRegistry = null;
}
}