/* * Copyright (c) 2011-2018 Pivotal Software 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 * * 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 reactor.core.publisher; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.Timer; import org.reactivestreams.Subscription; import reactor.core.CoreSubscriber; import reactor.util.annotation.Nullable; import static reactor.core.publisher.FluxMetrics.*; /** * Activate metrics gathering on a {@link Mono}, assumes Micrometer is on the classpath. * * @implNote Metrics.isInstrumentationAvailable() test should be performed BEFORE instantiating * or referencing this class, otherwise a {@link NoClassDefFoundError} will be thrown if * Micrometer is not there. * * @author Simon Baslé * @author Stephane Maldini */ final class MonoMetrics<T> extends InternalMonoOperator<T, T> { final String name; final Tags tags; final MeterRegistry meterRegistry; MonoMetrics(Mono<? extends T> mono) { this(mono, null); } /** * For testing purposes. * * @param meterRegistry the registry to use */ MonoMetrics(Mono<? extends T> mono, @Nullable MeterRegistry meterRegistry) { super(mono); this.name = resolveName(mono); this.tags = resolveTags(mono, FluxMetrics.DEFAULT_TAGS_MONO, this.name); if (meterRegistry == null) { this.meterRegistry = Metrics.globalRegistry; } else { this.meterRegistry = meterRegistry; } } @Override public CoreSubscriber<? super T> subscribeOrReturn(CoreSubscriber<? super T> actual) { return new MetricsSubscriber<>(actual, meterRegistry, Clock.SYSTEM, this.tags); } static class MetricsSubscriber<T> implements InnerOperator<T, T> { final CoreSubscriber<? super T> actual; final Clock clock; final Tags commonTags; final MeterRegistry registry; Timer.Sample subscribeToTerminateSample; boolean done; Subscription s; MetricsSubscriber(CoreSubscriber<? super T> actual, MeterRegistry registry, Clock clock, Tags commonTags) { this.actual = actual; this.clock = clock; this.commonTags = commonTags; this.registry = registry; } @Override final public CoreSubscriber<? super T> actual() { return actual; } @Override final public void cancel() { FluxMetrics.recordCancel(commonTags, registry, subscribeToTerminateSample); s.cancel(); } @Override public void onComplete() { if (done) { return; } done = true; FluxMetrics.recordOnComplete(commonTags, registry, subscribeToTerminateSample); actual.onComplete(); } @Override final public void onError(Throwable e) { if (done) { FluxMetrics.recordMalformed(commonTags, registry); Operators.onErrorDropped(e, actual.currentContext()); return; } done = true; FluxMetrics.recordOnError(commonTags, registry, subscribeToTerminateSample, e); actual.onError(e); } @Override public void onNext(T t) { if (done) { FluxMetrics.recordMalformed(commonTags, registry); Operators.onNextDropped(t, actual.currentContext()); return; } done = true; //TODO looks like we don't count onNext: `Mono.empty()` vs `Mono.just("foo")` FluxMetrics.recordOnComplete(commonTags, registry, subscribeToTerminateSample); actual.onNext(t); actual.onComplete(); } @Override public void onSubscribe(Subscription s) { if (Operators.validate(this.s, s)) { FluxMetrics.recordOnSubscribe(commonTags, registry); this.subscribeToTerminateSample = Timer.start(clock); this.s = s; actual.onSubscribe(this); } } @Override final public void request(long l) { if (Operators.validate(l)) { s.request(l); } } } }