
com.hazelcast.internal.metrics.impl.MetricsRegistryImpl Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.internal.metrics.impl;
import com.hazelcast.internal.metrics.DoubleGauge;
import com.hazelcast.internal.metrics.DoubleProbeFunction;
import com.hazelcast.internal.metrics.DynamicMetricsProvider;
import com.hazelcast.internal.metrics.LongProbeFunction;
import com.hazelcast.internal.metrics.MetricDescriptor;
import com.hazelcast.internal.metrics.MetricsRegistry;
import com.hazelcast.internal.metrics.ProbeFunction;
import com.hazelcast.internal.metrics.ProbeLevel;
import com.hazelcast.internal.metrics.ProbeUnit;
import com.hazelcast.internal.metrics.StaticMetricsProvider;
import com.hazelcast.internal.metrics.collectors.MetricsCollector;
import com.hazelcast.internal.util.ConcurrentReferenceHashMap;
import com.hazelcast.internal.util.concurrent.ThreadFactoryImpl;
import com.hazelcast.internal.util.executor.LoggingScheduledExecutor;
import com.hazelcast.logging.ILogger;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import static com.hazelcast.internal.metrics.impl.MetricsUtil.extractExcludedTargets;
import static com.hazelcast.internal.util.ConcurrentReferenceHashMap.Option.IDENTITY_COMPARISONS;
import static com.hazelcast.internal.util.ConcurrentReferenceHashMap.ReferenceType.STRONG;
import static com.hazelcast.internal.util.ConcurrentReferenceHashMap.ReferenceType.WEAK;
import static com.hazelcast.internal.util.Preconditions.checkNotNull;
import static com.hazelcast.internal.util.ThreadUtil.createThreadPoolName;
import static java.lang.Boolean.TRUE;
import static java.lang.String.format;
import static java.util.Collections.unmodifiableSet;
import static java.util.EnumSet.of;
/**
* The {@link MetricsRegistry} implementation.
*/
public class MetricsRegistryImpl implements MetricsRegistry {
final ILogger logger;
private final ProbeLevel minimumLevel;
private final ScheduledExecutorService scheduler;
private final ConcurrentMap probeInstances = new ConcurrentHashMap<>();
// use ConcurrentReferenceHashMap to allow unreferenced Class instances to be garbage collected
private final ConcurrentMap, SourceMetadata> metadataMap
= new ConcurrentReferenceHashMap<>();
private final ConcurrentMap gauges
= new ConcurrentReferenceHashMap<>(STRONG, WEAK);
private final ConcurrentMap metricSourceMap
= new ConcurrentReferenceHashMap<>(STRONG, STRONG, of(IDENTITY_COMPARISONS));
private final DefaultMetricDescriptorSupplier staticDescriptorSupplier = new DefaultMetricDescriptorSupplier();
private final AtomicReference metricDescriptorReusableData = new AtomicReference<>(null);
/**
* Creates a MetricsRegistryImpl instance.
*
* @param logger the ILogger used
* @param minimumLevel the minimum ProbeLevel. If a probe is registered with a ProbeLevel lower than the minimum ProbeLevel,
* then the registration is skipped.
* @throws NullPointerException if logger or minimumLevel is null
*/
public MetricsRegistryImpl(ILogger logger, ProbeLevel minimumLevel) {
this("default", logger, minimumLevel);
}
/**
* Creates a MetricsRegistryImpl instance.
*
* @param name Name of the registry
* @param logger the ILogger used
* @param minimumLevel the minimum ProbeLevel. If a
* probe is registered with a ProbeLevel lower than the
* minimum ProbeLevel, then the registration is skipped.
* @throws NullPointerException if logger or minimumLevel is null
*/
public MetricsRegistryImpl(String name, ILogger logger, ProbeLevel minimumLevel) {
this.logger = checkNotNull(logger, "logger can't be null");
this.minimumLevel = checkNotNull(minimumLevel, "minimumLevel can't be null");
this.scheduler = new LoggingScheduledExecutor(logger, 2,
new ThreadFactoryImpl(createThreadPoolName(name, "MetricsRegistry")));
if (logger.isFinestEnabled()) {
logger.finest("MetricsRegistry minimumLevel:" + minimumLevel);
}
}
@Override
public ProbeLevel minimumLevel() {
return minimumLevel;
}
@Override
public Set getNames() {
return unmodifiableSet(probeInstances.values().stream()
.map(probeInstance -> probeInstance.descriptor.metricString())
.collect(Collectors.toSet()));
}
/**
* Loads the {@link SourceMetadata}.
*
* @param clazz the Class to be analyzed.
* @return the loaded SourceMetadata.
*/
SourceMetadata loadSourceMetadata(Class> clazz) {
SourceMetadata metadata = metadataMap.get(clazz);
if (metadata == null) {
metadata = new SourceMetadata(clazz);
SourceMetadata found = metadataMap.putIfAbsent(clazz, metadata);
metadata = found == null ? metadata : found;
}
return metadata;
}
@Override
public void registerStaticMetrics(S source, String namePrefix) {
checkNotNull(source, "source can't be null");
checkNotNull(namePrefix, "namePrefix can't be null");
registerStaticMetrics(newMetricDescriptor().withPrefix(namePrefix), source);
}
@Override
public void registerStaticMetrics(MetricDescriptor descriptor, S source) {
checkNotNull(descriptor, "descriptor can't be null");
checkNotNull(source, "source can't be null");
SourceMetadata metadata = loadSourceMetadata(source.getClass());
for (FieldProbe field : metadata.fields()) {
field.register(this, descriptor, source);
}
for (MethodProbe method : metadata.methods()) {
method.register(this, descriptor, source);
}
}
@Override
public void registerDynamicMetricsProvider(DynamicMetricsProvider metricsProvider) {
metricSourceMap.put(metricsProvider, TRUE);
}
@Override
public void deregisterDynamicMetricsProvider(DynamicMetricsProvider metricsProvider) {
metricSourceMap.remove(metricsProvider);
}
@Override
public void registerStaticProbe(S source, MetricDescriptor descriptor, String name, ProbeLevel level,
ProbeUnit unit, ProbeFunction function) {
registerStaticProbeWithUnit(source, descriptor, name, level, unit, function);
}
@Override
public void registerStaticProbe(S source, String name, ProbeLevel level, LongProbeFunction function) {
registerStaticProbeWithoutUnit(source, name, level, function);
}
@Override
public void registerStaticProbe(S source, String name, ProbeLevel level, ProbeUnit unit, LongProbeFunction function) {
registerStaticProbeWithUnit(source, staticDescriptorSupplier.get(), name, level, unit, function);
}
@Override
public void registerStaticProbe(S source, MetricDescriptor descriptor, String name, ProbeLevel level,
ProbeUnit unit, LongProbeFunction function) {
registerStaticProbeWithUnit(source, descriptor, name, level, unit, function);
}
@Override
public void registerStaticProbe(S source, String name, ProbeLevel level, DoubleProbeFunction function) {
registerStaticProbeWithoutUnit(source, name, level, function);
}
@Override
public void registerStaticProbe(S source, String name, ProbeLevel level, ProbeUnit unit,
DoubleProbeFunction function) {
registerStaticProbeWithUnit(source, staticDescriptorSupplier.get(), name, level, unit, function);
}
@Override
public void registerStaticProbe(S source, MetricDescriptor descriptor, String name, ProbeLevel level, ProbeUnit unit,
DoubleProbeFunction function) {
registerStaticProbeWithUnit(source, descriptor, name, level, unit, function);
}
private void registerStaticProbeWithoutUnit(S source, String name, ProbeLevel level, ProbeFunction function) {
checkNotNull(source, "source can't be null");
checkNotNull(name, "name can't be null");
checkNotNull(function, "function can't be null");
checkNotNull(level, "level can't be null");
registerInternal(source, createDescriptor(name), level, function);
}
private void registerStaticProbeWithUnit(S source, MetricDescriptor descriptor, String name, ProbeLevel level,
ProbeUnit unit,
ProbeFunction function) {
registerInternal(source, descriptor.copy().withUnit(unit).withMetric(name), level, function);
}
ProbeInstance getProbeInstance(String name) {
checkNotNull(name, "name can't be null");
return probeInstances.get(createDescriptor(name).lookupView());
}
void registerInternal(S source, MetricDescriptor descriptor, ProbeLevel probeLevel, ProbeFunction function) {
if (!probeLevel.isEnabled(minimumLevel)) {
return;
}
descriptor.withExcludedTargets(extractExcludedTargets(function, minimumLevel));
MetricDescriptorImpl.LookupView descriptorLookupView = ((MetricDescriptorImpl) descriptor).lookupView();
ProbeInstance probeInstance = probeInstances
.computeIfAbsent(descriptorLookupView, k -> new ProbeInstance<>(descriptor, source, function));
if (probeInstance.source == source && probeInstance.function == function) {
if (logger.isFinestEnabled()) {
logger.finest("Registered probeInstance " + descriptor.metricString());
}
} else {
logOverwrite(probeInstance);
probeInstance.source = source;
probeInstance.function = function;
}
AbstractGauge gauge = gauges.get(descriptorLookupView);
if (gauge != null) {
gauge.onProbeInstanceSet(probeInstance);
}
}
private void logOverwrite(ProbeInstance probeInstance) {
if (probeInstance.function != null || probeInstance.source != null) {
logger.warning(format("Overwriting existing probe '%s'", probeInstance.descriptor));
}
}
@Override
public LongGaugeImpl newLongGauge(String name) {
checkNotNull(name, "name can't be null");
LongGaugeImpl gauge = new LongGaugeImpl(this, name);
gauges.put(createDescriptor(name).lookupView(), gauge);
return gauge;
}
@Override
public DoubleGauge newDoubleGauge(String name) {
checkNotNull(name, "name can't be null");
DoubleGaugeImpl gauge = new DoubleGaugeImpl(this, name);
gauges.put(createDescriptor(name).lookupView(), gauge);
return gauge;
}
private MetricDescriptorImpl createDescriptor(String name) {
MetricDescriptorImpl descriptor = new MetricDescriptorImpl(staticDescriptorSupplier);
int dotIdx = name.lastIndexOf('.');
if (dotIdx < 0) {
// simple metric name
descriptor.withMetric(name);
return descriptor;
}
descriptor.withMetric(name.substring(dotIdx + 1));
int bracketOpenIdx = name.indexOf('[');
if (bracketOpenIdx > 0) {
int bracketCloseIdx = name.indexOf(']');
String prefix = name.substring(0, bracketOpenIdx);
String discriminator = name.substring(bracketOpenIdx + 1, bracketCloseIdx);
descriptor.withPrefix(prefix)
.withDiscriminator("ignored", discriminator);
} else {
descriptor.withPrefix(name.substring(0, dotIdx));
}
return descriptor;
}
@Override
public void collect(MetricsCollector collector) {
checkNotNull(collector, "collector can't be null");
MetricsCollectionCycle collectionCycle = new MetricsCollectionCycle(this::loadSourceMetadata,
this::lookupMetricValueCatcher, collector, minimumLevel, metricDescriptorReusableData.getAndSet(null));
collectionCycle.collectStaticMetrics(probeInstances);
collectionCycle.collectDynamicMetrics(metricSourceMap.keySet());
collectionCycle.notifyAllGauges(gauges.values());
MetricDescriptorReusableData reusableData = collectionCycle.cleanUp();
boolean set = metricDescriptorReusableData.compareAndSet(null, reusableData);
if (!set) {
reusableData.destroy();
}
}
@Override
public void collectDynamicMetrics(MetricsCollector collector, Set metricsProviders) {
checkNotNull(collector, "collector can't be null");
MetricsCollectionCycle collectionCycle = new MetricsCollectionCycle(
this::loadSourceMetadata,
this::lookupMetricValueCatcher,
collector,
minimumLevel,
null
);
collectionCycle.collectDynamicMetrics(metricsProviders);
}
private MetricValueCatcher lookupMetricValueCatcher(MetricDescriptor descriptor) {
AbstractGauge gauge = gauges.get(((MetricDescriptorImpl) descriptor).lookupView());
return gauge != null ? gauge.getCatcherOrNull() : null;
}
@Override
public void provideMetrics(Object... providers) {
for (Object provider : providers) {
if (provider instanceof StaticMetricsProvider metricsProvider) {
metricsProvider.provideStaticMetrics(this);
}
}
}
@Override
public ScheduledFuture> scheduleAtFixedRate(Runnable publisher, long period, TimeUnit timeUnit, ProbeLevel probeLevel) {
if (!probeLevel.isEnabled(minimumLevel)) {
return null;
}
return scheduler.scheduleAtFixedRate(publisher, 0, period, timeUnit);
}
public void shutdown() {
// we want to immediately terminate; we don't want to wait till pending tasks have completed.
scheduler.shutdownNow();
}
@Override
public MetricDescriptorImpl newMetricDescriptor() {
return staticDescriptorSupplier.get();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy