/**
* Copyright 2017 Pivotal Software, 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
*
* 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 org.springframework.metrics.instrument.spectator;
import com.netflix.spectator.api.BasicTag;
import com.netflix.spectator.api.DefaultRegistry;
import com.netflix.spectator.api.Id;
import com.netflix.spectator.api.Registry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.metrics.instrument.*;
import org.springframework.metrics.instrument.Timer;
import org.springframework.metrics.instrument.internal.AbstractMeterRegistry;
import org.springframework.metrics.instrument.internal.ImmutableTag;
import java.util.*;
import java.util.function.ToDoubleFunction;
import java.util.stream.Collectors;
import static java.util.stream.StreamSupport.stream;
public class SpectatorMeterRegistry extends AbstractMeterRegistry {
private final Registry registry;
private final Map<com.netflix.spectator.api.Meter, Meter> meterMap = new HashMap<>();
public SpectatorMeterRegistry() {
this(new DefaultRegistry());
}
public SpectatorMeterRegistry(Registry registry) {
this(registry, Clock.SYSTEM);
}
@Autowired
public SpectatorMeterRegistry(Registry registry, Clock clock) {
super(clock);
this.registry = new ExternalClockSpectatorRegistry(registry, new com.netflix.spectator.api.Clock() {
@Override
public long wallTime() {
return System.currentTimeMillis();
}
@Override
public long monotonicTime() {
return clock.monotonicTime();
}
});
}
private Iterable<com.netflix.spectator.api.Tag> toSpectatorTags(Iterable<Tag> tags) {
return stream(tags.spliterator(), false)
.map(t -> new BasicTag(t.getKey(), t.getValue()))
.collect(Collectors.toList());
}
@Override
public Collection<Meter> getMeters() {
return meterMap.values();
}
@Override
public <M extends Meter> Optional<M> findMeter(Class<M> mClass, String name, Iterable<Tag> tags) {
Collection<Tag> tagsToMatch = new ArrayList<>();
tags.forEach(tagsToMatch::add);
//noinspection unchecked
return meterMap.entrySet().stream()
.filter(e -> mClass.isInstance(e.getValue()))
.filter(e -> e.getKey().id().name().equals(name))
.filter(e -> stream(e.getKey().id().tags().spliterator(), false)
.map(t -> new ImmutableTag(t.key(), t.value()))
.collect(Collectors.toList())
.containsAll(tagsToMatch))
.map(e -> (M) e.getValue())
.findAny();
}
@Override
public Counter counter(String name, Iterable<Tag> tags) {
com.netflix.spectator.api.Counter counter = registry.counter(name, toSpectatorTags(tags));
return (Counter) meterMap.computeIfAbsent(counter, c -> new SpectatorCounter(counter));
}
@Override
public DistributionSummary distributionSummary(String name, Iterable<Tag> tags) {
com.netflix.spectator.api.DistributionSummary ds = registry.distributionSummary(name, toSpectatorTags(tags));
return (DistributionSummary) meterMap.computeIfAbsent(ds, d -> new SpectatorDistributionSummary(ds));
}
@Override
public Timer timer(String name, Iterable<Tag> tags) {
com.netflix.spectator.api.Timer timer = registry.timer(name, toSpectatorTags(tags));
return (Timer) meterMap.computeIfAbsent(timer, t -> new SpectatorTimer(timer, getClock()));
}
@Override
public LongTaskTimer longTaskTimer(String name, Iterable<Tag> tags) {
com.netflix.spectator.api.LongTaskTimer timer = registry.longTaskTimer(name, toSpectatorTags(tags));
return (LongTaskTimer) meterMap.computeIfAbsent(timer, t -> new SpectatorLongTaskTimer(timer));
}
@Override
public <T> T gauge(String name, Iterable<Tag> tags, T obj, ToDoubleFunction<T> f) {
Id gaugeId = registry.createId(name, toSpectatorTags(tags));
com.netflix.spectator.api.Gauge gauge = new SpectatorToDoubleGauge<>(registry.clock(), gaugeId, obj, f);
registry.register(gauge);
meterMap.computeIfAbsent(gauge, g -> new SpectatorGauge(gauge));
return obj;
}
}