All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micrometer.core.instrument.search.Search Maven / Gradle / Ivy

/*
 * Copyright 2017 VMware, Inc.
 *
 * 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
 *
 * https://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.micrometer.core.instrument.search;

import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.core.instrument.config.MeterFilterReply;
import io.micrometer.core.lang.Nullable;

import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

/**
 * Search terms for locating a {@link Meter} or set of meters in a given registry based on
 * any combination of their name, tags, and type.
 *
 * @author Jon Schneider
 */
public final class Search {

    private final MeterRegistry registry;

    private final List tags = new ArrayList<>();

    private Predicate nameMatches = n -> true;

    private final Set requiredTagKeys = new HashSet<>();

    private final Map>> tagMatches = new HashMap<>();

    private Search(MeterRegistry registry) {
        this.registry = registry;
    }

    /**
     * Meter contains a tag with the exact name.
     * @param exactName Name to match against.
     * @return This search.
     */
    public Search name(String exactName) {
        return name(n -> n.equals(exactName));
    }

    /**
     * Meter contains a tag matching the name predicate.
     * @param nameMatches Name matching predicate.
     * @return This search.
     */
    public Search name(@Nullable Predicate nameMatches) {
        if (nameMatches != null) {
            this.nameMatches = nameMatches;
        }
        return this;
    }

    /**
     * Meter contains tags with the matching tag keys and values.
     * @param tags The tags to match.
     * @return This search.
     */
    public Search tags(Iterable tags) {
        tags.forEach(this.tags::add);
        return this;
    }

    /**
     * Meter contains tags with the matching tag keys and values.
     * @param tags Must be an even number of arguments representing key/value pairs of
     * tags.
     * @return This search.
     */
    public Search tags(String... tags) {
        return tags(Tags.of(tags));
    }

    /**
     * Meter contains a tag with the matching key and value.
     * @param tagKey The tag key to match.
     * @param tagValue The tag value to match.
     * @return This search.
     */
    public Search tag(String tagKey, String tagValue) {
        return tags(Tags.of(tagKey, tagValue));
    }

    /**
     * Meter contains tags with the matching keys.
     * @param tagKeys The tag keys to match.
     * @return This search.
     */
    public Search tagKeys(String... tagKeys) {
        return tagKeys(Arrays.asList(tagKeys));
    }

    /**
     * Meter contains tags with the matching keys.
     * @param tagKeys The tag keys to match.
     * @return This search.
     * @since 1.7.0
     */
    public Search tagKeys(Collection tagKeys) {
        requiredTagKeys.addAll(tagKeys);
        return this;
    }

    /**
     * Meter contains a tag with the provided key and a value matching
     * {@code tagValueMatches}.
     * @param tagKey The tag key to match.
     * @param tagValueMatches The test on the tag value.
     * @return This search.
     * @since 1.6.0
     */
    public Search tag(String tagKey, Predicate tagValueMatches) {
        tagMatches.computeIfAbsent(tagKey, k -> new ArrayList<>()).add(tagValueMatches);
        return this;
    }

    /**
     * @return The first matching {@link Timer}, or {@code null} if none match.
     */
    @Nullable
    public Timer timer() {
        return findOne(Timer.class);
    }

    /**
     * @return The first matching {@link Counter}, or {@code null} if none match.
     */
    @Nullable
    public Counter counter() {
        return findOne(Counter.class);
    }

    /**
     * @return The first matching {@link Gauge}, or {@code null} if none match.
     */
    @Nullable
    public Gauge gauge() {
        return findOne(Gauge.class);
    }

    /**
     * @return The first matching {@link FunctionCounter}, or {@code null} if none match.
     */
    @Nullable
    public FunctionCounter functionCounter() {
        return findOne(FunctionCounter.class);
    }

    /**
     * @return The first matching {@link TimeGauge}, or {@code null} if none match.
     */
    @Nullable
    public TimeGauge timeGauge() {
        return findOne(TimeGauge.class);
    }

    /**
     * @return The first matching {@link FunctionTimer}, or {@code null} if none match.
     */
    @Nullable
    public FunctionTimer functionTimer() {
        return findOne(FunctionTimer.class);
    }

    /**
     * @return The first matching {@link DistributionSummary}, or {@code null} if none
     * match.
     */
    @Nullable
    public DistributionSummary summary() {
        return findOne(DistributionSummary.class);
    }

