/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 io.vertx.micrometer.backends;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.vertx.micrometer.Label;
import io.vertx.micrometer.Match;
import io.vertx.micrometer.MetricsDomain;
import io.vertx.micrometer.MicrometerMetricsOptions;
import io.vertx.micrometer.VertxInfluxDbOptions;
import io.vertx.micrometer.VertxPrometheusOptions;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.regex.Pattern;
BackendRegistries
is responsible for managing registries related to particular micrometer backends (influxdb, prometheus...) It contains a store of BackendRegistry
objects, each of whose encapsulating a micrometer's MeterRegistry
Author: Joel Takvorian
/**
* {@link BackendRegistries} is responsible for managing registries related to particular micrometer backends (influxdb, prometheus...)
* It contains a store of {@link BackendRegistry} objects, each of whose encapsulating a micrometer's {@link MeterRegistry}
* @author Joel Takvorian
*/
public final class BackendRegistries {
private static final Map<String, BackendRegistry> REGISTRIES = new ConcurrentHashMap<>();
private BackendRegistries() {
}
Create a new backend registry, containing a micrometer registry, initialized with the provided options.
If a registry already exists with the associated name, it is just returned without any effect.
Params: - options – micrometer options, including configuration related to the backend. Should be a subclass of
MicrometerMetricsOptions
(ex: VertxInfluxDbOptions
, VertxPrometheusOptions
). If the class is not recognized, a NoopBackendRegistry
will be returned.
Returns: the created (or existing) BackendRegistry
/**
* Create a new backend registry, containing a micrometer registry, initialized with the provided options.
* If a registry already exists with the associated name, it is just returned without any effect.
* @param options micrometer options, including configuration related to the backend.
* Should be a subclass of {@link MicrometerMetricsOptions} (ex: {@link VertxInfluxDbOptions}, {@link VertxPrometheusOptions}).
* If the class is not recognized, a {@link NoopBackendRegistry} will be returned.
* @return the created (or existing) {@link BackendRegistry}
*/
public static BackendRegistry setupBackend(MicrometerMetricsOptions options) {
return REGISTRIES.computeIfAbsent(options.getRegistryName(), k -> {
final BackendRegistry reg;
if (options.getMicrometerRegistry() != null) {
if (options.getPrometheusOptions() != null && options.getMicrometerRegistry() instanceof PrometheusMeterRegistry) {
// If a Prometheus registry is provided, extra initialization steps may have to be performed
reg = new PrometheusBackendRegistry(options.getPrometheusOptions(), (PrometheusMeterRegistry) options.getMicrometerRegistry());
} else {
// Other backend registries have no special extra steps
reg = options::getMicrometerRegistry;
}
} else if (options.getInfluxDbOptions() != null && options.getInfluxDbOptions().isEnabled()) {
reg = new InfluxDbBackendRegistry(options.getInfluxDbOptions());
} else if (options.getPrometheusOptions() != null && options.getPrometheusOptions().isEnabled()) {
reg = new PrometheusBackendRegistry(options.getPrometheusOptions());
} else if (options.getJmxMetricsOptions() != null && options.getJmxMetricsOptions().isEnabled()) {
reg = new JmxBackendRegistry(options.getJmxMetricsOptions());
} else {
// No backend setup, use global registry
reg = NoopBackendRegistry.INSTANCE;
}
registerMatchers(reg.getMeterRegistry(), options.getLabels(), options.getLabelMatches());
return reg;
});
}
Get the default micrometer registry. May return null
if it hasn't been registered yet or if it has been stopped. Returns: the micrometer registry or null
/**
* Get the default micrometer registry.
* May return {@code null} if it hasn't been registered yet or if it has been stopped.
* @return the micrometer registry or {@code null}
*/
public static MeterRegistry getDefaultNow() {
return getNow(MicrometerMetricsOptions.DEFAULT_REGISTRY_NAME);
}
Get the micrometer registry of the given name. May return null
if it hasn't been registered yet or if it has been stopped. Params: - registryName – the name associated with this registry in Micrometer options
Returns: the micrometer registry or null
/**
* Get the micrometer registry of the given name.
* May return {@code null} if it hasn't been registered yet or if it has been stopped.
* @param registryName the name associated with this registry in Micrometer options
* @return the micrometer registry or {@code null}
*/
public static MeterRegistry getNow(String registryName) {
BackendRegistry backendRegistry = REGISTRIES.get(registryName);
if (backendRegistry != null) {
return backendRegistry.getMeterRegistry();
}
return null;
}
Stop (unregister) the backend registry of the given name.
Any resource started by this backend registry will be released (like running HTTP server)
Params: - registryName – the name associated with this registry in Micrometer options
/**
* Stop (unregister) the backend registry of the given name.
* Any resource started by this backend registry will be released (like running HTTP server)
* @param registryName the name associated with this registry in Micrometer options
*/
public static void stop(String registryName) {
BackendRegistry reg = REGISTRIES.remove(registryName);
if (reg != null) {
reg.close();
}
}
public static void registerMatchers(MeterRegistry registry, Set<Label> enabledLabels, List<Match> matches) {
String[] ignored = EnumSet.complementOf(EnumSet.copyOf(enabledLabels)).stream()
.map(Label::toString)
.toArray(String[]::new);
registry.config().meterFilter(MeterFilter.ignoreTags(ignored));
matches.forEach(m -> {
switch (m.getType()) {
case EQUALS:
if (m.getAlias() == null) {
// Exact match => accept
registry.config().meterFilter(MeterFilter.accept(id -> {
if (m.getDomain() != null && !id.getName().startsWith(m.getDomain().getPrefix())) {
// If domain has been specified and we're not in that domain, ignore rule
return true;
}
String tagValue = id.getTag(m.getLabel());
return m.getValue().equals(tagValue);
}));
} else {
// Exact match => alias
registry.config().meterFilter(replaceTagValues(
m.getDomain(),
m.getLabel(),
val -> {
if (m.getValue().equals(val)) {
return m.getAlias();
}
return val;
}
));
}
break;
case REGEX:
Pattern pattern = Pattern.compile(m.getValue());
if (m.getAlias() == null) {
// Regex match => accept
registry.config().meterFilter(MeterFilter.accept(id -> {
if (m.getDomain() != null && !id.getName().startsWith(m.getDomain().getPrefix())) {
// If domain has been specified and we're not in that domain, ignore rule
return true;
}
String tagValue = id.getTag(m.getLabel());
if (tagValue == null) {
return false;
}
return pattern.matcher(tagValue).matches();
}));
} else {
// Regex match => alias
registry.config().meterFilter(replaceTagValues(
m.getDomain(),
m.getLabel(),
val -> {
if (pattern.matcher(val).matches()) {
return m.getAlias();
}
return val;
}
));
}
break;
}
});
}
private static MeterFilter replaceTagValues(MetricsDomain domain, String tagKey, Function<String, String> replacement) {
return new MeterFilter() {
@Override
public Meter.Id map(Meter.Id id) {
if (domain != null && !id.getName().startsWith(domain.getPrefix())) {
return id;
}
return MeterFilter.replaceTagValues(tagKey, replacement).map(id);
}
};
}
}