/*
 * 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:
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.deny(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 false; } 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); } }; } }