    /**
     * @return The first matching {@link LongTaskTimer}, or {@code null} if none match.
     */
    @Nullable
    public LongTaskTimer longTaskTimer() {
        return findOne(LongTaskTimer.class);
    }

    /**
     * @return The first matching {@link Meter}, or {@code null} if none match.
     */
    @Nullable
    public Meter meter() {
        return findOne(Meter.class);
    }

    @Nullable
    private  M findOne(Class clazz) {
        return meterStream().filter(clazz::isInstance).findAny().map(clazz::cast).orElse(null);
    }

    /**
     * @return All matching meters, or an empty collection if none match.
     */
    public Collection meters() {
        return meterStream().collect(toList());
    }

    /**
     * @return An accept filter that accepts any meter that matches this search.
     * @since 1.6.0
     */
    public MeterFilter acceptFilter() {
        return new MeterFilter() {
            @Override
            public MeterFilterReply accept(Meter.Id id) {
                if (!nameMatches.test(id.getName())) {
                    return MeterFilterReply.NEUTRAL;
                }
                return isTagsMatched(id) ? MeterFilterReply.ACCEPT : MeterFilterReply.NEUTRAL;
            }
        };
    }

    private boolean isTagsMatched(Meter.Id id) {
        return isRequiredTagKeysPresent(id) && isTagPredicatesMatched(id) && id.getTags().containsAll(tags);
    }

    private boolean isRequiredTagKeysPresent(Meter.Id id) {
        if (!requiredTagKeys.isEmpty()) {
            final Set tagKeys = new HashSet<>();
            id.getTags().forEach(t -> tagKeys.add(t.getKey()));
            return tagKeys.containsAll(requiredTagKeys);
        }
        return true;
    }

    private boolean isTagPredicatesMatched(Meter.Id id) {
        if (!tagMatches.isEmpty()) {
            final Set matchingTagKeys = new HashSet<>();
            id.getTags().forEach(t -> {
                Collection> tagValueMatchers = tagMatches.get(t.getKey());
                if (tagValueMatchers != null) {
                    if (tagValueMatchers.stream().allMatch(matcher -> matcher.test(t.getValue()))) {
                        matchingTagKeys.add(t.getKey());
                    }
                }
            });
            return tagMatches.keySet().size() == matchingTagKeys.size();
        }
        return true;
    }

    private Stream meterStream() {
        Stream meterStream = registry.getMeters().stream().filter(m -> nameMatches.test(m.getId().getName()));
        if (!tags.isEmpty() || !requiredTagKeys.isEmpty() || !tagMatches.isEmpty()) {
            meterStream = meterStream.filter(m -> isTagsMatched(m.getId()));
        }
        return meterStream;
    }

    /**
     * @return All matching {@link Counter} meters.
     */
    public Collection counters() {
        return findAll(Counter.class);
    }

    /**
     * @return All matching {@link Gauge} meters.
     */
    public Collection gauges() {
        return findAll(Gauge.class);
    }

    /**
     * @return All matching {@link Timer} meters.
     */
    public Collection timers() {
        return findAll(Timer.class);
    }

    /**
     * @return All matching {@link DistributionSummary} meters.
     */
    public Collection summaries() {
        return findAll(DistributionSummary.class);
    }

    /**
     * @return All matching {@link LongTaskTimer} meters.
     */
    public Collection longTaskTimers() {
        return findAll(LongTaskTimer.class);
    }

    /**
     * @return All matching {@link FunctionCounter} meters.
     */
    public Collection functionCounters() {
        return findAll(FunctionCounter.class);
    }

    /**
     * @return All matching {@link FunctionTimer} meters.
     */
    public Collection functionTimers() {
        return findAll(FunctionTimer.class);
    }

    /**
     * @return All matching {@link TimeGauge} meters.
     */
    public Collection timeGauges() {
        return findAll(TimeGauge.class);
    }

    private  Collection findAll(Class clazz) {
        return meterStream().filter(clazz::isInstance).map(clazz::cast).collect(toList());
    }

    /**
     * Initiate a new search for meters inside a registry.
     * @param registry The registry to locate meters in.
     * @return A new search.
     */
    public static Search in(MeterRegistry registry) {
        return new Search(registry);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy