From c049fac39e0fd3f8b4f6423fc412cafdf106f0d0 Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Fri, 28 Jan 2022 13:10:05 -0800 Subject: [PATCH 1/6] Observation API more stateful version --- .../io/micrometer/api/aop/TimedAspect.java | 4 +- .../api/instrument/MeterRegistry.java | 33 +-- .../api/instrument/NoopObservation.java | 128 +++++++++++ .../api/instrument/Observation.java | 95 ++++++++ .../api/instrument/ObservationHandler.java | 161 +++++++++++++ .../api/instrument/SimpleObservation.java | 208 +++++++++++++++++ .../io/micrometer/api/instrument/Timer.java | 186 ++------------- .../api/instrument/TimerRecordingHandler.java | 215 ------------------ .../instrument/internal/TimedRunnable.java | 12 +- .../context/HttpClientHandlerContext.java | 4 +- .../http/context/HttpHandlerContext.java | 6 +- .../context/HttpServerHandlerContext.java | 4 +- .../instrument/CurrentObservationTest.java | 106 +++++++++ .../api/instrument/CurrentSampleTest.java | 107 --------- .../binder/db/JooqExecuteListener.java | 4 +- .../MicrometerHttpRequestExecutor.java | 4 +- .../binder/jetty/JettyClientMetrics.java | 4 +- .../binder/jetty/JettyConnectionMetrics.java | 4 +- .../instrument/binder/jetty/TimedHandler.java | 8 +- .../tck/MeterRegistryCompatibilityKit.java | 88 +++---- .../samples/ObservationHandlerSample.java | 109 +++++++++ .../samples/TimerRecordingHandlerSample.java | 105 --------- 22 files changed, 926 insertions(+), 669 deletions(-) create mode 100644 micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java create mode 100644 micrometer-api/src/main/java/io/micrometer/api/instrument/Observation.java create mode 100644 micrometer-api/src/main/java/io/micrometer/api/instrument/ObservationHandler.java create mode 100644 micrometer-api/src/main/java/io/micrometer/api/instrument/SimpleObservation.java delete mode 100644 micrometer-api/src/main/java/io/micrometer/api/instrument/TimerRecordingHandler.java create mode 100644 micrometer-api/src/test/java/io/micrometer/api/instrument/CurrentObservationTest.java delete mode 100644 micrometer-api/src/test/java/io/micrometer/api/instrument/CurrentSampleTest.java create mode 100644 samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java delete mode 100644 samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/TimerRecordingHandlerSample.java diff --git a/micrometer-api/src/main/java/io/micrometer/api/aop/TimedAspect.java b/micrometer-api/src/main/java/io/micrometer/api/aop/TimedAspect.java index 7446d75214..16345d2901 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/aop/TimedAspect.java +++ b/micrometer-api/src/main/java/io/micrometer/api/aop/TimedAspect.java @@ -201,7 +201,9 @@ private void record(ProceedingJoinPoint pjp, Timed timed, String metricName, Tim .tags(EXCEPTION_TAG, exceptionClass) .tags(tagsBasedOnJoinPoint.apply(pjp)) .publishPercentileHistogram(timed.histogram()) - .publishPercentiles(timed.percentiles().length == 0 ? null : timed.percentiles())); + .publishPercentiles(timed.percentiles().length == 0 ? null : timed.percentiles()) + .register(registry) + ); } catch (Exception e) { // ignoring on purpose } diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/MeterRegistry.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/MeterRegistry.java index da6d658f4b..2fca7b584b 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/MeterRegistry.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/MeterRegistry.java @@ -79,13 +79,13 @@ public abstract class MeterRegistry { protected final Clock clock; private final Object meterMapLock = new Object(); private volatile MeterFilter[] filters = new MeterFilter[0]; - private final List> timerRecordingHandlers = new CopyOnWriteArrayList<>(); + private final List> observationHandlers = new CopyOnWriteArrayList<>(); private final List> meterAddedListeners = new CopyOnWriteArrayList<>(); private final List> meterRemovedListeners = new CopyOnWriteArrayList<>(); private final List> meterRegistrationFailedListeners = new CopyOnWriteArrayList<>(); private final Config config = new Config(); private final More more = new More(); - private final ThreadLocal localSample = new ThreadLocal<>(); + private final ThreadLocal localObservation = new ThreadLocal<>(); // Even though writes are guarded by meterMapLock, iterators across value space are supported // Hence, we use CHM to support that iteration without ConcurrentModificationException risk @@ -116,12 +116,13 @@ protected MeterRegistry(Clock clock) { this.clock = clock; } - @Nullable public Timer.Sample getCurrentSample() { - return localSample.get(); + @Nullable + public Observation getCurrentObservation() { + return this.localObservation.get(); } - Timer.Scope openNewScope(Timer.Sample currentSample) { - return new Timer.Scope(localSample, currentSample); + void setCurrentObservation(@Nullable Observation current) { + this.localObservation.set(current); } /** @@ -567,6 +568,14 @@ public > T gaugeCollectionSize(String name, Iterable M registerMeterIfNecessary(Class meterClass, Meter.Id id, Function builder, Function noopBuilder) { return registerMeterIfNecessary(meterClass, id, null, (id2, conf) -> builder.apply(id2), noopBuilder); @@ -810,22 +819,20 @@ public Config onMeterRegistrationFailed(BiConsumer meterRegistration } /** - * Register an event handler for {@link Timer} recordings made using {@link Timer#start(MeterRegistry)} - * and {@link Timer.Sample#stop(Timer.Builder)} methods. You can add arbitrary behavior - * in the callbacks provided to get additional behavior out of timing instrumentation. + * Register a handler for the {@link Observation observations}. * * @param handler handler to add to the current configuration * @return This configuration instance * @since 2.0.0 */ - public Config timerRecordingHandler(TimerRecordingHandler handler) { - timerRecordingHandlers.add(handler); + public Config observationHandler(ObservationHandler handler) { + observationHandlers.add(handler); return this; } // package-private for minimal visibility - Collection> getTimerRecordingHandlers() { - return timerRecordingHandlers; + Collection> getObservationHandlers() { + return observationHandlers; } /** diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java new file mode 100644 index 0000000000..221cdb8278 --- /dev/null +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java @@ -0,0 +1,128 @@ +package io.micrometer.api.instrument; + +import java.time.Duration; +import java.util.Collections; + +import io.micrometer.api.lang.Nullable; + +public class NoopObservation implements Observation { + private static final Iterable TAGS = Collections.emptyList(); + public static final NoopObservation INSTANCE = new NoopObservation(); + + private NoopObservation() { + } + + @Override + public String getName() { + return "noop"; + } + + @Override + public String getDescription() { + return "noop"; + } + + @Override + public Observation description(String description) { + return this; + } + + @Override + public String getDisplayName() { + return "noop"; + } + + @Override + public Observation displayName(String displayName) { + return this; + } + + @Override + public Iterable getLowCardinalityTags() { + return TAGS; + } + + @Override + public Observation lowCardinalityTag(Tag tag) { + return this; + } + + @Override + public Observation lowCardinalityTag(String key, String value) { + return this; + } + + @Override + public Iterable getHighCardinalityTags() { + return TAGS; + } + + @Override + public Observation highCardinalityTag(Tag tag) { + return this; + } + + @Override + public Observation highCardinalityTag(String key, String value) { + return this; + } + + @Nullable + @Override + public Throwable getError() { + return null; + } + + @Override + public Observation error(Throwable error) { + return this; + } + + @Override + public Duration getDuration() { + return Duration.ZERO; + } + + @Override + public long getStartNanos() { + return 0; + } + + @Override + public long getStopNanos() { + return 0; + } + + @Override + public long getStartWallTime() { + return 0; + } + + @Override + public Observation start() { + return this; + } + + @Override + public void stop() { + } + + @Override + public Scope openScope() { + return NoOpScope.INSTANCE; + } + + static class NoOpScope implements Scope { + static final NoOpScope INSTANCE = new NoOpScope(); + + @Nullable + @Override + public Observation getCurrentObservation() { + return NoopObservation.INSTANCE; + } + + @Override + public void close() { + } + } +} diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/Observation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/Observation.java new file mode 100644 index 0000000000..52e6fed3cd --- /dev/null +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/Observation.java @@ -0,0 +1,95 @@ +package io.micrometer.api.instrument; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import io.micrometer.api.lang.Nullable; + +public interface Observation { + + String getName(); + + default String getDescription() { + return ""; + } + + default Observation description(String description) { + return this; + } + + String getDisplayName(); + + Observation displayName(String displayName); + + Iterable getLowCardinalityTags(); + + Observation lowCardinalityTag(Tag tag); + + default Observation lowCardinalityTag(String key, String value) { + return lowCardinalityTag(Tag.of(key, value)); + } + + Iterable getHighCardinalityTags(); + + Observation highCardinalityTag(Tag tag); + + default Observation highCardinalityTag(String key, String value) { + return highCardinalityTag(Tag.of(key, value)); + } + + @Nullable Throwable getError(); + + Observation error(Throwable error); + + Duration getDuration(); + + long getStartNanos(); + + long getStopNanos(); + + long getStartWallTime(); + + Observation start(); + + void stop(); + + Scope openScope(); + + interface Scope extends AutoCloseable { + @Nullable Observation getCurrentObservation(); + + @Override void close(); + } + + @SuppressWarnings("unchecked") + class Context implements TagsProvider { + private final Map, Object> map = new HashMap<>(); + + public Context put(Class clazz, T object) { + this.map.put(clazz, object); + return this; + } + + public void remove(Class clazz) { + this.map.remove(clazz); + } + + @Nullable public T get(Class clazz) { + return (T) this.map.get(clazz); + } + + public T getOrDefault(Class clazz, T defaultObject) { + return (T) this.map.getOrDefault(clazz, defaultObject); + } + + public T computeIfAbsent(Class clazz, Function, ? extends T> mappingFunction) { + return (T) this.map.computeIfAbsent(clazz, mappingFunction); + } + + public void clear() { + this.map.clear(); + } + } +} diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/ObservationHandler.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/ObservationHandler.java new file mode 100644 index 0000000000..781d74817b --- /dev/null +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/ObservationHandler.java @@ -0,0 +1,161 @@ +package io.micrometer.api.instrument; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +public interface ObservationHandler { + void onStart(Observation observation, T context); + + void onError(Observation observation, T context); + + default void onScopeOpened(Observation observation, T context) { + } + + default void onScopeClosed(Observation observation, T context) { + } + + void onStop(Observation observation, T context); + + boolean supportsContext(Observation.Context context); + + + /** + * Handler wrapping other handlers. + */ + @SuppressWarnings("rawtypes") + interface CompositeObservationHandler extends ObservationHandler { + /** + * Returns the registered recording handlers. + * @return registered handlers + */ + List getHandlers(); + } + + /** + * Handler picking the first matching recording handler from the list. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + class FirstMatchingCompositeObservationHandler implements CompositeObservationHandler { + private final List handlers; + + /** + * Creates a new instance of {@code FirstMatchingCompositeObservationHandler}. + * @param handlers the handlers that are registered under the composite + */ + public FirstMatchingCompositeObservationHandler(ObservationHandler... handlers) { + this(Arrays.asList(handlers)); + } + + /** + * Creates a new instance of {@code FirstMatchingCompositeObservationHandler}. + * @param handlers the handlers that are registered under the composite + */ + public FirstMatchingCompositeObservationHandler(List handlers) { + this.handlers = handlers; + } + + @Override + public List getHandlers() { + return this.handlers; + } + + @Override + public void onStart(Observation observation, Observation.Context context) { + getFirstApplicableHandler(context).ifPresent(handler -> handler.onStart(observation, context)); + } + + @Override + public void onError(Observation observation, Observation.Context context) { + getFirstApplicableHandler(context).ifPresent(handler -> handler.onError(observation, context)); + } + + @Override + public void onScopeOpened(Observation observation, Observation.Context context) { + getFirstApplicableHandler(context).ifPresent(handler -> handler.onScopeOpened(observation, context)); + } + + @Override + public void onScopeClosed(Observation observation, Observation.Context context) { + getFirstApplicableHandler(context).ifPresent(handler -> handler.onScopeClosed(observation, context)); + } + + @Override + public void onStop(Observation observation, Observation.Context context) { + getFirstApplicableHandler(context).ifPresent(handler -> handler.onStop(observation, context)); + } + + @Override + public boolean supportsContext(Observation.Context context) { + return getFirstApplicableHandler(context).isPresent(); + } + + private Optional getFirstApplicableHandler(Observation.Context context) { + return this.handlers.stream().filter(handler -> handler.supportsContext(context)).findFirst(); + } + } + + /** + * Handler picking all matching recording handlers from the list. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + class AllMatchingCompositeObservationHandler implements CompositeObservationHandler { + private final List handlers; + + /** + * Creates a new instance of {@code AllMatchingCompositeObservationHandler}. + * @param handlers the handlers that are registered under the composite + */ + public AllMatchingCompositeObservationHandler(ObservationHandler... handlers) { + this(Arrays.asList(handlers)); + } + + /** + * Creates a new instance of {@code AllMatchingCompositeObservationHandler}. + * @param handlers the handlers that are registered under the composite + */ + public AllMatchingCompositeObservationHandler(List handlers) { + this.handlers = handlers; + } + + @Override + public List getHandlers() { + return this.handlers; + } + + @Override + public void onStart(Observation observation, Observation.Context context) { + getAllApplicableHandlers(context).forEach(handler -> handler.onStart(observation, context)); + } + + @Override + public void onError(Observation observation, Observation.Context context) { + getAllApplicableHandlers(context).forEach(handler -> handler.onError(observation, context)); + } + + @Override + public void onScopeOpened(Observation observation, Observation.Context context) { + getAllApplicableHandlers(context).forEach(handler -> handler.onScopeOpened(observation, context)); + } + + @Override + public void onScopeClosed(Observation observation, Observation.Context context) { + getAllApplicableHandlers(context).forEach(handler -> handler.onScopeClosed(observation, context)); + } + + @Override + public void onStop(Observation observation, Observation.Context context) { + getAllApplicableHandlers(context).forEach(handler -> handler.onStop(observation, context)); + } + + @Override + public boolean supportsContext(Observation.Context context) { + return getAllApplicableHandlers(context).findAny().isPresent(); + } + + private Stream getAllApplicableHandlers(Observation.Context context) { + return this.handlers.stream().filter(handler -> handler.supportsContext(context)); + } + } +} diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/SimpleObservation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/SimpleObservation.java new file mode 100644 index 0000000000..657c315a01 --- /dev/null +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/SimpleObservation.java @@ -0,0 +1,208 @@ +package io.micrometer.api.instrument; + +import java.time.Duration; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import io.micrometer.api.lang.Nullable; + +public class SimpleObservation implements Observation { + private final String name; + private final MeterRegistry registry; + private final Clock clock; + private String description; + private String displayName; + private final Set lowCardinalityTags = new LinkedHashSet<>(); // We can add cardinality to the Tag interface (we did before) so that we can have just one Set tags + private final Set highCardinalityTags = new LinkedHashSet<>(); // We can also use the Tags class, but it is an immutable collection which copies everything over to a new instance when you want to add something + @Nullable private Throwable error; + private Duration duration = Duration.ZERO; + private long started = 0; + private long stopped = 0; + private long startWallTime = 0; + private final Context context; + @SuppressWarnings("rawtypes") + private final Collection handlers; + + public SimpleObservation(String name, MeterRegistry registry) { + this(name, registry, new Context()); + } + + public SimpleObservation(String name, MeterRegistry registry, Context context) { + this.name = name; + this.registry = registry; + this.clock = registry.config().clock(); + this.description = ""; + this.displayName = name; + this.context = context; + this.handlers = registry.config().getObservationHandlers().stream() + .filter(handler -> handler.supportsContext(this.context)) + .collect(Collectors.toList()); + } + + @Override + public String getName() { + return this.name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public Observation description(String description) { + this.description = description; + return this; + } + + @Override + public String getDisplayName() { + return this.displayName; + } + + @Override + public Observation displayName(String displayName) { + this.displayName = displayName; + return this; + } + + @Override + public Iterable getLowCardinalityTags() { + return this.context.getLowCardinalityTags().and(this.lowCardinalityTags); + } + + @Override + public Observation lowCardinalityTag(Tag tag) { + this.lowCardinalityTags.add(tag); + return this; + } + + @Override + public Iterable getHighCardinalityTags() { + return this.context.getHighCardinalityTags().and(this.highCardinalityTags); + } + + @Override + public Observation highCardinalityTag(Tag tag) { + this.highCardinalityTags.add(tag); + return this; + } + + @Nullable + @Override + public Throwable getError() { + return this.error; + } + + @Override + public Observation error(Throwable error) { + this.error = error; + this.notifyOnError(); + return this; + } + + @Override + public Duration getDuration() { + return this.duration; + } + + @Override + public long getStartNanos() { + return this.started; + } + + @Override + public long getStopNanos() { + return this.stopped; + } + + @Override + public long getStartWallTime() { + return this.startWallTime; + } + + @Override + public Observation start() { + this.startWallTime = this.clock.wallTime(); + this.started = this.clock.monotonicTime(); + this.notifyOnObservationStarted(); + return this; + } + + @Override + public void stop() { + this.stopped = this.clock.monotonicTime(); + this.duration = Duration.ofNanos(this.stopped - this.started); + this.notifyOnObservationStopped(); + } + + @Override + public Scope openScope() { + Scope scope = new SimpleScope(this.registry, this); + this.notifyOnScopeOpened(); + return scope; + } + + @Override + public String toString() { + return "{" + + "name=" + this.getName() + "(" + this.getDisplayName() + ")" + + ", duration=" + this.getDuration().toMillis() + "ms" + + ", lowCardTags=" + this.getLowCardinalityTags() + + ", highCardTags=" + this.getHighCardinalityTags() + + ", error=" + this.getError() + + ", context=" + this.context + + '}'; + } + + @SuppressWarnings("unchecked") + private void notifyOnObservationStarted() { + this.handlers.forEach(handler -> handler.onStart(this, this.context)); + } + + @SuppressWarnings("unchecked") + private void notifyOnError() { + this.handlers.forEach(handler -> handler.onError(this, this.context)); + } + + @SuppressWarnings("unchecked") + private void notifyOnScopeOpened() { + this.handlers.forEach(handler -> handler.onScopeOpened(this, this.context)); + } + + @SuppressWarnings("unchecked") + private void notifyOnScopeClosed() { + this.handlers.forEach(handler -> handler.onScopeClosed(this, this.context)); + } + + @SuppressWarnings("unchecked") + private void notifyOnObservationStopped() { + this.handlers.forEach(handler -> handler.onStop(this, this.context)); + } + + static class SimpleScope implements Scope { + private final MeterRegistry registry; + private final SimpleObservation currentObservation; + @Nullable private final Observation previousObservation; + + SimpleScope(MeterRegistry registry, SimpleObservation current) { + this.registry = registry; + this.currentObservation = current; + this.previousObservation = registry.getCurrentObservation(); + this.registry.setCurrentObservation(current); + } + + @Override + public Observation getCurrentObservation() { + return this.currentObservation; + } + + @Override + public void close() { + this.registry.setCurrentObservation(previousObservation); + this.currentObservation.notifyOnScopeClosed(); + } + } +} diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/Timer.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/Timer.java index 59325e2b8c..d949572390 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/Timer.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/Timer.java @@ -15,17 +15,11 @@ */ package io.micrometer.api.instrument; -import java.io.Closeable; import java.time.Duration; import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; -import java.util.function.Function; import java.util.function.Supplier; -import java.util.stream.Collectors; import io.micrometer.api.annotation.Incubating; import io.micrometer.api.annotation.Timed; @@ -44,6 +38,16 @@ * @author Oleksii Bondar */ public interface Timer extends Meter, HistogramSupport { + /** + * Start a timing sample using the {@link Clock#SYSTEM System clock}. + * + * @return A timing sample with start time recorded. + * @since 1.1.0 + */ + static Sample start() { + return start(Clock.SYSTEM); + } + /** * Start a timing sample. * @@ -51,19 +55,16 @@ public interface Timer extends Meter, HistogramSupport { * @return A timing sample with start time recorded. */ static Sample start(MeterRegistry registry) { - return start(registry, new HandlerContext()); + return start(registry.config().clock()); } /** * Start a timing sample. * - * @param registry A meter registry whose clock is to be used - * @param handlerContext handler context * @return A timing sample with start time recorded. - * @since 2.0.0 */ - static Sample start(MeterRegistry registry, HandlerContext handlerContext) { - return new Sample(registry, handlerContext); + static Sample start(Clock clock) { + return new Sample(clock); } static Builder builder(String name) { @@ -259,175 +260,28 @@ default double percentile(double percentile, TimeUnit unit) { /** * Maintains state on the clock's start position for a latency sample. Complete the timing - * by calling {@link Sample#stop(Timer.Builder)}. Note how the {@link Timer} isn't provided until the + * by calling {@link Sample#stop(Timer)}. Note how the {@link Timer} isn't provided until the * sample is stopped, allowing you to determine the timer's tags at the last minute. */ - @SuppressWarnings({ "unchecked", "rawtypes" }) class Sample { - private final long startTime; private final Clock clock; - private final Collection handlers; - private final HandlerContext handlerContext; - private final MeterRegistry registry; - Sample(MeterRegistry registry, HandlerContext ctx) { - this.clock = registry.config().clock(); + Sample(Clock clock) { + this.clock = clock; this.startTime = clock.monotonicTime(); - this.handlerContext = ctx; - this.handlers = registry.config().getTimerRecordingHandlers().stream() - .filter(handler -> handler.supportsContext(this.handlerContext)) - .collect(Collectors.toList()); - notifyOnSampleStarted(); - this.registry = registry; - } - - /** - * Mark an exception that happened between the sample's start/stop. - * - * @param throwable exception that happened - * @since 2.0.0 - */ - public void error(Throwable throwable) { - // TODO check stop hasn't been called yet? - // TODO doesn't do anything to tags currently; we should make error tagging more first-class - notifyOnError(throwable); - } - - /** - * Records the duration of the operation and adds tags to the {@link Timer.Builder} based on the - * {@link HandlerContext} for this {@link Sample}. - * - * @param timerBuilder The timer builder to record the sample to. - * @return The total duration of the sample in nanoseconds - * @since 2.0.0 - */ - public long stop(Timer.Builder timerBuilder) { - timerBuilder.tags(this.handlerContext.getLowCardinalityTags()); - return stop(timerBuilder.register(this.registry)); } - // TODO: We'll need to make this private. I'm leaving this for now as it is cause it breaks compilation in quite a few places /** * Records the duration of the operation. * - * @deprecated Will be removed in a subsequent milestone release, please use {@link Sample#stop(Builder)}. * @param timer The timer to record the sample to. * @return The total duration of the sample in nanoseconds */ - @Deprecated public long stop(Timer timer) { - long duration = clock.monotonicTime() - startTime; - timer.record(duration, TimeUnit.NANOSECONDS); - notifyOnSampleStopped(timer, Duration.ofNanos(duration)); - - return duration; - } - - /** - * Make this sample current. - * - * @return newly opened scope - * @since 2.0.0 - */ - public Scope makeCurrent() { - notifyOnScopeOpened(); - return registry.openNewScope(this); - } - - private void notifyOnSampleStarted() { - this.handlers.forEach(handler -> handler.onStart(this, this.handlerContext)); - } - - private void notifyOnError(Throwable throwable) { - this.handlers.forEach(handler -> handler.onError(this, this.handlerContext, throwable)); - } - - private void notifyOnScopeOpened() { - this.handlers.forEach(handler -> handler.onScopeOpened(this, this.handlerContext)); - } - - private void notifyOnScopeClosed() { - this.handlers.forEach(handler -> handler.onScopeClosed(this, this.handlerContext)); - } - - private void notifyOnSampleStopped(Timer timer, Duration duration) { - this.handlers.forEach(handler -> handler.onStop(this, this.handlerContext, timer, duration)); - } - } - - /** - * Nestable bounding for {@link Timer timed} operations that capture and pass along already opened scopes. - * - * @since 2.0.0 - */ - class Scope implements Closeable { - private final ThreadLocal threadLocal; - private final Sample currentSample; - private final Sample previousSample; - - public Scope(ThreadLocal threadLocal, Sample currentSample) { - this.threadLocal = threadLocal; - this.currentSample = currentSample; - this.previousSample = threadLocal.get(); - threadLocal.set(currentSample); - } - - public Sample getSample() { - return this.currentSample; - } - - @Override - public void close() { - this.currentSample.notifyOnScopeClosed(); - threadLocal.set(previousSample); - } - } - - /** - * Context for {@link Sample} instances used by {@link TimerRecordingHandler} to pass arbitrary objects between - * handler methods. Usage is similar to the JDK {@link Map} API. - * - * @since 2.0.0 - */ - @SuppressWarnings("unchecked") - class HandlerContext implements TagsProvider { - private final Map, Object> map = new HashMap<>(); - - /** - * The name of the recorded measurement in the context of the {@link TimerRecordingHandler}. - * The Timer itself has a name but you might want to use a different name in the {@link TimerRecordingHandler}. - * This method makes it possible to use a different name. - * - * @return the contextual name - */ - @Nullable public String getContextualName() { - return null; - } - - public HandlerContext put(Class clazz, T object) { - this.map.put(clazz, object); - return this; - } - - public void remove(Class clazz) { - this.map.remove(clazz); - } - - @Nullable public T get(Class clazz) { - return (T) this.map.get(clazz); - } - - public T getOrDefault(Class clazz, T defaultObject) { - return (T) this.map.getOrDefault(clazz, defaultObject); - } - - public T computeIfAbsent(Class clazz, Function, ? extends T> mappingFunction) { - return (T) this.map.computeIfAbsent(clazz, mappingFunction); - } - - public void clear() { - this.map.clear(); + long durationNs = clock.monotonicTime() - startTime; + timer.record(durationNs, TimeUnit.NANOSECONDS); + return durationNs; } } @@ -545,7 +399,7 @@ public Builder description(String description) { */ public Timer register(MeterRegistry registry) { // the base unit for a timer will be determined by the monitoring system implementation - return registry.timer(new Id(name, tags, null, description, Type.TIMER), distributionConfigBuilder.build(), + return registry.timer(new Meter.Id(name, tags, null, description, Type.TIMER), distributionConfigBuilder.build(), pauseDetector == null ? registry.config().pauseDetector() : pauseDetector); } } diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/TimerRecordingHandler.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/TimerRecordingHandler.java deleted file mode 100644 index c0c05155fc..0000000000 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/TimerRecordingHandler.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2021 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.api.instrument; - -import java.time.Duration; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.stream.Stream; - -import io.micrometer.api.lang.Nullable; - -/** - * Handler with callbacks for the {@link Timer#start(MeterRegistry) start} and - * {@link Timer.Sample#stop(Timer.Builder) stop} of a {@link Timer} recording. - * - * @author Marcin Grzejszczak - * @author Tommy Ludwig - * @author Jonatan Ivanov - * @since 2.0.0 - */ -public interface TimerRecordingHandler { - /** - * @param sample the sample that was started - * @param context handler context - */ - void onStart(Timer.Sample sample, @Nullable T context); - - /** - * @param sample sample for which the error happened - * @param context handler context - * @param throwable exception that happened during recording - */ - void onError(Timer.Sample sample, @Nullable T context, Throwable throwable); - - /** - * @param sample sample for which the scope was opened - * @param context handler context - */ - default void onScopeOpened(Timer.Sample sample, @Nullable T context) { - } - - /** - * @param sample sample for which the scope was closed - * @param context handler context - */ - default void onScopeClosed(Timer.Sample sample, @Nullable T context) { - } - - /** - * @param sample the sample that was stopped - * @param context handler context - * @param timer the timer to which the recording was made - * @param duration time recorded - */ - void onStop(Timer.Sample sample, @Nullable T context, Timer timer, Duration duration); - - /** - * @param handlerContext handler context, may be {@code null} - * @return {@code true} when this handler context is supported - */ - boolean supportsContext(@Nullable Timer.HandlerContext handlerContext); - - /** - * Handler wrapping other handlers. - */ - interface CompositeTimerRecordingHandler extends TimerRecordingHandler { - /** - * Returns the registered recording handlers. - * @return registered handlers - */ - List getHandlers(); - } - - /** - * Handler picking the first matching recording handler from the list. - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - class FirstMatchingCompositeTimerRecordingHandler implements CompositeTimerRecordingHandler { - - private final List handlers; - - /** - * Creates a new instance of {@code FirstMatchingCompositeTimerRecordingHandler}. - * @param handlers the handlers that are registered under the composite - */ - public FirstMatchingCompositeTimerRecordingHandler(TimerRecordingHandler... handlers) { - this(Arrays.asList(handlers)); - } - - /** - * Creates a new instance of {@code FirstMatchingCompositeTimerRecordingHandler}. - * @param handlers the handlers that are registered under the composite - */ - public FirstMatchingCompositeTimerRecordingHandler(List handlers) { - this.handlers = handlers; - } - - @Override - public void onStart(Timer.Sample sample, @Nullable Timer.HandlerContext context) { - getFirstApplicableHandler(context).ifPresent(handler -> handler.onStart(sample, context)); - } - - @Override - public void onError(Timer.Sample sample, @Nullable Timer.HandlerContext context, Throwable throwable) { - getFirstApplicableHandler(context).ifPresent(handler -> handler.onError(sample, context, throwable)); - } - - @Override - public void onScopeOpened(Timer.Sample sample, @Nullable Timer.HandlerContext context) { - getFirstApplicableHandler(context).ifPresent(handler -> handler.onScopeOpened(sample, context)); - } - - @Override - public void onScopeClosed(Timer.Sample sample, @Nullable Timer.HandlerContext context) { - getFirstApplicableHandler(context).ifPresent(handler -> handler.onScopeClosed(sample, context)); - } - - @Override - public void onStop(Timer.Sample sample, @Nullable Timer.HandlerContext context, Timer timer, Duration duration) { - getFirstApplicableHandler(context).ifPresent(handler -> handler.onStop(sample, context, timer, duration)); - } - - private Optional getFirstApplicableHandler(@Nullable Timer.HandlerContext context) { - return this.handlers.stream().filter(handler -> handler.supportsContext(context)).findFirst(); - } - - @Override - public boolean supportsContext(@Nullable Timer.HandlerContext handlerContext) { - return getFirstApplicableHandler(handlerContext).isPresent(); - } - - @Override - public List getHandlers() { - return this.handlers; - } - } - - /** - * Handler picking all matching recording handlers from the list. - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - class AllMatchingCompositeTimerRecordingHandler implements CompositeTimerRecordingHandler { - - private final List handlers; - - /** - * Creates a new instance of {@code FirstMatchingCompositeTimerRecordingHandler}. - * @param handlers the handlers that are registered under the composite - */ - public AllMatchingCompositeTimerRecordingHandler(TimerRecordingHandler... handlers) { - this(Arrays.asList(handlers)); - } - - /** - * Creates a new instance of {@code FirstMatchingCompositeTimerRecordingHandler}. - * @param handlers the handlers that are registered under the composite - */ - public AllMatchingCompositeTimerRecordingHandler(List handlers) { - this.handlers = handlers; - } - - @Override - public void onStart(Timer.Sample sample, @Nullable Timer.HandlerContext context) { - getAllApplicableHandlers(context).forEach(handler -> handler.onStart(sample, context)); - } - - @Override - public void onError(Timer.Sample sample, @Nullable Timer.HandlerContext context, Throwable throwable) { - getAllApplicableHandlers(context).forEach(handler -> handler.onError(sample, context, throwable)); - } - - @Override - public void onScopeOpened(Timer.Sample sample, @Nullable Timer.HandlerContext context) { - getAllApplicableHandlers(context).forEach(handler -> handler.onScopeOpened(sample, context)); - } - - @Override - public void onScopeClosed(Timer.Sample sample, @Nullable Timer.HandlerContext context) { - getAllApplicableHandlers(context).forEach(handler -> handler.onScopeClosed(sample, context)); - } - - @Override - public void onStop(Timer.Sample sample, @Nullable Timer.HandlerContext context, Timer timer, Duration duration) { - getAllApplicableHandlers(context).forEach(handler -> handler.onStop(sample, context, timer, duration)); - } - - private Stream getAllApplicableHandlers(@Nullable Timer.HandlerContext context) { - return this.handlers.stream().filter(handler -> handler.supportsContext(context)); - } - - @Override - public boolean supportsContext(@Nullable Timer.HandlerContext handlerContext) { - return getAllApplicableHandlers(handlerContext).findAny().isPresent(); - } - - @Override - public List getHandlers() { - return this.handlers; - } - } -} diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/internal/TimedRunnable.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/internal/TimedRunnable.java index 2e540f35c6..c6abe638e7 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/internal/TimedRunnable.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/internal/TimedRunnable.java @@ -27,7 +27,7 @@ class TimedRunnable implements Runnable { private final Timer idleTimer; private final Runnable command; private final Timer.Sample idleSample; - private final Timer.Scope idleScope; +// private final Timer.Scope idleScope; TimedRunnable(MeterRegistry registry, Timer executionTimer, Timer idleTimer, Runnable command) { this.registry = registry; @@ -35,17 +35,19 @@ class TimedRunnable implements Runnable { this.idleTimer = idleTimer; this.command = command; this.idleSample = Timer.start(registry); - this.idleScope = this.idleSample.makeCurrent(); +// this.idleScope = this.idleSample.makeCurrent(); } @Override public void run() { - idleScope.close(); +// idleScope.close(); idleSample.stop(idleTimer); Timer.Sample executionSample = Timer.start(registry); - try (Timer.Scope scope = executionSample.makeCurrent()) { +// try (Timer.Scope scope = executionSample.makeCurrent()) { + try { command.run(); - } finally { + } + finally { executionSample.stop(executionTimer); } } diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpClientHandlerContext.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpClientHandlerContext.java index bd5f3b14a8..b2b25c28db 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpClientHandlerContext.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpClientHandlerContext.java @@ -15,14 +15,14 @@ */ package io.micrometer.api.instrument.transport.http.context; -import io.micrometer.api.instrument.Timer; +import io.micrometer.api.instrument.Observation; import io.micrometer.api.instrument.transport.http.tags.HttpTagsProvider; import io.micrometer.api.instrument.transport.http.HttpClientRequest; import io.micrometer.api.instrument.transport.http.HttpClientResponse; import io.micrometer.api.lang.NonNull; /** - * {@link Timer.HandlerContext HandlerContext} + * {@link Observation.Context Context} * for an HTTP client request/response. * * @author Jonatan Ivanov diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpHandlerContext.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpHandlerContext.java index f972fcf8ca..a380b4cc5a 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpHandlerContext.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpHandlerContext.java @@ -15,8 +15,8 @@ */ package io.micrometer.api.instrument.transport.http.context; +import io.micrometer.api.instrument.Observation; import io.micrometer.api.instrument.Tags; -import io.micrometer.api.instrument.Timer; import io.micrometer.api.instrument.transport.http.HttpRequest; import io.micrometer.api.instrument.transport.http.HttpResponse; import io.micrometer.api.instrument.transport.http.tags.HttpTagsProvider; @@ -24,14 +24,14 @@ import io.micrometer.api.lang.Nullable; /** - * {@link Timer.HandlerContext HandlerContext} for an HTTP exchange. + * {@link Observation.Context} for an HTTP exchange. * * @author Marcin Grzejszczak * @since 2.0.0 * @param request type * @param response type */ -public abstract class HttpHandlerContext extends Timer.HandlerContext { +public abstract class HttpHandlerContext extends Observation.Context { private final HttpTagsProvider tagsProvider; diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpServerHandlerContext.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpServerHandlerContext.java index a88aa7aa15..884d1ebe08 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpServerHandlerContext.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpServerHandlerContext.java @@ -15,14 +15,14 @@ */ package io.micrometer.api.instrument.transport.http.context; -import io.micrometer.api.instrument.Timer; +import io.micrometer.api.instrument.Observation; import io.micrometer.api.instrument.transport.http.tags.HttpTagsProvider; import io.micrometer.api.lang.NonNull; import io.micrometer.api.instrument.transport.http.HttpServerRequest; import io.micrometer.api.instrument.transport.http.HttpServerResponse; /** - * {@link Timer.HandlerContext HandlerContext} for an HTTP server request/response. + * {@link Observation.Context Context} for an HTTP server request/response. * * @author Marcin Grzejszczak * @since 2.0.0 diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/CurrentObservationTest.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/CurrentObservationTest.java new file mode 100644 index 0000000000..4143132544 --- /dev/null +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/CurrentObservationTest.java @@ -0,0 +1,106 @@ +/* + * Copyright 2021 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.api.instrument; + +import io.micrometer.api.instrument.simple.SimpleMeterRegistry; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.*; + +import static org.assertj.core.api.Assertions.assertThat; + +class CurrentObservationTest { + private final MeterRegistry registry = new SimpleMeterRegistry(); + + @Test + void nestedSamples_parentChildThreadsInstrumented() throws ExecutionException, InterruptedException { + ExecutorService taskRunner = Executors.newSingleThreadExecutor(); + + Observation observation = registry.observation("test.observation"); + System.out.println("Outside task: " + observation); + assertThat(registry.getCurrentObservation()).isNull(); + try (Observation.Scope scope = observation.openScope()) { + assertThat(registry.getCurrentObservation()).isSameAs(observation); + taskRunner.submit(() -> { + System.out.println("In task: " + registry.getCurrentObservation()); + assertThat(registry.getCurrentObservation()).isNotEqualTo(observation); + }).get(); + } + assertThat(registry.getCurrentObservation()).isNull(); + observation.stop(); + } + + @Test + void start_thenStopOnChildThread() throws InterruptedException, ExecutionException { + ExecutorService executor = Executors.newSingleThreadExecutor(); + + Observation observation = registry.observation("test.observation"); + assertThat(registry.getCurrentObservation()).isNull(); + executor.submit(() -> { + try (Observation.Scope scope = observation.openScope()) { + assertThat(registry.getCurrentObservation()).isEqualTo(observation); + } + observation.stop(); + }).get(); + + assertThat(registry.getCurrentObservation()).isNull(); + } + + @Test + void startOnChildThread_thenStopOnSiblingThread() throws InterruptedException, ExecutionException { + // 2 thread pools with 1 thread each, so a different thread is used for the 2 tasks + ExecutorService executor = Executors.newSingleThreadExecutor(); + ExecutorService executor2 = Executors.newSingleThreadExecutor(); + Map observationMap = new HashMap<>(); + + executor.submit(() -> { + Observation observation = registry.observation("test.observation"); + assertThat(registry.getCurrentObservation()).isNull(); + observationMap.put("myObservation", observation); + }).get(); + + executor2.submit(() -> { + Observation myObservation= observationMap.get("myObservation"); + try (Observation.Scope scope = myObservation.openScope()) { + assertThat(registry.getCurrentObservation()).isEqualTo(myObservation); + } + myObservation.stop(); + assertThat(registry.getCurrentObservation()).isNull(); + }).get(); + + assertThat(registry.getCurrentObservation()).isNull(); + } + + @Test + void nestedSamples_sameThread() { + Observation observation = registry.observation("observation1"); + Observation observation2; + assertThat(registry.getCurrentObservation()).isNull(); + try (Observation.Scope scope = observation.openScope()) { + observation2 = registry.observation("observation2"); + assertThat(registry.getCurrentObservation()).isSameAs(observation); + } + try (Observation.Scope scope = observation2.openScope()) { + observation.stop(); + assertThat(registry.getCurrentObservation()).isSameAs(observation2); + } + observation2.stop(); + + assertThat(registry.getCurrentObservation()).isNull(); + } +} diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/CurrentSampleTest.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/CurrentSampleTest.java deleted file mode 100644 index 1ed9d8ba7b..0000000000 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/CurrentSampleTest.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2021 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.api.instrument; - -import io.micrometer.api.instrument.simple.SimpleMeterRegistry; -import org.junit.jupiter.api.Test; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.*; - -import static org.assertj.core.api.Assertions.assertThat; - -class CurrentSampleTest { - - MeterRegistry registry = new SimpleMeterRegistry(); - - @Test - void nestedSamples_parentChildThreadsInstrumented() throws ExecutionException, InterruptedException { - ExecutorService taskRunner = Executors.newSingleThreadExecutor(); - - Timer.Sample sample = Timer.start(registry); - System.out.println("Outside task: " + sample); - assertThat(registry.getCurrentSample()).isNull(); - try (Timer.Scope scope = sample.makeCurrent()) { - assertThat(registry.getCurrentSample()).isSameAs(sample); - taskRunner.submit(() -> { - System.out.println("In task: " + registry.getCurrentSample()); - assertThat(registry.getCurrentSample()).isNotEqualTo(sample); - }).get(); - } - assertThat(registry.getCurrentSample()).isNull(); - sample.stop(Timer.builder("my.service")); - } - - @Test - void start_thenStopOnChildThread() throws InterruptedException, ExecutionException { - ExecutorService executor = Executors.newSingleThreadExecutor(); - - Timer.Sample sample = Timer.start(registry); - assertThat(registry.getCurrentSample()).isNull(); - executor.submit(() -> { - try (Timer.Scope scope = sample.makeCurrent()) { - assertThat(registry.getCurrentSample()).isEqualTo(sample); - } - sample.stop(Timer.builder("my.timer")); - }).get(); - - assertThat(registry.getCurrentSample()).isNull(); - } - - @Test - void startOnChildThread_thenStopOnSiblingThread() throws InterruptedException, ExecutionException { - // 2 thread pools with 1 thread each, so a different thread is used for the 2 tasks - ExecutorService executor = Executors.newSingleThreadExecutor(); - ExecutorService executor2 = Executors.newSingleThreadExecutor(); - Map sampleMap = new HashMap<>(); - - executor.submit(() -> { - Timer.Sample sample = Timer.start(registry); - assertThat(registry.getCurrentSample()).isNull(); - sampleMap.put("mySample", sample); - }).get(); - - executor2.submit(() -> { - Timer.Sample mySample = sampleMap.get("mySample"); - try (Timer.Scope scope = mySample.makeCurrent()) { - assertThat(registry.getCurrentSample()).isEqualTo(mySample); - } - mySample.stop(Timer.builder("my.timer")); - assertThat(registry.getCurrentSample()).isNull(); - }).get(); - - assertThat(registry.getCurrentSample()).isNull(); - } - - @Test - void nestedSamples_sameThread() { - Timer.Sample sample = Timer.start(registry); - Timer.Sample sample2; - assertThat(registry.getCurrentSample()).isNull(); - try (Timer.Scope scope = sample.makeCurrent()) { - sample2 = Timer.start(registry); - assertThat(registry.getCurrentSample()).isSameAs(sample); - } - try (Timer.Scope scope = sample2.makeCurrent()) { - sample.stop(Timer.builder("test1")); - assertThat(registry.getCurrentSample()).isSameAs(sample2); - } - sample2.stop(Timer.builder("test2")); - - assertThat(registry.getCurrentSample()).isNull(); - } -} diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/db/JooqExecuteListener.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/db/JooqExecuteListener.java index bc9b9e7cac..4316333891 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/db/JooqExecuteListener.java +++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/db/JooqExecuteListener.java @@ -103,6 +103,8 @@ private void stopTimerIfStillRunning(ExecuteContext ctx) { .tag("type", ctx.type().name().toLowerCase()) .tag("exception", exceptionName) .tag("exception.subclass", exceptionSubclass) - .tags(tags)); + .tags(tags) + .register(registry) + ); } } diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpRequestExecutor.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpRequestExecutor.java index 4116897054..a16b5f6c08 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpRequestExecutor.java +++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpRequestExecutor.java @@ -121,7 +121,9 @@ public HttpResponse execute(HttpRequest request, HttpClientConnection conn, Http timerSample.stop(Timer.builder(METER_NAME) .description("Duration of Apache HttpClient request execution") - .tags(tags)); + .tags(tags) + .register(registry) + ); } } diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jetty/JettyClientMetrics.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jetty/JettyClientMetrics.java index a87f9f8d1a..5b6ec7524c 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jetty/JettyClientMetrics.java +++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jetty/JettyClientMetrics.java @@ -78,7 +78,9 @@ public void onQueued(Request request) { sample.stop(Timer.builder(timingMetricName) .description("Jetty HTTP client request timing") - .tags(httpRequestTags)); + .tags(httpRequestTags) + .register(registry) + ); }); } diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jetty/JettyConnectionMetrics.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jetty/JettyConnectionMetrics.java index bfffe289f6..86f306706f 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jetty/JettyConnectionMetrics.java +++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jetty/JettyConnectionMetrics.java @@ -165,7 +165,9 @@ public void onClosed(Connection connection) { sample.stop(Timer.builder("jetty.connections.request") .description("Jetty client or server requests") .tag("type", serverOrClient) - .tags(tags)); + .tags(tags) + .register(registry) + ); } messagesIn.increment(connection.getMessagesIn()); diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jetty/TimedHandler.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jetty/TimedHandler.java index 7d62aeeb6c..a861a00a81 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jetty/TimedHandler.java +++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jetty/TimedHandler.java @@ -136,7 +136,9 @@ public void handle(String path, Request baseRequest, HttpServletRequest request, sample.stop(Timer.builder("jetty.server.requests") .description("HTTP requests to the Jetty server") .tags(tagsProvider.getTags(request, response)) - .tags(tags)); + .tags(tags) + .register(registry) + ); requestSample.stop(); @@ -198,7 +200,9 @@ void onAsyncComplete(AsyncEvent event) { sample.stop(Timer.builder("jetty.server.requests") .description("HTTP requests to the Jetty server") .tags(tagsProvider.getTags(request, request.getResponse())) - .tags(tags)); + .tags(tags) + .register(registry) + ); lttSample.stop(); } diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java index 8771ad9ec7..2c8ce8a866 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java @@ -622,67 +622,67 @@ void wrapSupplier() { } } - @Test - @DisplayName("record with stateful Sample instance") - void recordWithSample() { - Timer.Sample sample = Timer.start(registry); - clock(registry).add(10, TimeUnit.NANOSECONDS); - sample.stop(Timer.builder("myTimer")); - clock(registry).add(step()); - - Timer timer = registry.timer("myTimer"); - assertAll(() -> assertEquals(1L, timer.count()), - () -> assertEquals(10, timer.totalTime(TimeUnit.NANOSECONDS), 1.0e-12)); - } - - @Test - @DisplayName("record with stateful Sample and Scope instances") - void recordWithSampleAndScope() { - Timer.Sample sample = Timer.start(registry); - try (Timer.Scope scope = sample.makeCurrent()) { - assertThat(scope.getSample()).isSameAs(sample); - clock(registry).add(10, TimeUnit.NANOSECONDS); - } - sample.stop(Timer.builder("myTimer")); - clock(registry).add(step()); - - Timer timer = registry.timer("myTimer"); - assertAll(() -> assertEquals(1L, timer.count()), - () -> assertEquals(10, timer.totalTime(TimeUnit.NANOSECONDS), 1.0e-12)); - } +// @Test +// @DisplayName("record with stateful Observation instance") +// void recordWithObservation() { +// Timer.Sample sample = Timer.start(registry); +// clock(registry).add(10, TimeUnit.NANOSECONDS); +// sample.stop(Timer.builder("myTimer")); +// clock(registry).add(step()); +// +// Timer timer = registry.timer("myTimer"); +// assertAll(() -> assertEquals(1L, timer.count()), +// () -> assertEquals(10, timer.totalTime(TimeUnit.NANOSECONDS), 1.0e-12)); +// } + +// @Test +// @DisplayName("record with stateful Observation and Scope instances") +// void recordWithObservationAndScope() { +// Timer.Sample sample = Timer.start(registry); +// try (Timer.Scope scope = sample.makeCurrent()) { +// assertThat(scope.getSample()).isSameAs(sample); +// clock(registry).add(10, TimeUnit.NANOSECONDS); +// } +// sample.stop(Timer.builder("myTimer")); +// clock(registry).add(step()); +// +// Timer timer = registry.timer("myTimer"); +// assertAll(() -> assertEquals(1L, timer.count()), +// () -> assertEquals(10, timer.totalTime(TimeUnit.NANOSECONDS), 1.0e-12)); +// } @Test @DisplayName("record using handlers") void recordWithHandlers() { @SuppressWarnings("unchecked") - TimerRecordingHandler handler = mock(TimerRecordingHandler.class); + ObservationHandler handler = mock(ObservationHandler.class); @SuppressWarnings("unchecked") - TimerRecordingHandler handlerThatHandlesNothing = mock(TimerRecordingHandler.class); - registry.config().timerRecordingHandler(handler); - registry.config().timerRecordingHandler(handlerThatHandlesNothing); + ObservationHandler handlerThatHandlesNothing = mock(ObservationHandler.class); + registry.config().observationHandler(handler); + registry.config().observationHandler(handlerThatHandlesNothing); when(handler.supportsContext(any())).thenReturn(true); when(handlerThatHandlesNothing.supportsContext(any())).thenReturn(false); - Timer.Sample sample = Timer.start(registry); - verify(handler).supportsContext(isA(Timer.HandlerContext.class)); - verify(handler).onStart(same(sample), isA(Timer.HandlerContext.class)); - verify(handlerThatHandlesNothing).supportsContext(isA(Timer.HandlerContext.class)); + Observation observation = registry.observation("myObservation"); + verify(handler).supportsContext(isA(Observation.Context.class)); + verify(handler).onStart(same(observation), isA(Observation.Context.class)); + verify(handlerThatHandlesNothing).supportsContext(isA(Observation.Context.class)); verifyNoMoreInteractions(handlerThatHandlesNothing); - try (Timer.Scope scope = sample.makeCurrent()) { - verify(handler).onScopeOpened(same(sample), isA(Timer.HandlerContext.class)); - assertThat(scope.getSample()).isSameAs(sample); + try (Observation.Scope scope = observation.openScope()) { + verify(handler).onScopeOpened(same(observation), isA(Observation.Context.class)); + assertThat(scope.getCurrentObservation()).isSameAs(observation); clock(registry).add(10, TimeUnit.NANOSECONDS); Throwable exception = new IOException("simulated"); - sample.error(exception); - verify(handler).onError(same(sample), isA(Timer.HandlerContext.class), same(exception)); + observation.error(exception); + verify(handler).onError(same(observation), isA(Observation.Context.class)); } - verify(handler).onScopeClosed(same(sample), isA(Timer.HandlerContext.class)); - sample.stop(Timer.builder("myTimer")); + verify(handler).onScopeClosed(same(observation), isA(Observation.Context.class)); + observation.stop(); Timer timer = registry.timer("myTimer"); - verify(handler).onStop(same(sample), isA(Timer.HandlerContext.class), same(timer), eq(Duration.ofNanos(10))); + verify(handler).onStop(same(observation), isA(Observation.Context.class)); clock(registry).add(step()); assertAll(() -> assertEquals(1L, timer.count()), diff --git a/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java b/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java new file mode 100644 index 0000000000..189a8479ca --- /dev/null +++ b/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java @@ -0,0 +1,109 @@ +/* + * Copyright 2021 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.samples; + +import java.io.IOException; +import java.time.Instant; +import java.util.UUID; + +import io.micrometer.api.instrument.Observation; +import io.micrometer.api.instrument.ObservationHandler; +import io.micrometer.api.instrument.Tags; +import io.micrometer.prometheus.PrometheusConfig; +import io.micrometer.prometheus.PrometheusMeterRegistry; + +public class ObservationHandlerSample { + private static final PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + + public static void main(String[] args) throws InterruptedException { + registry.config().observationHandler(new SampleHandler()); + + Observation observation = registry.observation("sample.operation", new CustomContext()) + .displayName("CALL sampleOperation") + .lowCardinalityTag("a", "1") + .highCardinalityTag("time", Instant.now().toString()) + .start(); + try (Observation.Scope scope = observation.openScope()) { + Thread.sleep(1_000); + observation.error(new IOException("simulated")); + } + observation.stop(); + + registry.observation("sample.operation").start().stop(); + registry.observation("sample.operation", new UnsupportedHandlerContext()).start().stop(); + + System.out.println(); + System.out.println(registry.scrape()); + } + + static class SampleHandler implements ObservationHandler { + @Override + public void onStart(Observation observation, CustomContext context) { + System.out.println("start: " + observation); + } + + @Override + public void onError(Observation observation, CustomContext context) { + System.out.println("error: " + observation); + } + + @Override + public void onScopeOpened(Observation observation, CustomContext context) { + System.out.println("context-opened: " + observation); + } + + @Override + public void onScopeClosed(Observation observation, CustomContext context) { + System.out.println("context-closed: " + observation); + } + + @Override + public void onStop(Observation observation, CustomContext context) { + System.out.println("stop: " + observation); + } + + @Override + public boolean supportsContext(Observation.Context context) { + return context instanceof CustomContext; + } + } + + static class CustomContext extends Observation.Context { + private final UUID uuid = UUID.randomUUID(); + + @Override + public Tags getLowCardinalityTags() { + return Tags.of("status", "ok"); + } + + @Override + public Tags getHighCardinalityTags() { + return Tags.of("userId", uuid.toString()); + } + + @Override + public String toString() { + return "CustomHandlerContext{" + uuid + '}'; + } + } + + static class UnsupportedHandlerContext extends Observation.Context { + @Override + public String toString() { + return "sorry"; + } + } +} diff --git a/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/TimerRecordingHandlerSample.java b/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/TimerRecordingHandlerSample.java deleted file mode 100644 index e1e12ba427..0000000000 --- a/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/TimerRecordingHandlerSample.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2021 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.samples; - -import java.io.IOException; -import java.time.Duration; -import java.util.UUID; - -import io.micrometer.api.instrument.Tags; -import io.micrometer.api.instrument.Timer; -import io.micrometer.api.instrument.TimerRecordingHandler; -import io.micrometer.api.lang.Nullable; -import io.micrometer.prometheus.PrometheusConfig; -import io.micrometer.prometheus.PrometheusMeterRegistry; - -public class TimerRecordingHandlerSample { - private static final PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); - - public static void main(String[] args) throws InterruptedException { - registry.config().timerRecordingHandler(new SampleHandler()); - Timer.Builder timerBuilder = Timer.builder("sample.timer").tag("a", "1"); - - Timer.Sample sample = Timer.start(registry, new CustomHandlerContext()); - try (Timer.Scope scope = sample.makeCurrent()) { - Thread.sleep(1_000); - sample.error(new IOException("simulated")); - } - sample.stop(timerBuilder); - - Timer.start(registry).stop(timerBuilder); - Timer.start(registry, new UnsupportedHandlerContext()).stop(timerBuilder); - - System.out.println(); - System.out.println(registry.scrape()); - } - - static class SampleHandler implements TimerRecordingHandler { - @Override - public void onStart(Timer.Sample sample, @Nullable CustomHandlerContext context) { - System.out.println("start: " + sample + " " + context); - } - - @Override - public void onError(Timer.Sample sample, @Nullable CustomHandlerContext context, Throwable throwable) { - System.out.println("error: " + throwable + " " + sample + " " + context); - } - - @Override - public void onStop(Timer.Sample sample, @Nullable CustomHandlerContext context, Timer timer, Duration duration) { - System.out.println("stop: " + duration + " " + toString(timer) + " " + sample + " " + context); - } - - @Override - public boolean supportsContext(@Nullable Timer.HandlerContext handlerContext) { - return handlerContext instanceof CustomHandlerContext; - } - - private String toString(Timer timer) { - return timer.getId().getName() + " " + timer.getId().getTags(); - } - - } - - static class CustomHandlerContext extends Timer.HandlerContext { - private final UUID uuid = UUID.randomUUID(); - - @Override - public Tags getLowCardinalityTags() { - return Tags.of("status", "ok"); - } - - @Override - public Tags getHighCardinalityTags() { - return Tags.of("userId", uuid.toString()); - } - - @Override - public String toString() { - return "CustomHandlerContext{" + - "uuid=" + uuid + ", " + - getAllTags() + - '}'; - } - } - - static class UnsupportedHandlerContext extends Timer.HandlerContext { - @Override - public String toString() { - return "sorry"; - } - } -} From 55e7fef87953d87ba350f7546087511c53b492d5 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 1 Feb 2022 14:50:58 +0100 Subject: [PATCH 2/6] Changes following a discussion with Tommy * We moved out all the getters - this API will provide a nice way to set things ON THE CONTEXT * We've added additionalLowCardinality and high cardinliaty tags on the context, tagsprovider is immutable * We removed timing information - there's no gain in unifying the time (e.g. same time for metrics & spans). It's up to the handlers to take control of doing measurements. If a handler is buggy we will see that in its timing information. * We removed the throwable getter to explicitly pass the throwable to the handler - that's the only parameter that will never change (onError - you have to have an error there) --- .../api/instrument/MeterRegistry.java | 36 --- .../api/instrument/NoopObservation.java | 57 +---- .../api/instrument/Observation.java | 95 -------- .../api/instrument/SimpleObservation.java | 208 ------------------ .../api/instrument/TagsProvider.java | 35 ++- .../instrument/observation/Observation.java | 196 +++++++++++++++++ .../{ => observation}/ObservationHandler.java | 12 +- .../observation/ObservationRegistry.java | 135 ++++++++++++ .../observation/SimpleObservation.java | 130 +++++++++++ .../SimpleObservationRegistry.java | 43 ++++ .../observation/TimerObservationHandler.java | 49 +++++ .../observation/TimerObservationRegistry.java | 27 +++ .../context/HttpClientHandlerContext.java | 2 +- .../http/context/HttpHandlerContext.java | 2 +- .../context/HttpServerHandlerContext.java | 2 +- .../api/instrument/MeterRegistryTest.java | 26 --- ...ngCompositeTimerRecordingHandlerTests.java | 47 ++-- .../CurrentObservationTest.java | 6 +- ...hingCompositeObservationHandlerTests.java} | 53 +++-- .../{ => observation}/HandlerContextTest.java | 9 +- .../observation/ObservationRegistryTest.java | 81 +++++++ ...TimerRecordingHandlerCompatibilityKit.java | 15 +- ...xtObservationHandlerCompatibilityKit.java} | 29 ++- .../core/tck/MeterRegistryAssert.java | 15 -- .../tck/MeterRegistryCompatibilityKit.java | 40 +--- ...TimerRecordingHandlerCompatibilityKit.java | 25 +-- .../core/tck/ObservationRegistryAssert.java | 79 +++++++ .../ObservationRegistryCompatibilityKit.java | 97 ++++++++ ...RecordingHandlerCompatibilityKitTests.java | 20 +- ...servationHandlerCompatibilityKitTests.java | 61 +++++ ...RecordingHandlerCompatibilityKitTests.java | 63 ------ .../core/tck/MeterRegistryAssertTests.java | 35 +-- ...RecordingHandlerCompatibilityKitTests.java | 20 +- .../tck/ObservationRegistryAssertTests.java | 49 +++++ ...ervationRegistryCompatibilityKitTests.java | 42 ++++ .../samples/ObservationHandlerSample.java | 35 ++- 36 files changed, 1177 insertions(+), 699 deletions(-) delete mode 100644 micrometer-api/src/main/java/io/micrometer/api/instrument/Observation.java delete mode 100644 micrometer-api/src/main/java/io/micrometer/api/instrument/SimpleObservation.java create mode 100644 micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java rename micrometer-api/src/main/java/io/micrometer/api/instrument/{ => observation}/ObservationHandler.java (94%) create mode 100644 micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationRegistry.java create mode 100644 micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java create mode 100644 micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservationRegistry.java create mode 100644 micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationHandler.java create mode 100644 micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationRegistry.java rename micrometer-api/src/test/java/io/micrometer/api/instrument/{ => observation}/AllMatchingCompositeTimerRecordingHandlerTests.java (65%) rename micrometer-api/src/test/java/io/micrometer/api/instrument/{ => observation}/CurrentObservationTest.java (94%) rename micrometer-api/src/test/java/io/micrometer/api/instrument/{FirstMatchingCompositeTimerRecordingHandlerTests.java => observation/FirstMatchingCompositeObservationHandlerTests.java} (58%) rename micrometer-api/src/test/java/io/micrometer/api/instrument/{ => observation}/HandlerContextTest.java (92%) create mode 100644 micrometer-api/src/test/java/io/micrometer/api/instrument/observation/ObservationRegistryTest.java rename micrometer-test/src/main/java/io/micrometer/core/tck/{ConcreteHandlerContextTimerRecordingHandlerCompatibilityKit.java => ConcreteHandlerContextObservationHandlerCompatibilityKit.java} (68%) create mode 100644 micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryAssert.java create mode 100644 micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java create mode 100644 micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKitTests.java delete mode 100644 micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextTimerRecordingHandlerCompatibilityKitTests.java create mode 100644 micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryAssertTests.java create mode 100644 micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKitTests.java diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/MeterRegistry.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/MeterRegistry.java index 2fca7b584b..34569af500 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/MeterRegistry.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/MeterRegistry.java @@ -79,13 +79,11 @@ public abstract class MeterRegistry { protected final Clock clock; private final Object meterMapLock = new Object(); private volatile MeterFilter[] filters = new MeterFilter[0]; - private final List> observationHandlers = new CopyOnWriteArrayList<>(); private final List> meterAddedListeners = new CopyOnWriteArrayList<>(); private final List> meterRemovedListeners = new CopyOnWriteArrayList<>(); private final List> meterRegistrationFailedListeners = new CopyOnWriteArrayList<>(); private final Config config = new Config(); private final More more = new More(); - private final ThreadLocal localObservation = new ThreadLocal<>(); // Even though writes are guarded by meterMapLock, iterators across value space are supported // Hence, we use CHM to support that iteration without ConcurrentModificationException risk @@ -116,15 +114,6 @@ protected MeterRegistry(Clock clock) { this.clock = clock; } - @Nullable - public Observation getCurrentObservation() { - return this.localObservation.get(); - } - - void setCurrentObservation(@Nullable Observation current) { - this.localObservation.set(current); - } - /** * Build a new gauge to be added to the registry. This is guaranteed to only be called if the gauge doesn't already exist. * @@ -568,14 +557,6 @@ public > T gaugeCollectionSize(String name, Iterable M registerMeterIfNecessary(Class meterClass, Meter.Id id, Function builder, Function noopBuilder) { return registerMeterIfNecessary(meterClass, id, null, (id2, conf) -> builder.apply(id2), noopBuilder); @@ -818,23 +799,6 @@ public Config onMeterRegistrationFailed(BiConsumer meterRegistration return this; } - /** - * Register a handler for the {@link Observation observations}. - * - * @param handler handler to add to the current configuration - * @return This configuration instance - * @since 2.0.0 - */ - public Config observationHandler(ObservationHandler handler) { - observationHandlers.add(handler); - return this; - } - - // package-private for minimal visibility - Collection> getObservationHandlers() { - return observationHandlers; - } - /** * Use the provided naming convention, overriding the default for your monitoring system. * diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java index 221cdb8278..3505914438 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java @@ -3,6 +3,7 @@ import java.time.Duration; import java.util.Collections; +import io.micrometer.api.instrument.observation.Observation; import io.micrometer.api.lang.Nullable; public class NoopObservation implements Observation { @@ -12,36 +13,11 @@ public class NoopObservation implements Observation { private NoopObservation() { } - @Override - public String getName() { - return "noop"; - } - - @Override - public String getDescription() { - return "noop"; - } - - @Override - public Observation description(String description) { - return this; - } - - @Override - public String getDisplayName() { - return "noop"; - } - @Override public Observation displayName(String displayName) { return this; } - @Override - public Iterable getLowCardinalityTags() { - return TAGS; - } - @Override public Observation lowCardinalityTag(Tag tag) { return this; @@ -52,11 +28,6 @@ public Observation lowCardinalityTag(String key, String value) { return this; } - @Override - public Iterable getHighCardinalityTags() { - return TAGS; - } - @Override public Observation highCardinalityTag(Tag tag) { return this; @@ -67,37 +38,11 @@ public Observation highCardinalityTag(String key, String value) { return this; } - @Nullable - @Override - public Throwable getError() { - return null; - } - @Override public Observation error(Throwable error) { return this; } - @Override - public Duration getDuration() { - return Duration.ZERO; - } - - @Override - public long getStartNanos() { - return 0; - } - - @Override - public long getStopNanos() { - return 0; - } - - @Override - public long getStartWallTime() { - return 0; - } - @Override public Observation start() { return this; diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/Observation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/Observation.java deleted file mode 100644 index 52e6fed3cd..0000000000 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/Observation.java +++ /dev/null @@ -1,95 +0,0 @@ -package io.micrometer.api.instrument; - -import java.time.Duration; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - -import io.micrometer.api.lang.Nullable; - -public interface Observation { - - String getName(); - - default String getDescription() { - return ""; - } - - default Observation description(String description) { - return this; - } - - String getDisplayName(); - - Observation displayName(String displayName); - - Iterable getLowCardinalityTags(); - - Observation lowCardinalityTag(Tag tag); - - default Observation lowCardinalityTag(String key, String value) { - return lowCardinalityTag(Tag.of(key, value)); - } - - Iterable getHighCardinalityTags(); - - Observation highCardinalityTag(Tag tag); - - default Observation highCardinalityTag(String key, String value) { - return highCardinalityTag(Tag.of(key, value)); - } - - @Nullable Throwable getError(); - - Observation error(Throwable error); - - Duration getDuration(); - - long getStartNanos(); - - long getStopNanos(); - - long getStartWallTime(); - - Observation start(); - - void stop(); - - Scope openScope(); - - interface Scope extends AutoCloseable { - @Nullable Observation getCurrentObservation(); - - @Override void close(); - } - - @SuppressWarnings("unchecked") - class Context implements TagsProvider { - private final Map, Object> map = new HashMap<>(); - - public Context put(Class clazz, T object) { - this.map.put(clazz, object); - return this; - } - - public void remove(Class clazz) { - this.map.remove(clazz); - } - - @Nullable public T get(Class clazz) { - return (T) this.map.get(clazz); - } - - public T getOrDefault(Class clazz, T defaultObject) { - return (T) this.map.getOrDefault(clazz, defaultObject); - } - - public T computeIfAbsent(Class clazz, Function, ? extends T> mappingFunction) { - return (T) this.map.computeIfAbsent(clazz, mappingFunction); - } - - public void clear() { - this.map.clear(); - } - } -} diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/SimpleObservation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/SimpleObservation.java deleted file mode 100644 index 657c315a01..0000000000 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/SimpleObservation.java +++ /dev/null @@ -1,208 +0,0 @@ -package io.micrometer.api.instrument; - -import java.time.Duration; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.stream.Collectors; - -import io.micrometer.api.lang.Nullable; - -public class SimpleObservation implements Observation { - private final String name; - private final MeterRegistry registry; - private final Clock clock; - private String description; - private String displayName; - private final Set lowCardinalityTags = new LinkedHashSet<>(); // We can add cardinality to the Tag interface (we did before) so that we can have just one Set tags - private final Set highCardinalityTags = new LinkedHashSet<>(); // We can also use the Tags class, but it is an immutable collection which copies everything over to a new instance when you want to add something - @Nullable private Throwable error; - private Duration duration = Duration.ZERO; - private long started = 0; - private long stopped = 0; - private long startWallTime = 0; - private final Context context; - @SuppressWarnings("rawtypes") - private final Collection handlers; - - public SimpleObservation(String name, MeterRegistry registry) { - this(name, registry, new Context()); - } - - public SimpleObservation(String name, MeterRegistry registry, Context context) { - this.name = name; - this.registry = registry; - this.clock = registry.config().clock(); - this.description = ""; - this.displayName = name; - this.context = context; - this.handlers = registry.config().getObservationHandlers().stream() - .filter(handler -> handler.supportsContext(this.context)) - .collect(Collectors.toList()); - } - - @Override - public String getName() { - return this.name; - } - - @Override - public String getDescription() { - return description; - } - - @Override - public Observation description(String description) { - this.description = description; - return this; - } - - @Override - public String getDisplayName() { - return this.displayName; - } - - @Override - public Observation displayName(String displayName) { - this.displayName = displayName; - return this; - } - - @Override - public Iterable getLowCardinalityTags() { - return this.context.getLowCardinalityTags().and(this.lowCardinalityTags); - } - - @Override - public Observation lowCardinalityTag(Tag tag) { - this.lowCardinalityTags.add(tag); - return this; - } - - @Override - public Iterable getHighCardinalityTags() { - return this.context.getHighCardinalityTags().and(this.highCardinalityTags); - } - - @Override - public Observation highCardinalityTag(Tag tag) { - this.highCardinalityTags.add(tag); - return this; - } - - @Nullable - @Override - public Throwable getError() { - return this.error; - } - - @Override - public Observation error(Throwable error) { - this.error = error; - this.notifyOnError(); - return this; - } - - @Override - public Duration getDuration() { - return this.duration; - } - - @Override - public long getStartNanos() { - return this.started; - } - - @Override - public long getStopNanos() { - return this.stopped; - } - - @Override - public long getStartWallTime() { - return this.startWallTime; - } - - @Override - public Observation start() { - this.startWallTime = this.clock.wallTime(); - this.started = this.clock.monotonicTime(); - this.notifyOnObservationStarted(); - return this; - } - - @Override - public void stop() { - this.stopped = this.clock.monotonicTime(); - this.duration = Duration.ofNanos(this.stopped - this.started); - this.notifyOnObservationStopped(); - } - - @Override - public Scope openScope() { - Scope scope = new SimpleScope(this.registry, this); - this.notifyOnScopeOpened(); - return scope; - } - - @Override - public String toString() { - return "{" - + "name=" + this.getName() + "(" + this.getDisplayName() + ")" - + ", duration=" + this.getDuration().toMillis() + "ms" - + ", lowCardTags=" + this.getLowCardinalityTags() - + ", highCardTags=" + this.getHighCardinalityTags() - + ", error=" + this.getError() - + ", context=" + this.context - + '}'; - } - - @SuppressWarnings("unchecked") - private void notifyOnObservationStarted() { - this.handlers.forEach(handler -> handler.onStart(this, this.context)); - } - - @SuppressWarnings("unchecked") - private void notifyOnError() { - this.handlers.forEach(handler -> handler.onError(this, this.context)); - } - - @SuppressWarnings("unchecked") - private void notifyOnScopeOpened() { - this.handlers.forEach(handler -> handler.onScopeOpened(this, this.context)); - } - - @SuppressWarnings("unchecked") - private void notifyOnScopeClosed() { - this.handlers.forEach(handler -> handler.onScopeClosed(this, this.context)); - } - - @SuppressWarnings("unchecked") - private void notifyOnObservationStopped() { - this.handlers.forEach(handler -> handler.onStop(this, this.context)); - } - - static class SimpleScope implements Scope { - private final MeterRegistry registry; - private final SimpleObservation currentObservation; - @Nullable private final Observation previousObservation; - - SimpleScope(MeterRegistry registry, SimpleObservation current) { - this.registry = registry; - this.currentObservation = current; - this.previousObservation = registry.getCurrentObservation(); - this.registry.setCurrentObservation(current); - } - - @Override - public Observation getCurrentObservation() { - return this.currentObservation; - } - - @Override - public void close() { - this.registry.setCurrentObservation(previousObservation); - this.currentObservation.notifyOnScopeClosed(); - } - } -} diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/TagsProvider.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/TagsProvider.java index de9b79fdd8..f64d91910c 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/TagsProvider.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/TagsProvider.java @@ -23,15 +23,48 @@ */ public interface TagsProvider { + /** + * Default low cardinality tags. + * + * @return tags + */ default Tags getLowCardinalityTags() { return Tags.empty(); } + /** + * Default high cardinality tags. + * + * @return tags + */ default Tags getHighCardinalityTags() { return Tags.empty(); } + /** + * Default low cardinality tags. + * + * @return tags + */ default Tags getAllTags() { - return Tags.concat(getLowCardinalityTags(), getHighCardinalityTags()); + return Tags.concat(getLowCardinalityTags(), getHighCardinalityTags()).and(getAdditionalLowCardinalityTags()).and(getAdditionalHighCardinalityTags()); + } + + /** + * Additional to the default low cardinality tags. Can be set at runtime. + * + * @return tags + */ + default Tags getAdditionalLowCardinalityTags() { + return Tags.empty(); + } + + /** + * Additional to the default high cardinality tags. Can be set at runtime. + * + * @return tags + */ + default Tags getAdditionalHighCardinalityTags() { + return Tags.empty(); } } diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java new file mode 100644 index 0000000000..fac7b1a5da --- /dev/null +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java @@ -0,0 +1,196 @@ +package io.micrometer.api.instrument.observation; + +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import io.micrometer.api.instrument.Tag; +import io.micrometer.api.instrument.Tags; +import io.micrometer.api.instrument.TagsProvider; +import io.micrometer.api.lang.Nullable; + +/* + +We moved out all the getters - this API will provide a nice way to set things ON THE CONTEXT +* We've added additionalLowCardinality and high cardinliaty tags on the context, tagsprovider is immutable +We remove timing information - there's no gain in unifying the time (e.g. same time for metrics & spans). It's up to the handlers +to take control of doing measurements. If a handler is buggy we will see that in its timing information. +We removed the throwable getter to explicitly pass the throwable to the handler - that's the only parameter that will never change (onError - you have to have an error there) + */ +public interface Observation { + + /** + * Sets the display name (a more human-readable name). + * + * @param displayName display name + * @return this + */ + Observation displayName(String displayName); + + /** + * Sets an additional low cardinality tag. + * + * @param tag tag + * @return this + */ + Observation lowCardinalityTag(Tag tag); + + /** + * Sets an additional low cardinality tag. + * + * @param key tag key + * @param value tag value + * @return this + */ + default Observation lowCardinalityTag(String key, String value) { + return lowCardinalityTag(Tag.of(key, value)); + } + + /** + * Sets an additional high cardinality tag. + * + * @param tag tag + * @return this + */ + Observation highCardinalityTag(Tag tag); + + /** + * Sets an additional high cardinality tag. + * + * @param key tag key + * @param value tag value + * @return this + */ + default Observation highCardinalityTag(String key, String value) { + return highCardinalityTag(Tag.of(key, value)); + } + + /** + * Sets an error. + * + * @param error error + * @return this + */ + Observation error(Throwable error); + + /** + * Starts the observation. Remember to call this method, otherwise + * timing calculations will not take place. + * + * @return this + */ + Observation start(); + + /** + * Stop the observation. Remember to call this method, otherwise + * timing calculations won't be finished. + */ + void stop(); + + /** + * When put in scope, additional operations can take place by the + * {@link ObservationHandler}s such as putting entries in thread local. + * + * @return new scope + */ + Scope openScope(); + + interface Scope extends AutoCloseable { + @Nullable Observation getCurrentObservation(); + + @Override void close(); + } + + @SuppressWarnings("unchecked") + class Context implements TagsProvider { + private final Map, Object> map = new HashMap<>(); + + private String name; + + private String displayName; + + private final Set additionalLowCardinalityTags = new LinkedHashSet<>(); + + private final Set additionalHighCardinalityTags = new LinkedHashSet<>(); + + public Context put(Class clazz, T object) { + this.map.put(clazz, object); + return this; + } + + public String getName() { + return this.name; + } + + public Context setName(String name) { + this.name = name; + return this; + } + + public String getDisplayName() { + return this.displayName; + } + + public Context setDisplayName(String displayName) { + this.displayName = displayName; + return this; + } + + public void remove(Class clazz) { + this.map.remove(clazz); + } + + @Nullable public T get(Class clazz) { + return (T) this.map.get(clazz); + } + + public boolean containsKey(Class clazz) { + return this.map.containsKey(clazz); + } + + public T getOrDefault(Class clazz, T defaultObject) { + return (T) this.map.getOrDefault(clazz, defaultObject); + } + + public T computeIfAbsent(Class clazz, Function, ? extends T> mappingFunction) { + return (T) this.map.computeIfAbsent(clazz, mappingFunction); + } + + public void clear() { + this.map.clear(); + } + + public void addLowCardinalityTag(Tag tag) { + this.additionalLowCardinalityTags.add(tag); + } + + public void addHighCardinalityTag(Tag tag) { + this.additionalHighCardinalityTags.add(tag); + } + + @Override + public Tags getAdditionalLowCardinalityTags() { + return Tags.of(this.additionalLowCardinalityTags); + } + + @Override + public Tags getAdditionalHighCardinalityTags() { + return Tags.of(this.additionalHighCardinalityTags); + } + + @Override + public String toString() { + return "Context{" + + "map=" + map + + ", name='" + name + '\'' + + ", displayName='" + displayName + '\'' + + ", lowCardinalityTags=" + getLowCardinalityTags() + + ", additionalLowCardinalityTags=" + additionalLowCardinalityTags + + ", highCardinalityTags=" + getHighCardinalityTags() + + ", additionalHighCardinalityTags=" + additionalHighCardinalityTags + + '}'; + } + } +} diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/ObservationHandler.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationHandler.java similarity index 94% rename from micrometer-api/src/main/java/io/micrometer/api/instrument/ObservationHandler.java rename to micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationHandler.java index 781d74817b..08607a37f5 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/ObservationHandler.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationHandler.java @@ -1,4 +1,4 @@ -package io.micrometer.api.instrument; +package io.micrometer.api.instrument.observation; import java.util.Arrays; import java.util.List; @@ -8,7 +8,7 @@ public interface ObservationHandler { void onStart(Observation observation, T context); - void onError(Observation observation, T context); + void onError(Observation observation, T context, Throwable throwable); default void onScopeOpened(Observation observation, T context) { } @@ -67,8 +67,8 @@ public void onStart(Observation observation, Observation.Context context) { } @Override - public void onError(Observation observation, Observation.Context context) { - getFirstApplicableHandler(context).ifPresent(handler -> handler.onError(observation, context)); + public void onError(Observation observation, Observation.Context context, Throwable throwable) { + getFirstApplicableHandler(context).ifPresent(handler -> handler.onError(observation, context, throwable)); } @Override @@ -130,8 +130,8 @@ public void onStart(Observation observation, Observation.Context context) { } @Override - public void onError(Observation observation, Observation.Context context) { - getAllApplicableHandlers(context).forEach(handler -> handler.onError(observation, context)); + public void onError(Observation observation, Observation.Context context, Throwable throwable) { + getAllApplicableHandlers(context).forEach(handler -> handler.onError(observation, context, throwable)); } @Override diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationRegistry.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationRegistry.java new file mode 100644 index 0000000000..a6e251c83c --- /dev/null +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationRegistry.java @@ -0,0 +1,135 @@ +/* + * 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.api.instrument.observation; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.BiPredicate; + +import io.micrometer.api.instrument.MeterRegistry; +import io.micrometer.api.instrument.NoopObservation; +import io.micrometer.api.lang.Nullable; + +/** + */ +public interface ObservationRegistry { + @Nullable + Observation getCurrentObservation(); + + void setCurrentObservation(@Nullable Observation current); + + /** + * !!!!!!!!!!!!!!!!!!!! THIS IS NOT STARTED !!!!!!!!!!!!!!!!!!!! + * !!!!!!!!!!!!!!!!!!!! REMEMBER TO CALL START() OTHERWISE YOU WILL FILE ISSUES THAT STUFF IS NOT WORKING !!!!!!!!!!!!!!!!!!!! + * @param name observation name + * @return this + */ + default Observation observation(String name) { + if (config().isObservationEnabled(name, null)) { + return new SimpleObservation(name, this, new Observation.Context()); + } + return NoopObservation.INSTANCE; + } + + /** + * !!!!!!!!!!!!!!!!!!!! THIS IS NOT STARTED !!!!!!!!!!!!!!!!!!!! + * !!!!!!!!!!!!!!!!!!!! REMEMBER TO CALL START() OTHERWISE YOU WILL FILE ISSUES THAT STUFF IS NOT WORKING !!!!!!!!!!!!!!!!!!!! + * @param name observation name + * @param context context + * @return this + */ + default Observation observation(String name, Observation.Context context) { + if (config().isObservationEnabled(name, context)) { + return new SimpleObservation(name, this, context); + } + return NoopObservation.INSTANCE; + } + + /** + * Started observation. + * + * @param name observation name + * @return this + */ + default Observation start(String name) { + return observation(name).start(); + } + + /** + * Started observation. + * + * @param name observation name + * @param context context + * @return this + */ + default Observation start(String name, Observation.Context context) { + return observation(name, context).start(); + } + + Config config(); + + /** + * Access to configuration options for this registry. + */ + class Config { + + private List> observationHandlers = new CopyOnWriteArrayList<>(); + + private List> observationPredicates = new CopyOnWriteArrayList<>(); + + /** + * Register a handler for the {@link Observation observations}. + * + * @param handler handler to add to the current configuration + * @return This configuration instance + * @since 2.0.0 + */ + public ObservationRegistry.Config observationHandler(ObservationHandler handler) { + this.observationHandlers.add(handler); + return this; + } + + /** + * Register a predicate to define whether {@link Observation observation} should be created or a + * {@link NoopObservation} instead. + * + * @param predicate predicate + * @return This configuration instance + * @since 2.0.0 + */ + public ObservationRegistry.Config observationPredicate(BiPredicate predicate) { + this.observationPredicates.add(predicate); + return this; + } + + /** + * Check to assert whether {@link Observation} should be created or {@link NoopObservation} instead. + * + * @param name observation technical name + * @param context context + * @return {@code true} when observation is enabled + */ + public boolean isObservationEnabled(String name, @Nullable Observation.Context context) { + return this.observationPredicates.stream().allMatch(predicate -> predicate.test(name, context)); + } + + // package-private for minimal visibility + Collection> getObservationHandlers() { + return observationHandlers; + } + } +} diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java new file mode 100644 index 0000000000..c009c8bc58 --- /dev/null +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java @@ -0,0 +1,130 @@ +package io.micrometer.api.instrument.observation; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Deque; +import java.util.stream.Collectors; + +import io.micrometer.api.instrument.Tag; +import io.micrometer.api.lang.Nullable; + +public class SimpleObservation implements Observation { + private final ObservationRegistry registry; + @Nullable private Throwable error; + private final Context context; + @SuppressWarnings("rawtypes") + private final Deque handlers; + + public SimpleObservation(String name, ObservationRegistry registry) { + this(name, registry, new Context()); + } + + public SimpleObservation(String name, ObservationRegistry registry, Context context) { + this.registry = registry; + this.context = context.setName(name); + this.handlers = registry.config().getObservationHandlers().stream() + .filter(handler -> handler.supportsContext(this.context)) + .collect(Collectors.toCollection(ArrayDeque::new)); + } + + @Override + public Observation displayName(String displayName) { + this.context.setDisplayName(displayName); + return this; + } + + @Override + public Observation lowCardinalityTag(Tag tag) { + this.context.addLowCardinalityTag(tag); + return this; + } + + @Override + public Observation highCardinalityTag(Tag tag) { + this.context.addHighCardinalityTag(tag); + return this; + } + + @Override + public Observation error(Throwable error) { + this.error = error; + this.notifyOnError(); + return this; + } + @Override + public Observation start() { + this.notifyOnObservationStarted(); + return this; + } + + @Override + public void stop() { + this.notifyOnObservationStopped(); + } + + @Override + public Scope openScope() { + Scope scope = new SimpleScope(this.registry, this); + this.notifyOnScopeOpened(); + return scope; + } + + @Override + public String toString() { + return "{" + + "name=" + this.context.getName() + "(" + this.context.getDisplayName() + ")" + + ", error=" + this.error + + ", context=" + this.context + + '}'; + } + + @SuppressWarnings("unchecked") + private void notifyOnObservationStarted() { + this.handlers.forEach(handler -> handler.onStart(this, this.context)); + } + + @SuppressWarnings("unchecked") + private void notifyOnError() { + this.handlers.forEach(handler -> handler.onError(this, this.context, this.error)); + } + + @SuppressWarnings("unchecked") + private void notifyOnScopeOpened() { + this.handlers.forEach(handler -> handler.onScopeOpened(this, this.context)); + } + + @SuppressWarnings("unchecked") + private void notifyOnScopeClosed() { + this.handlers.forEach(handler -> handler.onScopeClosed(this, this.context)); + } + + @SuppressWarnings("unchecked") + private void notifyOnObservationStopped() { + // TODO: We're closing from end till beggining - e.g. we started with handlers with ids 1,2,3 and we need to call close on 3,2,1 + this.handlers.descendingIterator().forEachRemaining(handler -> handler.onStop(this, this.context)); + } + + static class SimpleScope implements Scope { + private final ObservationRegistry registry; + private final SimpleObservation currentObservation; + @Nullable private final Observation previousObservation; + + SimpleScope(ObservationRegistry registry, SimpleObservation current) { + this.registry = registry; + this.currentObservation = current; + this.previousObservation = registry.getCurrentObservation(); + this.registry.setCurrentObservation(current); + } + + @Override + public Observation getCurrentObservation() { + return this.currentObservation; + } + + @Override + public void close() { + this.registry.setCurrentObservation(previousObservation); + this.currentObservation.notifyOnScopeClosed(); + } + } +} diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservationRegistry.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservationRegistry.java new file mode 100644 index 0000000000..5d6ebb0967 --- /dev/null +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservationRegistry.java @@ -0,0 +1,43 @@ +/* + * 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.api.instrument.observation; + +import io.micrometer.api.lang.Nullable; + +/** + */ +public class SimpleObservationRegistry implements ObservationRegistry { + private final ThreadLocal localObservation = new ThreadLocal<>(); + + private final Config config = new Config(); + + @Nullable + @Override + public Observation getCurrentObservation() { + return this.localObservation.get(); + } + + @Override + public void setCurrentObservation(@Nullable Observation current) { + this.localObservation.set(current); + } + + @Override + public Config config() { + return this.config; + } + +} diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationHandler.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationHandler.java new file mode 100644 index 0000000000..9b30a52444 --- /dev/null +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationHandler.java @@ -0,0 +1,49 @@ +package io.micrometer.api.instrument.observation; + +import io.micrometer.api.instrument.MeterRegistry; +import io.micrometer.api.instrument.Tag; +import io.micrometer.api.instrument.Tags; +import io.micrometer.api.instrument.Timer; + +/** + * Handler for {@link Timer.Sample}. + * + * @author Marcin Grzejszczak + * @since 2.0.0 + */ +public class TimerObservationHandler implements ObservationHandler { + + private final MeterRegistry meterRegistry; + + public TimerObservationHandler(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + @Override + public void onStart(Observation observation, Observation.Context context) { + Timer.Sample sample = Timer.start(meterRegistry); + context.put(Timer.Sample.class, sample); + } + + @Override + public void onError(Observation observation, Observation.Context context, Throwable throwable) { + context.put(Throwable.class, throwable); + } + + @Override + public void onStop(Observation observation, Observation.Context context) { + Timer.Sample sample = context.get(Timer.Sample.class); + Tags tags = context.getLowCardinalityTags().and(context.getAdditionalLowCardinalityTags()); + if (context.containsKey(Throwable.class)) { + tags = tags.and(Tag.of("error", context.get(Throwable.class).getLocalizedMessage())); + } + sample.stop(Timer.builder(context.getName()) + .tags(tags) + .register(this.meterRegistry)); + } + + @Override + public boolean supportsContext(Observation.Context context) { + return true; + } +} diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationRegistry.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationRegistry.java new file mode 100644 index 0000000000..6ddff20a81 --- /dev/null +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationRegistry.java @@ -0,0 +1,27 @@ +/* + * 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.api.instrument.observation; + +import io.micrometer.api.instrument.MeterRegistry; + +/** + */ +public class TimerObservationRegistry extends SimpleObservationRegistry { + + public TimerObservationRegistry(MeterRegistry meterRegistry) { + config().observationHandler(new TimerObservationHandler(meterRegistry)); + } +} diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpClientHandlerContext.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpClientHandlerContext.java index b2b25c28db..6126551607 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpClientHandlerContext.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpClientHandlerContext.java @@ -15,7 +15,7 @@ */ package io.micrometer.api.instrument.transport.http.context; -import io.micrometer.api.instrument.Observation; +import io.micrometer.api.instrument.observation.Observation; import io.micrometer.api.instrument.transport.http.tags.HttpTagsProvider; import io.micrometer.api.instrument.transport.http.HttpClientRequest; import io.micrometer.api.instrument.transport.http.HttpClientResponse; diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpHandlerContext.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpHandlerContext.java index a380b4cc5a..100255608c 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpHandlerContext.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpHandlerContext.java @@ -15,7 +15,7 @@ */ package io.micrometer.api.instrument.transport.http.context; -import io.micrometer.api.instrument.Observation; +import io.micrometer.api.instrument.observation.Observation; import io.micrometer.api.instrument.Tags; import io.micrometer.api.instrument.transport.http.HttpRequest; import io.micrometer.api.instrument.transport.http.HttpResponse; diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpServerHandlerContext.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpServerHandlerContext.java index 884d1ebe08..06f7cbf503 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpServerHandlerContext.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/transport/http/context/HttpServerHandlerContext.java @@ -15,7 +15,7 @@ */ package io.micrometer.api.instrument.transport.http.context; -import io.micrometer.api.instrument.Observation; +import io.micrometer.api.instrument.observation.Observation; import io.micrometer.api.instrument.transport.http.tags.HttpTagsProvider; import io.micrometer.api.lang.NonNull; import io.micrometer.api.instrument.transport.http.HttpServerRequest; diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/MeterRegistryTest.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/MeterRegistryTest.java index eabbbfa2aa..f6490df2b3 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/MeterRegistryTest.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/MeterRegistryTest.java @@ -207,30 +207,4 @@ void shouldNotLetRegisteringMetersTwice() { .hasMessage("There is already a registered meter of a different type (CumulativeCounter vs. Timer) with the same name: my.dupe.meter") .hasNoCause(); } - - @Test - void openingScopeShouldSetSampleAsCurrent() { - Timer.Builder timerBuilder = Timer.builder("test.timer"); - Timer.Sample sample = Timer.start(registry); - Timer.Scope scope = registry.openNewScope(sample); - - assertThat(registry.getCurrentSample()).isSameAs(sample); - - scope.close(); - sample.stop(timerBuilder); - - assertThat(registry.getCurrentSample()).isNull(); - } - - @Test - void timerRecordingHandlerShouldAddThePassedHandler() { - TimerRecordingHandler handler1 = mock(TimerRecordingHandler.class); - TimerRecordingHandler handler2 = mock(TimerRecordingHandler.class); - - registry.config().timerRecordingHandler(handler1); - assertThat(registry.config().getTimerRecordingHandlers()).containsExactly(handler1); - - registry.config().timerRecordingHandler(handler2); - assertThat(registry.config().getTimerRecordingHandlers()).containsExactlyInAnyOrder(handler1, handler2); - } } diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/AllMatchingCompositeTimerRecordingHandlerTests.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/AllMatchingCompositeTimerRecordingHandlerTests.java similarity index 65% rename from micrometer-api/src/test/java/io/micrometer/api/instrument/AllMatchingCompositeTimerRecordingHandlerTests.java rename to micrometer-api/src/test/java/io/micrometer/api/instrument/observation/AllMatchingCompositeTimerRecordingHandlerTests.java index ed13ee920d..2452fe1a78 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/AllMatchingCompositeTimerRecordingHandlerTests.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/AllMatchingCompositeTimerRecordingHandlerTests.java @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micrometer.api.instrument; +package io.micrometer.api.instrument.observation; import java.time.Duration; -import io.micrometer.api.instrument.TimerRecordingHandler.AllMatchingCompositeTimerRecordingHandler; +import io.micrometer.api.instrument.Timer; +import io.micrometer.api.instrument.observation.ObservationHandler.AllMatchingCompositeObservationHandler; import io.micrometer.api.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; @@ -29,11 +30,13 @@ class AllMatchingCompositeTimerRecordingHandlerTests { MatchingHandler matchingHandler2 = new MatchingHandler(); - Timer.Sample sample = Timer.start(new SimpleMeterRegistry()); + ObservationRegistry registry = new SimpleObservationRegistry(); + + Observation sample = registry.start("hello"); @Test void should_run_on_start_for_all_matching_handlers() { - AllMatchingCompositeTimerRecordingHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeTimerRecordingHandler( + AllMatchingCompositeObservationHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler(), this.matchingHandler2); allMatchingCompositeTimerRecordingHandler.onStart(sample, null); @@ -44,10 +47,10 @@ void should_run_on_start_for_all_matching_handlers() { @Test void should_run_on_stop_for_all_matching_handlers() { - AllMatchingCompositeTimerRecordingHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeTimerRecordingHandler( + AllMatchingCompositeObservationHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler(), this.matchingHandler2); - allMatchingCompositeTimerRecordingHandler.onStop(sample, null, null, null); + allMatchingCompositeTimerRecordingHandler.onStop(sample, null); assertThat(this.matchingHandler.stopped).isTrue(); assertThat(this.matchingHandler2.stopped).isTrue(); @@ -55,7 +58,7 @@ void should_run_on_stop_for_all_matching_handlers() { @Test void should_run_on_error_for_all_matching_handlers() { - AllMatchingCompositeTimerRecordingHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeTimerRecordingHandler( + AllMatchingCompositeObservationHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler(), this.matchingHandler2); allMatchingCompositeTimerRecordingHandler.onError(sample, null, new RuntimeException()); @@ -66,7 +69,7 @@ void should_run_on_error_for_all_matching_handlers() { @Test void should_run_on_scope_opened_for_all_matching_handlers() { - AllMatchingCompositeTimerRecordingHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeTimerRecordingHandler( + AllMatchingCompositeObservationHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler(), this.matchingHandler2); allMatchingCompositeTimerRecordingHandler.onScopeOpened(sample, null); @@ -77,7 +80,7 @@ void should_run_on_scope_opened_for_all_matching_handlers() { @Test void should_run_on_scope_closed_for_all_matching_handlers() { - AllMatchingCompositeTimerRecordingHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeTimerRecordingHandler( + AllMatchingCompositeObservationHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler(), this.matchingHandler2); allMatchingCompositeTimerRecordingHandler.onScopeClosed(sample, null); @@ -86,7 +89,7 @@ void should_run_on_scope_closed_for_all_matching_handlers() { assertThat(this.matchingHandler2.scopeClosed).isTrue(); } - static class MatchingHandler implements TimerRecordingHandler { + static class MatchingHandler implements ObservationHandler { boolean started; @@ -100,60 +103,60 @@ static class MatchingHandler implements TimerRecordingHandler { @Override - public void onStart(Timer.Sample sample, Timer.HandlerContext context) { + public void onStart(Observation observation, Observation.Context context) { this.started = true; } @Override - public void onError(Timer.Sample sample, Timer.HandlerContext context, Throwable throwable) { + public void onError(Observation observation, Observation.Context context, Throwable throwable) { this.errored = true; } @Override - public void onScopeOpened(Timer.Sample sample, Timer.HandlerContext context) { + public void onScopeOpened(Observation observation, Observation.Context context) { this.scopeOpened = true; } @Override - public void onScopeClosed(Timer.Sample sample, Timer.HandlerContext context) { + public void onScopeClosed(Observation observation, Observation.Context context) { this.scopeClosed = true; } @Override - public void onStop(Timer.Sample sample, Timer.HandlerContext context, Timer timer, Duration duration) { + public void onStop(Observation observation, Observation.Context context) { this.stopped = true; } @Override - public boolean supportsContext(Timer.HandlerContext handlerContext) { + public boolean supportsContext(Observation.Context handlerContext) { return true; } } - static class NotMatchingHandler implements TimerRecordingHandler { + static class NotMatchingHandler implements ObservationHandler { @Override - public void onStart(Timer.Sample sample, Timer.HandlerContext context) { + public void onStart(Observation observation, Observation.Context context) { throwAssertionError(); } @Override - public void onError(Timer.Sample sample, Timer.HandlerContext context, Throwable throwable) { + public void onError(Observation observation, Observation.Context context, Throwable throwable) { throwAssertionError(); } @Override - public void onScopeOpened(Timer.Sample sample, Timer.HandlerContext context) { + public void onScopeOpened(Observation observation, Observation.Context context) { throwAssertionError(); } @Override - public void onStop(Timer.Sample sample, Timer.HandlerContext context, Timer timer, Duration duration) { + public void onStop(Observation observation, Observation.Context context) { throwAssertionError(); } @Override - public boolean supportsContext(Timer.HandlerContext handlerContext) { + public boolean supportsContext(Observation.Context handlerContext) { return false; } diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/CurrentObservationTest.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/CurrentObservationTest.java similarity index 94% rename from micrometer-api/src/test/java/io/micrometer/api/instrument/CurrentObservationTest.java rename to micrometer-api/src/test/java/io/micrometer/api/instrument/observation/CurrentObservationTest.java index 4143132544..a7b1c7c1c2 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/CurrentObservationTest.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/CurrentObservationTest.java @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micrometer.api.instrument; +package io.micrometer.api.instrument.observation; +import io.micrometer.api.instrument.MeterRegistry; +import io.micrometer.api.instrument.observation.Observation; import io.micrometer.api.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; @@ -25,7 +27,7 @@ import static org.assertj.core.api.Assertions.assertThat; class CurrentObservationTest { - private final MeterRegistry registry = new SimpleMeterRegistry(); + private final ObservationRegistry registry = new SimpleObservationRegistry(); @Test void nestedSamples_parentChildThreadsInstrumented() throws ExecutionException, InterruptedException { diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/FirstMatchingCompositeTimerRecordingHandlerTests.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/FirstMatchingCompositeObservationHandlerTests.java similarity index 58% rename from micrometer-api/src/test/java/io/micrometer/api/instrument/FirstMatchingCompositeTimerRecordingHandlerTests.java rename to micrometer-api/src/test/java/io/micrometer/api/instrument/observation/FirstMatchingCompositeObservationHandlerTests.java index 8e028edb87..6e8dae5729 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/FirstMatchingCompositeTimerRecordingHandlerTests.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/FirstMatchingCompositeObservationHandlerTests.java @@ -13,27 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micrometer.api.instrument; +package io.micrometer.api.instrument.observation; -import java.time.Duration; - -import io.micrometer.api.instrument.Timer; -import io.micrometer.api.instrument.TimerRecordingHandler; -import io.micrometer.api.instrument.TimerRecordingHandler.FirstMatchingCompositeTimerRecordingHandler; -import io.micrometer.api.instrument.simple.SimpleMeterRegistry; +import io.micrometer.api.instrument.observation.ObservationHandler.FirstMatchingCompositeObservationHandler; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -class FirstMatchingCompositeTimerRecordingHandlerTests { +class FirstMatchingCompositeObservationHandlerTests { MatchingHandler matchingHandler = new MatchingHandler(); - Timer.Sample sample = Timer.start(new SimpleMeterRegistry()); + ObservationRegistry registry = new SimpleObservationRegistry(); + + Observation sample = registry.start("hello"); @Test void should_run_on_start_only_for_first_matching_handler() { - FirstMatchingCompositeTimerRecordingHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeTimerRecordingHandler( + FirstMatchingCompositeObservationHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler()); firstMatchingCompositeTimerRecordingHandler.onStart(sample, null); @@ -43,17 +40,17 @@ void should_run_on_start_only_for_first_matching_handler() { @Test void should_run_on_stop_only_for_first_matching_handler() { - FirstMatchingCompositeTimerRecordingHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeTimerRecordingHandler( + FirstMatchingCompositeObservationHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler()); - firstMatchingCompositeTimerRecordingHandler.onStop(sample, null, null, null); + firstMatchingCompositeTimerRecordingHandler.onStop(sample, null); assertThat(this.matchingHandler.stopped).isTrue(); } @Test void should_run_on_error_only_for_first_matching_handler() { - FirstMatchingCompositeTimerRecordingHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeTimerRecordingHandler( + FirstMatchingCompositeObservationHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler()); firstMatchingCompositeTimerRecordingHandler.onError(sample, null, new RuntimeException()); @@ -63,7 +60,7 @@ void should_run_on_error_only_for_first_matching_handler() { @Test void should_run_on_scope_opened_only_for_first_matching_handler() { - FirstMatchingCompositeTimerRecordingHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeTimerRecordingHandler( + FirstMatchingCompositeObservationHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler()); firstMatchingCompositeTimerRecordingHandler.onScopeOpened(sample, null); @@ -73,7 +70,7 @@ void should_run_on_scope_opened_only_for_first_matching_handler() { @Test void should_run_on_scope_closed_only_for_first_matching_handler() { - FirstMatchingCompositeTimerRecordingHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeTimerRecordingHandler( + FirstMatchingCompositeObservationHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler()); firstMatchingCompositeTimerRecordingHandler.onScopeClosed(sample, null); @@ -81,7 +78,7 @@ void should_run_on_scope_closed_only_for_first_matching_handler() { assertThat(this.matchingHandler.scopeClosed).isTrue(); } - static class MatchingHandler implements TimerRecordingHandler { + static class MatchingHandler implements ObservationHandler { boolean started; @@ -95,60 +92,60 @@ static class MatchingHandler implements TimerRecordingHandler { @Override - public void onStart(Timer.Sample sample, Timer.HandlerContext context) { + public void onStart(Observation observation, Observation.Context context) { this.started = true; } @Override - public void onError(Timer.Sample sample, Timer.HandlerContext context, Throwable throwable) { + public void onError(Observation observation, Observation.Context context, Throwable throwable) { this.errored = true; } @Override - public void onScopeOpened(Timer.Sample sample, Timer.HandlerContext context) { + public void onScopeOpened(Observation observation, Observation.Context context) { this.scopeOpened = true; } @Override - public void onScopeClosed(Timer.Sample sample, Timer.HandlerContext context) { + public void onScopeClosed(Observation observation, Observation.Context context) { this.scopeClosed = true; } @Override - public void onStop(Timer.Sample sample, Timer.HandlerContext context, Timer timer, Duration duration) { + public void onStop(Observation observation, Observation.Context context) { this.stopped = true; } @Override - public boolean supportsContext(Timer.HandlerContext handlerContext) { + public boolean supportsContext(Observation.Context handlerContext) { return true; } } - static class NotMatchingHandler implements TimerRecordingHandler { + static class NotMatchingHandler implements ObservationHandler { @Override - public void onStart(Timer.Sample sample, Timer.HandlerContext context) { + public void onStart(Observation observation, Observation.Context context) { throwAssertionError(); } @Override - public void onError(Timer.Sample sample, Timer.HandlerContext context, Throwable throwable) { + public void onError(Observation observation, Observation.Context context, Throwable throwable) { throwAssertionError(); } @Override - public void onScopeOpened(Timer.Sample sample, Timer.HandlerContext context) { + public void onScopeOpened(Observation observation, Observation.Context context) { throwAssertionError(); } @Override - public void onStop(Timer.Sample sample, Timer.HandlerContext context, Timer timer, Duration duration) { + public void onStop(Observation observation, Observation.Context context) { throwAssertionError(); } @Override - public boolean supportsContext(Timer.HandlerContext handlerContext) { + public boolean supportsContext(Observation.Context handlerContext) { return false; } diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/HandlerContextTest.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/HandlerContextTest.java similarity index 92% rename from micrometer-api/src/test/java/io/micrometer/api/instrument/HandlerContextTest.java rename to micrometer-api/src/test/java/io/micrometer/api/instrument/observation/HandlerContextTest.java index 3afbfe7962..a05936c505 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/HandlerContextTest.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/HandlerContextTest.java @@ -13,24 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.micrometer.api.instrument; +package io.micrometer.api.instrument.observation; +import io.micrometer.api.instrument.Timer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link Timer.HandlerContext}. + * Tests for {@link Observation.Context}. * * @author Jonatan Ivanov */ class HandlerContextTest { - private Timer.HandlerContext handlerContext; + private Observation.Context handlerContext; @BeforeEach void setUp() { - this.handlerContext = new Timer.HandlerContext(); + this.handlerContext = new Observation.Context(); } @Test diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/ObservationRegistryTest.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/ObservationRegistryTest.java new file mode 100644 index 0000000000..d8cbe9b0f4 --- /dev/null +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/ObservationRegistryTest.java @@ -0,0 +1,81 @@ +/* + * 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.api.instrument.observation; + +import javax.annotation.Nonnull; + +import io.micrometer.api.instrument.Counter; +import io.micrometer.api.instrument.Meter; +import io.micrometer.api.instrument.MeterRegistry; +import io.micrometer.api.instrument.NoopObservation; +import io.micrometer.api.instrument.Timer; +import io.micrometer.api.instrument.config.MeterFilter; +import io.micrometer.api.instrument.config.MeterFilterReply; +import io.micrometer.api.instrument.distribution.DistributionStatisticConfig; +import io.micrometer.api.instrument.distribution.pause.PauseDetector; +import io.micrometer.api.instrument.noop.NoopCounter; +import io.micrometer.api.instrument.noop.NoopTimer; +import io.micrometer.api.instrument.simple.SimpleMeterRegistry; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link MeterRegistry}. + * + * @author Jon Schneider + * @author Johnny Lim + */ +class ObservationRegistryTest { + private ObservationRegistry registry = new SimpleObservationRegistry(); + + @Test + void openingScopeShouldSetSampleAsCurrent() { + Observation sample = registry.start("test.timer"); + Observation.Scope scope = sample.openScope(); + + assertThat(registry.getCurrentObservation()).isSameAs(sample); + + scope.close(); + sample.stop(); + + assertThat(registry.getCurrentObservation()).isNull(); + } + + @Test + void timerRecordingHandlerShouldAddThePassedHandler() { + ObservationHandler handler1 = mock(ObservationHandler.class); + ObservationHandler handler2 = mock(ObservationHandler.class); + + registry.config().observationHandler(handler1); + assertThat(registry.config().getObservationHandlers()).containsExactly(handler1); + + registry.config().observationHandler(handler2); + assertThat(registry.config().getObservationHandlers()).containsExactlyInAnyOrder(handler1, handler2); + } + + + @Test + void observationShouldBeNoOpWhenPredicateApplicable() { + registry.config().observationPredicate((name, context) -> !name.equals("test.timer")); + + Observation sample = registry.start("test.timer"); + + assertThat(sample).isSameAs(NoopObservation.INSTANCE); + } +} diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKit.java index 0efde50d56..aae858d16f 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKit.java @@ -15,10 +15,8 @@ */ package io.micrometer.core.tck; -import java.time.Duration; - -import io.micrometer.api.instrument.Timer; -import io.micrometer.api.instrument.TimerRecordingHandler; +import io.micrometer.api.instrument.observation.Observation; +import io.micrometer.api.instrument.observation.ObservationHandler; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -26,8 +24,8 @@ import static org.assertj.core.api.Assertions.assertThatCode; /** - * Base class for {@link TimerRecordingHandler} compatibility tests that support any context. - * To run a {@link TimerRecordingHandler} implementation against this TCK, make a test class that extends this + * Base class for {@link ObservationHandler} compatibility tests that support any context. + * To run a {@link ObservationHandler} implementation against this TCK, make a test class that extends this * and implement the abstract methods. * * @author Marcin Grzejszczak @@ -40,15 +38,14 @@ public abstract class AnyHandlerContextTimerRecordingHandlerCompatibilityKit ext void handlerSupportsAnyContext() { TestHandlerContext testContext = new TestHandlerContext(); assertThatCode(() -> handler.onStart(sample, testContext)).doesNotThrowAnyException(); - assertThatCode(() -> handler.onStop(sample, testContext, Timer.builder("timer for test context") - .register(meterRegistry), Duration.ofSeconds(1L))).doesNotThrowAnyException(); + assertThatCode(() -> handler.onStop(sample, testContext)).doesNotThrowAnyException(); assertThatCode(() -> handler.onError(sample, testContext, new RuntimeException())).doesNotThrowAnyException(); assertThatCode(() -> handler.onScopeOpened(sample, testContext)).doesNotThrowAnyException(); assertThatCode(() -> handler.supportsContext(testContext)).doesNotThrowAnyException(); assertThat(handler.supportsContext(testContext)).as("Handler supports any context").isTrue(); } - static class TestHandlerContext extends Timer.HandlerContext { + static class TestHandlerContext extends Observation.Context { } } diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextTimerRecordingHandlerCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKit.java similarity index 68% rename from micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextTimerRecordingHandlerCompatibilityKit.java rename to micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKit.java index e1f3169379..3a3e744d7a 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextTimerRecordingHandlerCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKit.java @@ -15,12 +15,10 @@ */ package io.micrometer.core.tck; -import java.time.Duration; - -import io.micrometer.api.instrument.MeterRegistry; -import io.micrometer.api.instrument.Timer; -import io.micrometer.api.instrument.TimerRecordingHandler; -import io.micrometer.api.instrument.simple.SimpleMeterRegistry; +import io.micrometer.api.instrument.observation.Observation; +import io.micrometer.api.instrument.observation.ObservationHandler; +import io.micrometer.api.instrument.observation.ObservationRegistry; +import io.micrometer.api.instrument.observation.SimpleObservationRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -29,22 +27,22 @@ import static org.assertj.core.api.Assertions.assertThatCode; /** - * Base class for {@link TimerRecordingHandler} compatibility tests that support a concrete type of context only. - * To run a {@link TimerRecordingHandler} implementation against this TCK, make a test class that extends this + * Base class for {@link ObservationHandler} compatibility tests that support a concrete type of context only. + * To run a {@link ObservationHandler} implementation against this TCK, make a test class that extends this * and implement the abstract methods. * * @author Marcin Grzejszczak * @since 2.0.0 */ -public abstract class ConcreteHandlerContextTimerRecordingHandlerCompatibilityKit { +public abstract class ConcreteHandlerContextObservationHandlerCompatibilityKit { - protected TimerRecordingHandler handler; + protected ObservationHandler handler; - protected MeterRegistry meterRegistry = new SimpleMeterRegistry(); + protected ObservationRegistry meterRegistry = new SimpleObservationRegistry(); - public abstract TimerRecordingHandler handler(); + public abstract ObservationHandler handler(); - protected Timer.Sample sample = Timer.start(meterRegistry); + protected Observation sample = meterRegistry.observation("hello"); public abstract T context(); @@ -58,8 +56,7 @@ void setup() { @DisplayName("compatibility test provides a concrete context accepting timer recording handler") void handlerSupportsConcreteContextForHandlerMethods() { assertThatCode(() -> handler.onStart(sample, context())).doesNotThrowAnyException(); - assertThatCode(() -> handler.onStop(sample, context(), Timer.builder("timer for concrete context") - .register(meterRegistry), Duration.ofSeconds(1L))).doesNotThrowAnyException(); + assertThatCode(() -> handler.onStop(sample, context())).doesNotThrowAnyException(); assertThatCode(() -> handler.onError(sample, context(), new RuntimeException())).doesNotThrowAnyException(); assertThatCode(() -> handler.onScopeOpened(sample, context())).doesNotThrowAnyException(); } @@ -76,7 +73,7 @@ void handlerSupportsConcreteContextOnly() { assertThat(handler.supportsContext(new NotMatchingHandlerContext())).as("Handler supports only concrete context").isFalse(); } - static class NotMatchingHandlerContext extends Timer.HandlerContext { + static class NotMatchingHandlerContext extends Observation.Context { } } diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryAssert.java b/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryAssert.java index c3a9d45cc6..400a78b580 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryAssert.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryAssert.java @@ -105,21 +105,6 @@ public MeterRegistryAssert doesNotHaveTimerWithName(String timerName) { return this; } - /** - * Verifies that there's no current {@link Timer.Sample} left in the {@link MeterRegistry}. - * - * @return this - * @throws AssertionError if there is a current sample remaining in the registry - */ - public MeterRegistryAssert doesNotHaveRemainingSample() { - isNotNull(); - Timer.Sample currentSample = actual.getCurrentSample(); - if (currentSample != null) { - failWithMessage("Expected no current sample in the registry but found one"); - } - return this; - } - /** * Verifies that a timer with given name and key-value tags exists in the provided {@link MeterRegistry}. * diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java index 2c8ce8a866..7c3f84cd8b 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java @@ -17,6 +17,8 @@ import io.micrometer.api.instrument.*; import io.micrometer.api.instrument.Timer; +import io.micrometer.api.instrument.observation.Observation; +import io.micrometer.api.instrument.observation.ObservationHandler; import io.micrometer.core.Issue; import io.micrometer.api.annotation.Timed; import io.micrometer.api.instrument.distribution.CountAtBucket; @@ -651,44 +653,6 @@ void wrapSupplier() { // () -> assertEquals(10, timer.totalTime(TimeUnit.NANOSECONDS), 1.0e-12)); // } - @Test - @DisplayName("record using handlers") - void recordWithHandlers() { - @SuppressWarnings("unchecked") - ObservationHandler handler = mock(ObservationHandler.class); - @SuppressWarnings("unchecked") - ObservationHandler handlerThatHandlesNothing = mock(ObservationHandler.class); - registry.config().observationHandler(handler); - registry.config().observationHandler(handlerThatHandlesNothing); - when(handler.supportsContext(any())).thenReturn(true); - when(handlerThatHandlesNothing.supportsContext(any())).thenReturn(false); - - Observation observation = registry.observation("myObservation"); - verify(handler).supportsContext(isA(Observation.Context.class)); - verify(handler).onStart(same(observation), isA(Observation.Context.class)); - verify(handlerThatHandlesNothing).supportsContext(isA(Observation.Context.class)); - verifyNoMoreInteractions(handlerThatHandlesNothing); - - try (Observation.Scope scope = observation.openScope()) { - verify(handler).onScopeOpened(same(observation), isA(Observation.Context.class)); - assertThat(scope.getCurrentObservation()).isSameAs(observation); - - clock(registry).add(10, TimeUnit.NANOSECONDS); - Throwable exception = new IOException("simulated"); - observation.error(exception); - verify(handler).onError(same(observation), isA(Observation.Context.class)); - } - verify(handler).onScopeClosed(same(observation), isA(Observation.Context.class)); - observation.stop(); - - Timer timer = registry.timer("myTimer"); - verify(handler).onStop(same(observation), isA(Observation.Context.class)); - clock(registry).add(step()); - - assertAll(() -> assertEquals(1L, timer.count()), - () -> assertEquals(10, timer.totalTime(TimeUnit.NANOSECONDS), 1.0e-12)); - } - @Test void recordMax() { Timer timer = registry.timer("my.timer"); diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java index 2f3efb92fe..e2260ed2ce 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java @@ -15,12 +15,10 @@ */ package io.micrometer.core.tck; -import java.time.Duration; - -import io.micrometer.api.instrument.MeterRegistry; -import io.micrometer.api.instrument.Timer; -import io.micrometer.api.instrument.TimerRecordingHandler; -import io.micrometer.api.instrument.simple.SimpleMeterRegistry; +import io.micrometer.api.instrument.observation.Observation; +import io.micrometer.api.instrument.observation.ObservationHandler; +import io.micrometer.api.instrument.observation.ObservationRegistry; +import io.micrometer.api.instrument.observation.SimpleObservationRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -29,8 +27,8 @@ import static org.assertj.core.api.Assertions.assertThatCode; /** - * Base class for {@link TimerRecordingHandler} compatibility tests that support {@code null} contexts only. - * To run a {@link TimerRecordingHandler} implementation against this TCK, make a test class that extends this + * Base class for {@link ObservationHandler} compatibility tests that support {@code null} contexts only. + * To run a {@link ObservationHandler} implementation against this TCK, make a test class that extends this * and implement the abstract methods. * * @author Marcin Grzejszczak @@ -38,13 +36,13 @@ */ public abstract class NullHandlerContextTimerRecordingHandlerCompatibilityKit { - protected TimerRecordingHandler handler; + protected ObservationHandler handler; - protected MeterRegistry meterRegistry = new SimpleMeterRegistry(); + protected ObservationRegistry meterRegistry = new SimpleObservationRegistry(); - public abstract TimerRecordingHandler handler(); + public abstract ObservationHandler handler(); - protected Timer.Sample sample = Timer.start(meterRegistry); + protected Observation sample = meterRegistry.observation("hello"); @BeforeEach void setup() { @@ -56,8 +54,7 @@ void setup() { @DisplayName("compatibility test provides a null context accepting timer recording handler") void handlerSupportsNullContext() { assertThatCode(() -> handler.onStart(sample, null)).doesNotThrowAnyException(); - assertThatCode(() -> handler.onStop(sample, null, Timer.builder("timer for null context") - .register(meterRegistry), Duration.ofSeconds(1L))).doesNotThrowAnyException(); + assertThatCode(() -> handler.onStop(sample, null)).doesNotThrowAnyException(); assertThatCode(() -> handler.onError(sample, null, new RuntimeException())).doesNotThrowAnyException(); assertThatCode(() -> handler.onScopeOpened(sample, null)).doesNotThrowAnyException(); assertThatCode(() -> handler.supportsContext(null)).doesNotThrowAnyException(); diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryAssert.java b/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryAssert.java new file mode 100644 index 0000000000..fbd9247dcf --- /dev/null +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryAssert.java @@ -0,0 +1,79 @@ +/* + * Copyright 2021 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.tck; + +import java.util.List; +import java.util.stream.Collectors; + +import io.micrometer.api.instrument.MeterRegistry; +import io.micrometer.api.instrument.Tags; +import io.micrometer.api.instrument.Timer; +import io.micrometer.api.instrument.observation.Observation; +import io.micrometer.api.instrument.observation.ObservationRegistry; +import org.assertj.core.api.AbstractAssert; + +/** + * Assertion methods for {@code MeterRegistry}s. + *

+ * To create a new instance of this class, invoke {@link ObservationRegistryAssert#assertThat(ObservationRegistry)} + * or {@link ObservationRegistryAssert#then(ObservationRegistry)}. + * + * @author Marcin Grzejszczak + * @since 2.0.0 + */ +public class ObservationRegistryAssert extends AbstractAssert { + + protected ObservationRegistryAssert(ObservationRegistry actual) { + super(actual, ObservationRegistryAssert.class); + } + + /** + * Creates the assert object for {@link ObservationRegistry}. + * + * @param actual meter registry to assert against + * @return meter registry assertions + */ + public static ObservationRegistryAssert assertThat(ObservationRegistry actual) { + return new ObservationRegistryAssert(actual); + } + + /** + * Creates the assert object for {@link ObservationRegistry}. + * + * @param actual meter registry to assert against + * @return meter registry assertions + */ + public static ObservationRegistryAssert then(ObservationRegistry actual) { + return new ObservationRegistryAssert(actual); + } + + + /** + * Verifies that there's no current {@link Observation} left in the {@link ObservationRegistry}. + * + * @return this + * @throws AssertionError if there is a current sample remaining in the registry + */ + public ObservationRegistryAssert doesNotHaveRemainingObservation() { + isNotNull(); + Observation current = actual.getCurrentObservation(); + if (current != null) { + failWithMessage("Expected no current observation in the registry but found one"); + } + return this; + } + +} diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java new file mode 100644 index 0000000000..5d7e1dd8d5 --- /dev/null +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java @@ -0,0 +1,97 @@ +/* + * 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.tck; + +import java.io.IOException; +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import io.micrometer.api.instrument.MeterRegistry; +import io.micrometer.api.instrument.Timer; +import io.micrometer.api.instrument.observation.Observation; +import io.micrometer.api.instrument.observation.ObservationHandler; +import io.micrometer.api.instrument.observation.ObservationRegistry; +import io.micrometer.api.instrument.observation.SimpleObservationRegistry; +import io.micrometer.api.instrument.observation.TimerObservationRegistry; +import io.micrometer.api.instrument.simple.SimpleMeterRegistry; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static io.micrometer.api.instrument.MockClock.clock; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +/** + * Base class for {@link MeterRegistry} compatibility tests. + * To run a {@link MeterRegistry} implementation against this TCK, make a test class that extends this + * and implement the abstract methods. + * + * @author Jon Schneider + * @author Johnny Lim + */ +public abstract class ObservationRegistryCompatibilityKit { + + private ObservationRegistry registry; + + public abstract ObservationRegistry registry(); + public abstract Duration step(); + + @BeforeEach + void setup() { + // assigned here rather than at initialization so subclasses can use fields in their registry() implementation + registry = registry(); + } + + @Test + @DisplayName("record using handlers") + void recordWithHandlers() { + @SuppressWarnings("unchecked") + ObservationHandler handler = mock(ObservationHandler.class); + @SuppressWarnings("unchecked") + ObservationHandler handlerThatHandlesNothing = mock(ObservationHandler.class); + registry.config().observationHandler(handler); + registry.config().observationHandler(handlerThatHandlesNothing); + when(handler.supportsContext(any())).thenReturn(true); + when(handlerThatHandlesNothing.supportsContext(any())).thenReturn(false); + + Observation observation = registry.start("myObservation"); + verify(handler).supportsContext(isA(Observation.Context.class)); + verify(handler).onStart(same(observation), isA(Observation.Context.class)); + verify(handlerThatHandlesNothing).supportsContext(isA(Observation.Context.class)); + verifyNoMoreInteractions(handlerThatHandlesNothing); + + try (Observation.Scope scope = observation.openScope()) { + verify(handler).onScopeOpened(same(observation), isA(Observation.Context.class)); + assertThat(scope.getCurrentObservation()).isSameAs(observation); + + Throwable exception = new IOException("simulated"); + observation.error(exception); + verify(handler).onError(same(observation), isA(Observation.Context.class), isA(IOException.class)); + } + verify(handler).onScopeClosed(same(observation), isA(Observation.Context.class)); + observation.stop(); + } +} + diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKitTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKitTests.java index 2122186948..43731da5b3 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKitTests.java +++ b/micrometer-test/src/test/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKitTests.java @@ -15,38 +15,36 @@ */ package io.micrometer.core.tck; -import java.time.Duration; - -import io.micrometer.api.instrument.Timer; -import io.micrometer.api.instrument.TimerRecordingHandler; +import io.micrometer.api.instrument.observation.Observation; +import io.micrometer.api.instrument.observation.ObservationHandler; class AnyHandlerContextTimerRecordingHandlerCompatibilityKitTests extends AnyHandlerContextTimerRecordingHandlerCompatibilityKit { @Override - public TimerRecordingHandler handler() { - return new TimerRecordingHandler() { + public ObservationHandler handler() { + return new ObservationHandler() { @Override - public void onStart(Timer.Sample sample, Timer.HandlerContext handlerContext) { + public void onStart(Observation sample, Observation.Context handlerContext) { } @Override - public void onError(Timer.Sample sample, Timer.HandlerContext handlerContext, Throwable throwable) { + public void onError(Observation sample, Observation.Context handlerContext, Throwable throwable) { } @Override - public void onScopeOpened(Timer.Sample sample, Timer.HandlerContext handlerContext) { + public void onScopeOpened(Observation sample, Observation.Context handlerContext) { } @Override - public void onStop(Timer.Sample sample, Timer.HandlerContext handlerContext, Timer timer, Duration duration) { + public void onStop(Observation sample, Observation.Context handlerContext) { } @Override - public boolean supportsContext(Timer.HandlerContext handlerContext) { + public boolean supportsContext(Observation.Context handlerContext) { return true; } }; diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKitTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKitTests.java new file mode 100644 index 0000000000..b08e6512e2 --- /dev/null +++ b/micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKitTests.java @@ -0,0 +1,61 @@ +/* + * Copyright 2021 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.tck; + +import io.micrometer.api.instrument.observation.Observation; +import io.micrometer.api.instrument.observation.ObservationHandler; + +class ConcreteHandlerContextObservationHandlerCompatibilityKitTests extends ConcreteHandlerContextObservationHandlerCompatibilityKit { + + @Override + public ObservationHandler handler() { + return new ObservationHandler() { + @Override + public void onStart(Observation sample, Observation.Context handlerContext) { + + } + + @Override + public void onError(Observation sample, Observation.Context handlerContext, Throwable throwable) { + + } + + @Override + public void onScopeOpened(Observation sample, Observation.Context handlerContext) { + + } + + @Override + public void onStop(Observation sample, Observation.Context handlerContext) { + + } + + @Override + public boolean supportsContext(Observation.Context handlerContext) { + return handlerContext instanceof MyHandlerContext; + } + }; + } + + @Override + public Observation.Context context() { + return new MyHandlerContext(); + } + + static class MyHandlerContext extends Observation.Context { + + } +} diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextTimerRecordingHandlerCompatibilityKitTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextTimerRecordingHandlerCompatibilityKitTests.java deleted file mode 100644 index 02bfcb0db3..0000000000 --- a/micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextTimerRecordingHandlerCompatibilityKitTests.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2021 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.tck; - -import java.time.Duration; - -import io.micrometer.api.instrument.Timer; -import io.micrometer.api.instrument.TimerRecordingHandler; - -class ConcreteHandlerContextTimerRecordingHandlerCompatibilityKitTests extends ConcreteHandlerContextTimerRecordingHandlerCompatibilityKit { - - @Override - public TimerRecordingHandler handler() { - return new TimerRecordingHandler() { - @Override - public void onStart(Timer.Sample sample, Timer.HandlerContext handlerContext) { - - } - - @Override - public void onError(Timer.Sample sample, Timer.HandlerContext handlerContext, Throwable throwable) { - - } - - @Override - public void onScopeOpened(Timer.Sample sample, Timer.HandlerContext handlerContext) { - - } - - @Override - public void onStop(Timer.Sample sample, Timer.HandlerContext handlerContext, Timer timer, Duration duration) { - - } - - @Override - public boolean supportsContext(Timer.HandlerContext handlerContext) { - return handlerContext instanceof MyHandlerContext; - } - }; - } - - @Override - public Timer.HandlerContext context() { - return new MyHandlerContext(); - } - - static class MyHandlerContext extends Timer.HandlerContext { - - } -} diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/MeterRegistryAssertTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/MeterRegistryAssertTests.java index 69929d0179..32ed2ca3cb 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/tck/MeterRegistryAssertTests.java +++ b/micrometer-test/src/test/java/io/micrometer/core/tck/MeterRegistryAssertTests.java @@ -32,7 +32,7 @@ class MeterRegistryAssertTests { @Test void assertionErrorThrownWhenMetricsArePresent() { - Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name")); + Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name").register(this.simpleMeterRegistry)); assertThatThrownBy(() -> meterRegistryAssert.hasNoMetrics()) .isInstanceOf(AssertionError.class) @@ -62,7 +62,7 @@ void assertionErrorThrownWhenNoTimer() { @Test void assertionErrorThrownWhenTimerPresentButWrongTagKeys() { - Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name").tag("notmatching-tag", "baz")); + Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name").tag("notmatching-tag", "baz").register(this.simpleMeterRegistry)); assertThatThrownBy(() -> meterRegistryAssert.hasTimerWithNameAndTagKeys("matching-metric-name", "non-existent-tag")) .isInstanceOf(AssertionError.class) @@ -71,7 +71,7 @@ void assertionErrorThrownWhenTimerPresentButWrongTagKeys() { @Test void assertionErrorThrownWhenTimerPresentButWrongTagValue() { - Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name").tag("matching-tag", "not-matching-value")); + Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name").tag("matching-tag", "not-matching-value").register(this.simpleMeterRegistry)); assertThatThrownBy(() -> meterRegistryAssert.hasTimerWithNameAndTags("matching-metric-name", Tags.of("matching-tag", "some-value"))) .isInstanceOf(AssertionError.class) @@ -80,27 +80,16 @@ void assertionErrorThrownWhenTimerPresentButWrongTagValue() { @Test void assertionErrorThrownWhenTimerFound() { - Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name")); + Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name").register(this.simpleMeterRegistry)); assertThatThrownBy(() -> meterRegistryAssert.doesNotHaveTimerWithName("matching-metric-name")) .isInstanceOf(AssertionError.class) .hasMessageContaining("Expected no timer with name but found one with tags <[]>"); } - @Test - void assertionErrorThrownWhenRemainingSampleFound() { - Timer.Sample sample = Timer.start(this.simpleMeterRegistry); - - try (Timer.Scope ws = sample.makeCurrent()) { - assertThatThrownBy(() -> meterRegistryAssert.doesNotHaveRemainingSample()) - .isInstanceOf(AssertionError.class) - .hasMessageContaining("Expected no current sample in the registry but found one"); - } - } - @Test void assertionErrorThrownWhenTimerPresentWithTagKeys() { - Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name").tag("matching-tag", "baz")); + Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name").tag("matching-tag", "baz").register(this.simpleMeterRegistry)); assertThatThrownBy(() -> meterRegistryAssert.doesNotHaveTimerWithNameAndTagKeys("matching-metric-name", "matching-tag")) .isInstanceOf(AssertionError.class) @@ -109,7 +98,7 @@ void assertionErrorThrownWhenTimerPresentWithTagKeys() { @Test void assertionErrorThrownWhenTimerPresentWithTagValue() { - Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name").tag("matching-tag", "matching-value")); + Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name").tag("matching-tag", "matching-value").register(this.simpleMeterRegistry)); assertThatThrownBy(() -> meterRegistryAssert.doesNotHaveTimerWithNameAndTags("matching-metric-name", Tags.of("matching-tag", "matching-value"))) .isInstanceOf(AssertionError.class) @@ -124,7 +113,7 @@ void noAssertionErrorThrownWhenNoMetricsRegistered() { @Test void noAssertionErrorThrownWhenTimerPresent() { - Timer.start(this.simpleMeterRegistry).stop(Timer.builder("foo")); + Timer.start(this.simpleMeterRegistry).stop(Timer.builder("foo").register(this.simpleMeterRegistry)); assertThatCode(() -> meterRegistryAssert.hasTimerWithName("foo")) .doesNotThrowAnyException(); @@ -132,7 +121,7 @@ void noAssertionErrorThrownWhenTimerPresent() { @Test void noAssertionErrorThrownWhenTimerWithTagKeysPresent() { - Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name").tag("matching-tag", "baz")); + Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name").tag("matching-tag", "baz").register(this.simpleMeterRegistry)); assertThatCode(() -> meterRegistryAssert.hasTimerWithNameAndTagKeys("matching-metric-name", "matching-tag")) .doesNotThrowAnyException(); @@ -140,7 +129,7 @@ void noAssertionErrorThrownWhenTimerWithTagKeysPresent() { @Test void noAssertionErrorThrownWhenTimerWithTagPresent() { - Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name").tag("matching-tag", "matching-value")); + Timer.start(this.simpleMeterRegistry).stop(Timer.builder("matching-metric-name").tag("matching-tag", "matching-value").register(this.simpleMeterRegistry)); assertThatCode(() -> meterRegistryAssert.hasTimerWithNameAndTags("matching-metric-name", Tags.of("matching-tag", "matching-value"))) .doesNotThrowAnyException(); @@ -152,12 +141,6 @@ void noAssertionErrorThrownWhenTimerMissing() { .doesNotThrowAnyException(); } - @Test - void noAssertionErrorThrownWhenNoCurrentSample() { - assertThatCode(() -> meterRegistryAssert.doesNotHaveRemainingSample()) - .doesNotThrowAnyException(); - } - @Test void noAssertionErrorThrownWhenTimerWithTagsMissing() { assertThatCode(() -> meterRegistryAssert.doesNotHaveTimerWithNameAndTags("foo", Tags.of(Tag.of("bar", "baz")))) diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/NullContextTimerRecordingHandlerCompatibilityKitTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/NullContextTimerRecordingHandlerCompatibilityKitTests.java index 2be2afedf1..00f9550079 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/tck/NullContextTimerRecordingHandlerCompatibilityKitTests.java +++ b/micrometer-test/src/test/java/io/micrometer/core/tck/NullContextTimerRecordingHandlerCompatibilityKitTests.java @@ -15,38 +15,36 @@ */ package io.micrometer.core.tck; -import java.time.Duration; - -import io.micrometer.api.instrument.Timer; -import io.micrometer.api.instrument.TimerRecordingHandler; +import io.micrometer.api.instrument.observation.Observation; +import io.micrometer.api.instrument.observation.ObservationHandler; class NullContextTimerRecordingHandlerCompatibilityKitTests extends NullHandlerContextTimerRecordingHandlerCompatibilityKit { @Override - public TimerRecordingHandler handler() { - return new TimerRecordingHandler() { + public ObservationHandler handler() { + return new ObservationHandler() { @Override - public void onStart(Timer.Sample sample, Timer.HandlerContext handlerContext) { + public void onStart(Observation sample, Observation.Context handlerContext) { } @Override - public void onError(Timer.Sample sample, Timer.HandlerContext handlerContext, Throwable throwable) { + public void onError(Observation sample, Observation.Context handlerContext, Throwable throwable) { } @Override - public void onScopeOpened(Timer.Sample sample, Timer.HandlerContext handlerContext) { + public void onScopeOpened(Observation sample, Observation.Context handlerContext) { } @Override - public void onStop(Timer.Sample sample, Timer.HandlerContext handlerContext, Timer timer, Duration duration) { + public void onStop(Observation sample, Observation.Context handlerContext) { } @Override - public boolean supportsContext(Timer.HandlerContext handlerContext) { + public boolean supportsContext(Observation.Context handlerContext) { return true; } }; diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryAssertTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryAssertTests.java new file mode 100644 index 0000000000..3c0971b1b0 --- /dev/null +++ b/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryAssertTests.java @@ -0,0 +1,49 @@ +/* + * Copyright 2021 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.tck; + +import io.micrometer.api.instrument.observation.Observation; +import io.micrometer.api.instrument.observation.ObservationRegistry; +import io.micrometer.api.instrument.observation.SimpleObservationRegistry; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class ObservationRegistryAssertTests { + + ObservationRegistry registry = new SimpleObservationRegistry(); + + ObservationRegistryAssert registryAssert = new ObservationRegistryAssert(registry); + + @Test + void assertionErrorThrownWhenRemainingSampleFound() { + Observation sample = this.registry.start("hello"); + + try (Observation.Scope ws = sample.openScope()) { + assertThatThrownBy(() -> registryAssert.doesNotHaveRemainingObservation()) + .isInstanceOf(AssertionError.class) + .hasMessageContaining("Expected no current observation in the registry but found one"); + } + } + + @Test + void noAssertionErrorThrownWhenNoCurrentSample() { + assertThatCode(() -> this.registryAssert.doesNotHaveRemainingObservation()) + .doesNotThrowAnyException(); + } + +} diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKitTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKitTests.java new file mode 100644 index 0000000000..ed863f154b --- /dev/null +++ b/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKitTests.java @@ -0,0 +1,42 @@ +/* + * Copyright 2021 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.tck; + +import java.time.Duration; +import java.time.temporal.TemporalUnit; + +import io.micrometer.api.instrument.observation.Observation; +import io.micrometer.api.instrument.observation.ObservationRegistry; +import io.micrometer.api.instrument.observation.SimpleObservationRegistry; +import io.micrometer.api.instrument.simple.SimpleConfig; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class ObservationRegistryCompatibilityKitTests extends ObservationRegistryCompatibilityKit { + + ObservationRegistry registry = new SimpleObservationRegistry(); + + @Override + public ObservationRegistry registry() { + return this.registry; + } + + @Override + public Duration step() { + return SimpleConfig.DEFAULT.step(); + } +} diff --git a/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java b/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java index 189a8479ca..39e277831b 100644 --- a/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java +++ b/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java @@ -19,31 +19,45 @@ import java.time.Instant; import java.util.UUID; -import io.micrometer.api.instrument.Observation; -import io.micrometer.api.instrument.ObservationHandler; +import io.micrometer.api.instrument.observation.Observation; +import io.micrometer.api.instrument.observation.ObservationHandler; import io.micrometer.api.instrument.Tags; +import io.micrometer.api.instrument.observation.ObservationRegistry; +import io.micrometer.api.instrument.observation.TimerObservationRegistry; import io.micrometer.prometheus.PrometheusConfig; import io.micrometer.prometheus.PrometheusMeterRegistry; public class ObservationHandlerSample { private static final PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); - public static void main(String[] args) throws InterruptedException { - registry.config().observationHandler(new SampleHandler()); + private static final ObservationRegistry observationRegistry = new TimerObservationRegistry(registry); - Observation observation = registry.observation("sample.operation", new CustomContext()) + public static void main(String[] args) throws InterruptedException { + observationRegistry.config().observationHandler(new SampleHandler()); + observationRegistry.config().observationPredicate((s, context) -> { + boolean observationEnabled = !"sample.ignored".equals(s); + if (!observationEnabled) { + System.out.println("Ignoring sample.ignored"); + } + return observationEnabled; + }); + + Observation observation = observationRegistry.observation("sample.operation", new CustomContext()) .displayName("CALL sampleOperation") .lowCardinalityTag("a", "1") .highCardinalityTag("time", Instant.now().toString()) .start(); + try (Observation.Scope scope = observation.openScope()) { Thread.sleep(1_000); observation.error(new IOException("simulated")); } observation.stop(); - registry.observation("sample.operation").start().stop(); - registry.observation("sample.operation", new UnsupportedHandlerContext()).start().stop(); + observationRegistry.start("sample.operation").stop(); + observationRegistry.start("sample.operation", new UnsupportedHandlerContext()).stop(); + + observationRegistry.start("sample.ignored", new CustomContext()).stop(); System.out.println(); System.out.println(registry.scrape()); @@ -52,12 +66,15 @@ public static void main(String[] args) throws InterruptedException { static class SampleHandler implements ObservationHandler { @Override public void onStart(Observation observation, CustomContext context) { + if (context.getName().equals("sample.ignored")) { + throw new AssertionError("Boom!"); + } System.out.println("start: " + observation); } @Override - public void onError(Observation observation, CustomContext context) { - System.out.println("error: " + observation); + public void onError(Observation observation, CustomContext context, Throwable throwable) { + System.out.println("error: " + observation + " " + throwable); } @Override From 0ce4d9b96afc5ec6387c0bc37c52a9b356c468ad Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Tue, 1 Feb 2022 18:55:59 -0800 Subject: [PATCH 3/6] Remove the error from the handler API and add it to the context + cleaning up (fixing imports, checkstyle issues, license, etc.) --- .../api/instrument/NoopObservation.java | 21 ++++++-- .../instrument/observation/Observation.java | 27 ++++++++++ .../observation/ObservationHandler.java | 25 +++++++-- .../observation/ObservationRegistry.java | 1 - .../observation/SimpleObservation.java | 23 ++++++-- .../observation/TimerObservationHandler.java | 29 ++++++---- .../api/instrument/MeterRegistryTest.java | 1 - ...ngCompositeTimerRecordingHandlerTests.java | 14 ++--- .../observation/CurrentObservationTest.java | 5 +- ...chingCompositeObservationHandlerTests.java | 10 ++-- .../observation/HandlerContextTest.java | 1 - .../observation/ObservationRegistryTest.java | 13 ----- ...TimerRecordingHandlerCompatibilityKit.java | 2 +- ...extObservationHandlerCompatibilityKit.java | 2 +- .../tck/MeterRegistryCompatibilityKit.java | 53 +++++++++++-------- ...TimerRecordingHandlerCompatibilityKit.java | 2 +- .../core/tck/ObservationRegistryAssert.java | 6 --- .../ObservationRegistryCompatibilityKit.java | 10 +--- ...RecordingHandlerCompatibilityKitTests.java | 2 +- ...servationHandlerCompatibilityKitTests.java | 2 +- ...RecordingHandlerCompatibilityKitTests.java | 2 +- ...ervationRegistryCompatibilityKitTests.java | 5 -- .../samples/ObservationHandlerSample.java | 4 +- 23 files changed, 150 insertions(+), 110 deletions(-) diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java index 3505914438..8fa14d3d3e 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java @@ -1,13 +1,24 @@ +/* + * Copyright 2022 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.api.instrument; -import java.time.Duration; -import java.util.Collections; - import io.micrometer.api.instrument.observation.Observation; import io.micrometer.api.lang.Nullable; public class NoopObservation implements Observation { - private static final Iterable TAGS = Collections.emptyList(); public static final NoopObservation INSTANCE = new NoopObservation(); private NoopObservation() { @@ -58,7 +69,7 @@ public Scope openScope() { } static class NoOpScope implements Scope { - static final NoOpScope INSTANCE = new NoOpScope(); + static final NoOpScope INSTANCE = new NoOpScope(); @Nullable @Override diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java index fac7b1a5da..cbe9fd2e3e 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java @@ -1,8 +1,24 @@ +/* + * Copyright 2022 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.api.instrument.observation; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.function.Function; @@ -111,6 +127,8 @@ class Context implements TagsProvider { private String displayName; + @Nullable private Throwable error; + private final Set additionalLowCardinalityTags = new LinkedHashSet<>(); private final Set additionalHighCardinalityTags = new LinkedHashSet<>(); @@ -138,6 +156,15 @@ public Context setDisplayName(String displayName) { return this; } + public Optional getError() { + return Optional.ofNullable(this.error); + } + + public Context setError(Throwable error) { + this.error = error; + return this; + } + public void remove(Class clazz) { this.map.remove(clazz); } diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationHandler.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationHandler.java index 08607a37f5..ab30eaf6cd 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationHandler.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationHandler.java @@ -1,3 +1,18 @@ +/* + * Copyright 2022 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.api.instrument.observation; import java.util.Arrays; @@ -8,7 +23,7 @@ public interface ObservationHandler { void onStart(Observation observation, T context); - void onError(Observation observation, T context, Throwable throwable); + void onError(Observation observation, T context); default void onScopeOpened(Observation observation, T context) { } @@ -67,8 +82,8 @@ public void onStart(Observation observation, Observation.Context context) { } @Override - public void onError(Observation observation, Observation.Context context, Throwable throwable) { - getFirstApplicableHandler(context).ifPresent(handler -> handler.onError(observation, context, throwable)); + public void onError(Observation observation, Observation.Context context) { + getFirstApplicableHandler(context).ifPresent(handler -> handler.onError(observation, context)); } @Override @@ -130,8 +145,8 @@ public void onStart(Observation observation, Observation.Context context) { } @Override - public void onError(Observation observation, Observation.Context context, Throwable throwable) { - getAllApplicableHandlers(context).forEach(handler -> handler.onError(observation, context, throwable)); + public void onError(Observation observation, Observation.Context context) { + getAllApplicableHandlers(context).forEach(handler -> handler.onError(observation, context)); } @Override diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationRegistry.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationRegistry.java index a6e251c83c..b1c95c6720 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationRegistry.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationRegistry.java @@ -20,7 +20,6 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.BiPredicate; -import io.micrometer.api.instrument.MeterRegistry; import io.micrometer.api.instrument.NoopObservation; import io.micrometer.api.lang.Nullable; diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java index c009c8bc58..8ec0ed9bcb 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java @@ -1,7 +1,21 @@ +/* + * Copyright 2022 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.api.instrument.observation; import java.util.ArrayDeque; -import java.util.Collection; import java.util.Deque; import java.util.stream.Collectors; @@ -10,7 +24,6 @@ public class SimpleObservation implements Observation { private final ObservationRegistry registry; - @Nullable private Throwable error; private final Context context; @SuppressWarnings("rawtypes") private final Deque handlers; @@ -47,7 +60,7 @@ public Observation highCardinalityTag(Tag tag) { @Override public Observation error(Throwable error) { - this.error = error; + this.context.setError(error); this.notifyOnError(); return this; } @@ -73,7 +86,7 @@ public Scope openScope() { public String toString() { return "{" + "name=" + this.context.getName() + "(" + this.context.getDisplayName() + ")" - + ", error=" + this.error + + ", error=" + this.context.getError() + ", context=" + this.context + '}'; } @@ -85,7 +98,7 @@ private void notifyOnObservationStarted() { @SuppressWarnings("unchecked") private void notifyOnError() { - this.handlers.forEach(handler -> handler.onError(this, this.context, this.error)); + this.handlers.forEach(handler -> handler.onError(this, this.context)); } @SuppressWarnings("unchecked") diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationHandler.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationHandler.java index 9b30a52444..4c58f4a090 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationHandler.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationHandler.java @@ -1,8 +1,21 @@ +/* + * Copyright 2022 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.api.instrument.observation; import io.micrometer.api.instrument.MeterRegistry; -import io.micrometer.api.instrument.Tag; -import io.micrometer.api.instrument.Tags; import io.micrometer.api.instrument.Timer; /** @@ -11,7 +24,7 @@ * @author Marcin Grzejszczak * @since 2.0.0 */ -public class TimerObservationHandler implements ObservationHandler { +public class TimerObservationHandler implements ObservationHandler { private final MeterRegistry meterRegistry; @@ -26,19 +39,15 @@ public void onStart(Observation observation, Observation.Context context) { } @Override - public void onError(Observation observation, Observation.Context context, Throwable throwable) { - context.put(Throwable.class, throwable); + public void onError(Observation observation, Observation.Context context) { } @Override public void onStop(Observation observation, Observation.Context context) { Timer.Sample sample = context.get(Timer.Sample.class); - Tags tags = context.getLowCardinalityTags().and(context.getAdditionalLowCardinalityTags()); - if (context.containsKey(Throwable.class)) { - tags = tags.and(Tag.of("error", context.get(Throwable.class).getLocalizedMessage())); - } sample.stop(Timer.builder(context.getName()) - .tags(tags) + .tag("error", context.getError().map(Throwable::getMessage).orElse("none")) + .tags(context.getLowCardinalityTags().and(context.getAdditionalLowCardinalityTags())) .register(this.meterRegistry)); } diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/MeterRegistryTest.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/MeterRegistryTest.java index f6490df2b3..e7e9d2a200 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/MeterRegistryTest.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/MeterRegistryTest.java @@ -28,7 +28,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.mock; /** * Tests for {@link MeterRegistry}. diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/AllMatchingCompositeTimerRecordingHandlerTests.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/AllMatchingCompositeTimerRecordingHandlerTests.java index 2452fe1a78..ec3321f09a 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/AllMatchingCompositeTimerRecordingHandlerTests.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/AllMatchingCompositeTimerRecordingHandlerTests.java @@ -15,11 +15,7 @@ */ package io.micrometer.api.instrument.observation; -import java.time.Duration; - -import io.micrometer.api.instrument.Timer; import io.micrometer.api.instrument.observation.ObservationHandler.AllMatchingCompositeObservationHandler; -import io.micrometer.api.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -61,7 +57,7 @@ void should_run_on_error_for_all_matching_handlers() { AllMatchingCompositeObservationHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler(), this.matchingHandler2); - allMatchingCompositeTimerRecordingHandler.onError(sample, null, new RuntimeException()); + allMatchingCompositeTimerRecordingHandler.onError(sample, null); assertThat(this.matchingHandler.errored).isTrue(); assertThat(this.matchingHandler2.errored).isTrue(); @@ -89,7 +85,7 @@ void should_run_on_scope_closed_for_all_matching_handlers() { assertThat(this.matchingHandler2.scopeClosed).isTrue(); } - static class MatchingHandler implements ObservationHandler { + static class MatchingHandler implements ObservationHandler { boolean started; @@ -108,7 +104,7 @@ public void onStart(Observation observation, Observation.Context context) { } @Override - public void onError(Observation observation, Observation.Context context, Throwable throwable) { + public void onError(Observation observation, Observation.Context context) { this.errored = true; } @@ -133,7 +129,7 @@ public boolean supportsContext(Observation.Context handlerContext) { } } - static class NotMatchingHandler implements ObservationHandler { + static class NotMatchingHandler implements ObservationHandler { @Override public void onStart(Observation observation, Observation.Context context) { @@ -141,7 +137,7 @@ public void onStart(Observation observation, Observation.Context context) { } @Override - public void onError(Observation observation, Observation.Context context, Throwable throwable) { + public void onError(Observation observation, Observation.Context context) { throwAssertionError(); } diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/CurrentObservationTest.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/CurrentObservationTest.java index a7b1c7c1c2..c974246cda 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/CurrentObservationTest.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/CurrentObservationTest.java @@ -15,9 +15,6 @@ */ package io.micrometer.api.instrument.observation; -import io.micrometer.api.instrument.MeterRegistry; -import io.micrometer.api.instrument.observation.Observation; -import io.micrometer.api.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; import java.util.HashMap; @@ -77,7 +74,7 @@ void startOnChildThread_thenStopOnSiblingThread() throws InterruptedException, E }).get(); executor2.submit(() -> { - Observation myObservation= observationMap.get("myObservation"); + Observation myObservation = observationMap.get("myObservation"); try (Observation.Scope scope = myObservation.openScope()) { assertThat(registry.getCurrentObservation()).isEqualTo(myObservation); } diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/FirstMatchingCompositeObservationHandlerTests.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/FirstMatchingCompositeObservationHandlerTests.java index 6e8dae5729..ef0f4bfd7b 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/FirstMatchingCompositeObservationHandlerTests.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/FirstMatchingCompositeObservationHandlerTests.java @@ -53,7 +53,7 @@ void should_run_on_error_only_for_first_matching_handler() { FirstMatchingCompositeObservationHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler()); - firstMatchingCompositeTimerRecordingHandler.onError(sample, null, new RuntimeException()); + firstMatchingCompositeTimerRecordingHandler.onError(sample, null); assertThat(this.matchingHandler.errored).isTrue(); } @@ -78,7 +78,7 @@ void should_run_on_scope_closed_only_for_first_matching_handler() { assertThat(this.matchingHandler.scopeClosed).isTrue(); } - static class MatchingHandler implements ObservationHandler { + static class MatchingHandler implements ObservationHandler { boolean started; @@ -97,7 +97,7 @@ public void onStart(Observation observation, Observation.Context context) { } @Override - public void onError(Observation observation, Observation.Context context, Throwable throwable) { + public void onError(Observation observation, Observation.Context context) { this.errored = true; } @@ -122,7 +122,7 @@ public boolean supportsContext(Observation.Context handlerContext) { } } - static class NotMatchingHandler implements ObservationHandler { + static class NotMatchingHandler implements ObservationHandler { @Override public void onStart(Observation observation, Observation.Context context) { @@ -130,7 +130,7 @@ public void onStart(Observation observation, Observation.Context context) { } @Override - public void onError(Observation observation, Observation.Context context, Throwable throwable) { + public void onError(Observation observation, Observation.Context context) { throwAssertionError(); } diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/HandlerContextTest.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/HandlerContextTest.java index a05936c505..ec18676365 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/HandlerContextTest.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/HandlerContextTest.java @@ -15,7 +15,6 @@ */ package io.micrometer.api.instrument.observation; -import io.micrometer.api.instrument.Timer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/ObservationRegistryTest.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/ObservationRegistryTest.java index d8cbe9b0f4..8dd96a4cb7 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/ObservationRegistryTest.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/ObservationRegistryTest.java @@ -15,24 +15,11 @@ */ package io.micrometer.api.instrument.observation; -import javax.annotation.Nonnull; - -import io.micrometer.api.instrument.Counter; -import io.micrometer.api.instrument.Meter; import io.micrometer.api.instrument.MeterRegistry; import io.micrometer.api.instrument.NoopObservation; -import io.micrometer.api.instrument.Timer; -import io.micrometer.api.instrument.config.MeterFilter; -import io.micrometer.api.instrument.config.MeterFilterReply; -import io.micrometer.api.instrument.distribution.DistributionStatisticConfig; -import io.micrometer.api.instrument.distribution.pause.PauseDetector; -import io.micrometer.api.instrument.noop.NoopCounter; -import io.micrometer.api.instrument.noop.NoopTimer; -import io.micrometer.api.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; /** diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKit.java index aae858d16f..f9e2117f09 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKit.java @@ -39,7 +39,7 @@ void handlerSupportsAnyContext() { TestHandlerContext testContext = new TestHandlerContext(); assertThatCode(() -> handler.onStart(sample, testContext)).doesNotThrowAnyException(); assertThatCode(() -> handler.onStop(sample, testContext)).doesNotThrowAnyException(); - assertThatCode(() -> handler.onError(sample, testContext, new RuntimeException())).doesNotThrowAnyException(); + assertThatCode(() -> handler.onError(sample, testContext)).doesNotThrowAnyException(); assertThatCode(() -> handler.onScopeOpened(sample, testContext)).doesNotThrowAnyException(); assertThatCode(() -> handler.supportsContext(testContext)).doesNotThrowAnyException(); assertThat(handler.supportsContext(testContext)).as("Handler supports any context").isTrue(); diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKit.java index 3a3e744d7a..fad0a29629 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKit.java @@ -57,7 +57,7 @@ void setup() { void handlerSupportsConcreteContextForHandlerMethods() { assertThatCode(() -> handler.onStart(sample, context())).doesNotThrowAnyException(); assertThatCode(() -> handler.onStop(sample, context())).doesNotThrowAnyException(); - assertThatCode(() -> handler.onError(sample, context(), new RuntimeException())).doesNotThrowAnyException(); + assertThatCode(() -> handler.onError(sample, context())).doesNotThrowAnyException(); assertThatCode(() -> handler.onScopeOpened(sample, context())).doesNotThrowAnyException(); } diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java index 7c3f84cd8b..1492419049 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/MeterRegistryCompatibilityKit.java @@ -15,17 +15,35 @@ */ package io.micrometer.core.tck; -import io.micrometer.api.instrument.*; -import io.micrometer.api.instrument.Timer; -import io.micrometer.api.instrument.observation.Observation; -import io.micrometer.api.instrument.observation.ObservationHandler; -import io.micrometer.core.Issue; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; + import io.micrometer.api.annotation.Timed; +import io.micrometer.api.instrument.Counter; +import io.micrometer.api.instrument.DistributionSummary; +import io.micrometer.api.instrument.FunctionTimer; +import io.micrometer.api.instrument.Gauge; +import io.micrometer.api.instrument.LongTaskTimer; +import io.micrometer.api.instrument.Meter; +import io.micrometer.api.instrument.MeterRegistry; +import io.micrometer.api.instrument.Statistic; +import io.micrometer.api.instrument.Tag; +import io.micrometer.api.instrument.Timer; import io.micrometer.api.instrument.distribution.CountAtBucket; import io.micrometer.api.instrument.distribution.DistributionStatisticConfig; import io.micrometer.api.instrument.distribution.ValueAtPercentile; import io.micrometer.api.instrument.internal.CumulativeHistogramLongTaskTimer; import io.micrometer.api.instrument.util.TimeUtils; +import io.micrometer.core.Issue; import org.assertj.core.data.Offset; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -34,30 +52,19 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import java.io.IOException; -import java.time.Duration; -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Supplier; - import static io.micrometer.api.instrument.MockClock.clock; import static io.micrometer.api.instrument.Statistic.ACTIVE_TASKS; import static io.micrometer.api.instrument.Statistic.DURATION; import static io.micrometer.api.instrument.util.TimeUtils.millisToUnit; import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; -import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.offset; +import static org.assertj.core.api.Assertions.within; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Base class for {@link MeterRegistry} compatibility tests. diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java index e2260ed2ce..8fd5f843cf 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java @@ -55,7 +55,7 @@ void setup() { void handlerSupportsNullContext() { assertThatCode(() -> handler.onStart(sample, null)).doesNotThrowAnyException(); assertThatCode(() -> handler.onStop(sample, null)).doesNotThrowAnyException(); - assertThatCode(() -> handler.onError(sample, null, new RuntimeException())).doesNotThrowAnyException(); + assertThatCode(() -> handler.onError(sample, null)).doesNotThrowAnyException(); assertThatCode(() -> handler.onScopeOpened(sample, null)).doesNotThrowAnyException(); assertThatCode(() -> handler.supportsContext(null)).doesNotThrowAnyException(); assertThat(handler.supportsContext(null)).as("Handler supports null context").isTrue(); diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryAssert.java b/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryAssert.java index fbd9247dcf..6966e924e2 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryAssert.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryAssert.java @@ -15,12 +15,6 @@ */ package io.micrometer.core.tck; -import java.util.List; -import java.util.stream.Collectors; - -import io.micrometer.api.instrument.MeterRegistry; -import io.micrometer.api.instrument.Tags; -import io.micrometer.api.instrument.Timer; import io.micrometer.api.instrument.observation.Observation; import io.micrometer.api.instrument.observation.ObservationRegistry; import org.assertj.core.api.AbstractAssert; diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java index 5d7e1dd8d5..342f73b52e 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java @@ -17,24 +17,16 @@ import java.io.IOException; import java.time.Duration; -import java.util.concurrent.TimeUnit; import io.micrometer.api.instrument.MeterRegistry; -import io.micrometer.api.instrument.Timer; import io.micrometer.api.instrument.observation.Observation; import io.micrometer.api.instrument.observation.ObservationHandler; import io.micrometer.api.instrument.observation.ObservationRegistry; -import io.micrometer.api.instrument.observation.SimpleObservationRegistry; -import io.micrometer.api.instrument.observation.TimerObservationRegistry; -import io.micrometer.api.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import static io.micrometer.api.instrument.MockClock.clock; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.ArgumentMatchers.same; @@ -88,7 +80,7 @@ void recordWithHandlers() { Throwable exception = new IOException("simulated"); observation.error(exception); - verify(handler).onError(same(observation), isA(Observation.Context.class), isA(IOException.class)); + verify(handler).onError(same(observation), isA(Observation.Context.class)); } verify(handler).onScopeClosed(same(observation), isA(Observation.Context.class)); observation.stop(); diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKitTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKitTests.java index 43731da5b3..9399cff4e1 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKitTests.java +++ b/micrometer-test/src/test/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKitTests.java @@ -29,7 +29,7 @@ public void onStart(Observation sample, Observation.Context handlerContext) { } @Override - public void onError(Observation sample, Observation.Context handlerContext, Throwable throwable) { + public void onError(Observation sample, Observation.Context handlerContext) { } diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKitTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKitTests.java index b08e6512e2..2bb5f238e0 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKitTests.java +++ b/micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKitTests.java @@ -29,7 +29,7 @@ public void onStart(Observation sample, Observation.Context handlerContext) { } @Override - public void onError(Observation sample, Observation.Context handlerContext, Throwable throwable) { + public void onError(Observation sample, Observation.Context handlerContext) { } diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/NullContextTimerRecordingHandlerCompatibilityKitTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/NullContextTimerRecordingHandlerCompatibilityKitTests.java index 00f9550079..d79f2f7c8f 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/tck/NullContextTimerRecordingHandlerCompatibilityKitTests.java +++ b/micrometer-test/src/test/java/io/micrometer/core/tck/NullContextTimerRecordingHandlerCompatibilityKitTests.java @@ -29,7 +29,7 @@ public void onStart(Observation sample, Observation.Context handlerContext) { } @Override - public void onError(Observation sample, Observation.Context handlerContext, Throwable throwable) { + public void onError(Observation sample, Observation.Context handlerContext) { } diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKitTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKitTests.java index ed863f154b..765cd0ac6d 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKitTests.java +++ b/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKitTests.java @@ -16,15 +16,10 @@ package io.micrometer.core.tck; import java.time.Duration; -import java.time.temporal.TemporalUnit; -import io.micrometer.api.instrument.observation.Observation; import io.micrometer.api.instrument.observation.ObservationRegistry; import io.micrometer.api.instrument.observation.SimpleObservationRegistry; import io.micrometer.api.instrument.simple.SimpleConfig; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; class ObservationRegistryCompatibilityKitTests extends ObservationRegistryCompatibilityKit { diff --git a/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java b/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java index 39e277831b..c9be41b32b 100644 --- a/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java +++ b/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java @@ -73,8 +73,8 @@ public void onStart(Observation observation, CustomContext context) { } @Override - public void onError(Observation observation, CustomContext context, Throwable throwable) { - System.out.println("error: " + observation + " " + throwable); + public void onError(Observation observation, CustomContext context) { + System.out.println("error: " + observation + " " + context.getError()); } @Override From 8a9e93145b90a1737750bbd8d4862f2eac441736 Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Tue, 1 Feb 2022 19:13:37 -0800 Subject: [PATCH 4/6] Remove observation from the handler API: it's not useful since the observation does not have any getters --- .../observation/ObservationHandler.java | 50 +++++++++---------- .../observation/SimpleObservation.java | 10 ++-- .../observation/TimerObservationHandler.java | 6 +-- ...ngCompositeTimerRecordingHandlerTests.java | 28 +++++------ ...chingCompositeObservationHandlerTests.java | 28 +++++------ ...TimerRecordingHandlerCompatibilityKit.java | 8 +-- ...extObservationHandlerCompatibilityKit.java | 8 +-- ...TimerRecordingHandlerCompatibilityKit.java | 8 +-- .../ObservationRegistryCompatibilityKit.java | 9 ++-- ...RecordingHandlerCompatibilityKitTests.java | 8 +-- ...servationHandlerCompatibilityKitTests.java | 8 +-- ...RecordingHandlerCompatibilityKitTests.java | 8 +-- .../samples/ObservationHandlerSample.java | 20 ++++---- 13 files changed, 99 insertions(+), 100 deletions(-) diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationHandler.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationHandler.java index ab30eaf6cd..25ec2867e9 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationHandler.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationHandler.java @@ -21,17 +21,17 @@ import java.util.stream.Stream; public interface ObservationHandler { - void onStart(Observation observation, T context); + void onStart(T context); - void onError(Observation observation, T context); + void onError(T context); - default void onScopeOpened(Observation observation, T context) { + default void onScopeOpened(T context) { } - default void onScopeClosed(Observation observation, T context) { + default void onScopeClosed(T context) { } - void onStop(Observation observation, T context); + void onStop(T context); boolean supportsContext(Observation.Context context); @@ -77,28 +77,28 @@ public List getHandlers() { } @Override - public void onStart(Observation observation, Observation.Context context) { - getFirstApplicableHandler(context).ifPresent(handler -> handler.onStart(observation, context)); + public void onStart(Observation.Context context) { + getFirstApplicableHandler(context).ifPresent(handler -> handler.onStart(context)); } @Override - public void onError(Observation observation, Observation.Context context) { - getFirstApplicableHandler(context).ifPresent(handler -> handler.onError(observation, context)); + public void onError(Observation.Context context) { + getFirstApplicableHandler(context).ifPresent(handler -> handler.onError(context)); } @Override - public void onScopeOpened(Observation observation, Observation.Context context) { - getFirstApplicableHandler(context).ifPresent(handler -> handler.onScopeOpened(observation, context)); + public void onScopeOpened(Observation.Context context) { + getFirstApplicableHandler(context).ifPresent(handler -> handler.onScopeOpened(context)); } @Override - public void onScopeClosed(Observation observation, Observation.Context context) { - getFirstApplicableHandler(context).ifPresent(handler -> handler.onScopeClosed(observation, context)); + public void onScopeClosed(Observation.Context context) { + getFirstApplicableHandler(context).ifPresent(handler -> handler.onScopeClosed(context)); } @Override - public void onStop(Observation observation, Observation.Context context) { - getFirstApplicableHandler(context).ifPresent(handler -> handler.onStop(observation, context)); + public void onStop(Observation.Context context) { + getFirstApplicableHandler(context).ifPresent(handler -> handler.onStop(context)); } @Override @@ -140,28 +140,28 @@ public List getHandlers() { } @Override - public void onStart(Observation observation, Observation.Context context) { - getAllApplicableHandlers(context).forEach(handler -> handler.onStart(observation, context)); + public void onStart(Observation.Context context) { + getAllApplicableHandlers(context).forEach(handler -> handler.onStart(context)); } @Override - public void onError(Observation observation, Observation.Context context) { - getAllApplicableHandlers(context).forEach(handler -> handler.onError(observation, context)); + public void onError(Observation.Context context) { + getAllApplicableHandlers(context).forEach(handler -> handler.onError(context)); } @Override - public void onScopeOpened(Observation observation, Observation.Context context) { - getAllApplicableHandlers(context).forEach(handler -> handler.onScopeOpened(observation, context)); + public void onScopeOpened(Observation.Context context) { + getAllApplicableHandlers(context).forEach(handler -> handler.onScopeOpened(context)); } @Override - public void onScopeClosed(Observation observation, Observation.Context context) { - getAllApplicableHandlers(context).forEach(handler -> handler.onScopeClosed(observation, context)); + public void onScopeClosed(Observation.Context context) { + getAllApplicableHandlers(context).forEach(handler -> handler.onScopeClosed(context)); } @Override - public void onStop(Observation observation, Observation.Context context) { - getAllApplicableHandlers(context).forEach(handler -> handler.onStop(observation, context)); + public void onStop(Observation.Context context) { + getAllApplicableHandlers(context).forEach(handler -> handler.onStop(context)); } @Override diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java index 8ec0ed9bcb..dd607be212 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java @@ -93,28 +93,28 @@ public String toString() { @SuppressWarnings("unchecked") private void notifyOnObservationStarted() { - this.handlers.forEach(handler -> handler.onStart(this, this.context)); + this.handlers.forEach(handler -> handler.onStart(this.context)); } @SuppressWarnings("unchecked") private void notifyOnError() { - this.handlers.forEach(handler -> handler.onError(this, this.context)); + this.handlers.forEach(handler -> handler.onError(this.context)); } @SuppressWarnings("unchecked") private void notifyOnScopeOpened() { - this.handlers.forEach(handler -> handler.onScopeOpened(this, this.context)); + this.handlers.forEach(handler -> handler.onScopeOpened(this.context)); } @SuppressWarnings("unchecked") private void notifyOnScopeClosed() { - this.handlers.forEach(handler -> handler.onScopeClosed(this, this.context)); + this.handlers.forEach(handler -> handler.onScopeClosed(this.context)); } @SuppressWarnings("unchecked") private void notifyOnObservationStopped() { // TODO: We're closing from end till beggining - e.g. we started with handlers with ids 1,2,3 and we need to call close on 3,2,1 - this.handlers.descendingIterator().forEachRemaining(handler -> handler.onStop(this, this.context)); + this.handlers.descendingIterator().forEachRemaining(handler -> handler.onStop(this.context)); } static class SimpleScope implements Scope { diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationHandler.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationHandler.java index 4c58f4a090..ac8052be45 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationHandler.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationHandler.java @@ -33,17 +33,17 @@ public TimerObservationHandler(MeterRegistry meterRegistry) { } @Override - public void onStart(Observation observation, Observation.Context context) { + public void onStart(Observation.Context context) { Timer.Sample sample = Timer.start(meterRegistry); context.put(Timer.Sample.class, sample); } @Override - public void onError(Observation observation, Observation.Context context) { + public void onError(Observation.Context context) { } @Override - public void onStop(Observation observation, Observation.Context context) { + public void onStop(Observation.Context context) { Timer.Sample sample = context.get(Timer.Sample.class); sample.stop(Timer.builder(context.getName()) .tag("error", context.getError().map(Throwable::getMessage).orElse("none")) diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/AllMatchingCompositeTimerRecordingHandlerTests.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/AllMatchingCompositeTimerRecordingHandlerTests.java index ec3321f09a..a8026b8a98 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/AllMatchingCompositeTimerRecordingHandlerTests.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/AllMatchingCompositeTimerRecordingHandlerTests.java @@ -35,7 +35,7 @@ void should_run_on_start_for_all_matching_handlers() { AllMatchingCompositeObservationHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler(), this.matchingHandler2); - allMatchingCompositeTimerRecordingHandler.onStart(sample, null); + allMatchingCompositeTimerRecordingHandler.onStart(null); assertThat(this.matchingHandler.started).isTrue(); assertThat(this.matchingHandler2.started).isTrue(); @@ -46,7 +46,7 @@ void should_run_on_stop_for_all_matching_handlers() { AllMatchingCompositeObservationHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler(), this.matchingHandler2); - allMatchingCompositeTimerRecordingHandler.onStop(sample, null); + allMatchingCompositeTimerRecordingHandler.onStop(null); assertThat(this.matchingHandler.stopped).isTrue(); assertThat(this.matchingHandler2.stopped).isTrue(); @@ -57,7 +57,7 @@ void should_run_on_error_for_all_matching_handlers() { AllMatchingCompositeObservationHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler(), this.matchingHandler2); - allMatchingCompositeTimerRecordingHandler.onError(sample, null); + allMatchingCompositeTimerRecordingHandler.onError(null); assertThat(this.matchingHandler.errored).isTrue(); assertThat(this.matchingHandler2.errored).isTrue(); @@ -68,7 +68,7 @@ void should_run_on_scope_opened_for_all_matching_handlers() { AllMatchingCompositeObservationHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler(), this.matchingHandler2); - allMatchingCompositeTimerRecordingHandler.onScopeOpened(sample, null); + allMatchingCompositeTimerRecordingHandler.onScopeOpened(null); assertThat(this.matchingHandler.scopeOpened).isTrue(); assertThat(this.matchingHandler2.scopeOpened).isTrue(); @@ -79,7 +79,7 @@ void should_run_on_scope_closed_for_all_matching_handlers() { AllMatchingCompositeObservationHandler allMatchingCompositeTimerRecordingHandler = new AllMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler(), this.matchingHandler2); - allMatchingCompositeTimerRecordingHandler.onScopeClosed(sample, null); + allMatchingCompositeTimerRecordingHandler.onScopeClosed(null); assertThat(this.matchingHandler.scopeClosed).isTrue(); assertThat(this.matchingHandler2.scopeClosed).isTrue(); @@ -99,27 +99,27 @@ static class MatchingHandler implements ObservationHandler @Override - public void onStart(Observation observation, Observation.Context context) { + public void onStart(Observation.Context context) { this.started = true; } @Override - public void onError(Observation observation, Observation.Context context) { + public void onError(Observation.Context context) { this.errored = true; } @Override - public void onScopeOpened(Observation observation, Observation.Context context) { + public void onScopeOpened(Observation.Context context) { this.scopeOpened = true; } @Override - public void onScopeClosed(Observation observation, Observation.Context context) { + public void onScopeClosed(Observation.Context context) { this.scopeClosed = true; } @Override - public void onStop(Observation observation, Observation.Context context) { + public void onStop(Observation.Context context) { this.stopped = true; } @@ -132,22 +132,22 @@ public boolean supportsContext(Observation.Context handlerContext) { static class NotMatchingHandler implements ObservationHandler { @Override - public void onStart(Observation observation, Observation.Context context) { + public void onStart(Observation.Context context) { throwAssertionError(); } @Override - public void onError(Observation observation, Observation.Context context) { + public void onError(Observation.Context context) { throwAssertionError(); } @Override - public void onScopeOpened(Observation observation, Observation.Context context) { + public void onScopeOpened(Observation.Context context) { throwAssertionError(); } @Override - public void onStop(Observation observation, Observation.Context context) { + public void onStop(Observation.Context context) { throwAssertionError(); } diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/FirstMatchingCompositeObservationHandlerTests.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/FirstMatchingCompositeObservationHandlerTests.java index ef0f4bfd7b..5ff3a3bf12 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/FirstMatchingCompositeObservationHandlerTests.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/FirstMatchingCompositeObservationHandlerTests.java @@ -33,7 +33,7 @@ void should_run_on_start_only_for_first_matching_handler() { FirstMatchingCompositeObservationHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler()); - firstMatchingCompositeTimerRecordingHandler.onStart(sample, null); + firstMatchingCompositeTimerRecordingHandler.onStart(null); assertThat(this.matchingHandler.started).isTrue(); } @@ -43,7 +43,7 @@ void should_run_on_stop_only_for_first_matching_handler() { FirstMatchingCompositeObservationHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler()); - firstMatchingCompositeTimerRecordingHandler.onStop(sample, null); + firstMatchingCompositeTimerRecordingHandler.onStop(null); assertThat(this.matchingHandler.stopped).isTrue(); } @@ -53,7 +53,7 @@ void should_run_on_error_only_for_first_matching_handler() { FirstMatchingCompositeObservationHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler()); - firstMatchingCompositeTimerRecordingHandler.onError(sample, null); + firstMatchingCompositeTimerRecordingHandler.onError(null); assertThat(this.matchingHandler.errored).isTrue(); } @@ -63,7 +63,7 @@ void should_run_on_scope_opened_only_for_first_matching_handler() { FirstMatchingCompositeObservationHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler()); - firstMatchingCompositeTimerRecordingHandler.onScopeOpened(sample, null); + firstMatchingCompositeTimerRecordingHandler.onScopeOpened(null); assertThat(this.matchingHandler.scopeOpened).isTrue(); } @@ -73,7 +73,7 @@ void should_run_on_scope_closed_only_for_first_matching_handler() { FirstMatchingCompositeObservationHandler firstMatchingCompositeTimerRecordingHandler = new FirstMatchingCompositeObservationHandler( new NotMatchingHandler(), this.matchingHandler, new NotMatchingHandler()); - firstMatchingCompositeTimerRecordingHandler.onScopeClosed(sample, null); + firstMatchingCompositeTimerRecordingHandler.onScopeClosed(null); assertThat(this.matchingHandler.scopeClosed).isTrue(); } @@ -92,27 +92,27 @@ static class MatchingHandler implements ObservationHandler @Override - public void onStart(Observation observation, Observation.Context context) { + public void onStart(Observation.Context context) { this.started = true; } @Override - public void onError(Observation observation, Observation.Context context) { + public void onError(Observation.Context context) { this.errored = true; } @Override - public void onScopeOpened(Observation observation, Observation.Context context) { + public void onScopeOpened(Observation.Context context) { this.scopeOpened = true; } @Override - public void onScopeClosed(Observation observation, Observation.Context context) { + public void onScopeClosed(Observation.Context context) { this.scopeClosed = true; } @Override - public void onStop(Observation observation, Observation.Context context) { + public void onStop(Observation.Context context) { this.stopped = true; } @@ -125,22 +125,22 @@ public boolean supportsContext(Observation.Context handlerContext) { static class NotMatchingHandler implements ObservationHandler { @Override - public void onStart(Observation observation, Observation.Context context) { + public void onStart(Observation.Context context) { throwAssertionError(); } @Override - public void onError(Observation observation, Observation.Context context) { + public void onError(Observation.Context context) { throwAssertionError(); } @Override - public void onScopeOpened(Observation observation, Observation.Context context) { + public void onScopeOpened(Observation.Context context) { throwAssertionError(); } @Override - public void onStop(Observation observation, Observation.Context context) { + public void onStop(Observation.Context context) { throwAssertionError(); } diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKit.java index f9e2117f09..a9ec655696 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKit.java @@ -37,10 +37,10 @@ public abstract class AnyHandlerContextTimerRecordingHandlerCompatibilityKit ext @DisplayName("compatibility test provides a test context accepting timer recording handler") void handlerSupportsAnyContext() { TestHandlerContext testContext = new TestHandlerContext(); - assertThatCode(() -> handler.onStart(sample, testContext)).doesNotThrowAnyException(); - assertThatCode(() -> handler.onStop(sample, testContext)).doesNotThrowAnyException(); - assertThatCode(() -> handler.onError(sample, testContext)).doesNotThrowAnyException(); - assertThatCode(() -> handler.onScopeOpened(sample, testContext)).doesNotThrowAnyException(); + assertThatCode(() -> handler.onStart(testContext)).doesNotThrowAnyException(); + assertThatCode(() -> handler.onStop(testContext)).doesNotThrowAnyException(); + assertThatCode(() -> handler.onError(testContext)).doesNotThrowAnyException(); + assertThatCode(() -> handler.onScopeOpened(testContext)).doesNotThrowAnyException(); assertThatCode(() -> handler.supportsContext(testContext)).doesNotThrowAnyException(); assertThat(handler.supportsContext(testContext)).as("Handler supports any context").isTrue(); } diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKit.java index fad0a29629..502458100f 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKit.java @@ -55,10 +55,10 @@ void setup() { @Test @DisplayName("compatibility test provides a concrete context accepting timer recording handler") void handlerSupportsConcreteContextForHandlerMethods() { - assertThatCode(() -> handler.onStart(sample, context())).doesNotThrowAnyException(); - assertThatCode(() -> handler.onStop(sample, context())).doesNotThrowAnyException(); - assertThatCode(() -> handler.onError(sample, context())).doesNotThrowAnyException(); - assertThatCode(() -> handler.onScopeOpened(sample, context())).doesNotThrowAnyException(); + assertThatCode(() -> handler.onStart(context())).doesNotThrowAnyException(); + assertThatCode(() -> handler.onStop(context())).doesNotThrowAnyException(); + assertThatCode(() -> handler.onError(context())).doesNotThrowAnyException(); + assertThatCode(() -> handler.onScopeOpened(context())).doesNotThrowAnyException(); } @Test diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java index 8fd5f843cf..c59b7eeefa 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java @@ -53,10 +53,10 @@ void setup() { @Test @DisplayName("compatibility test provides a null context accepting timer recording handler") void handlerSupportsNullContext() { - assertThatCode(() -> handler.onStart(sample, null)).doesNotThrowAnyException(); - assertThatCode(() -> handler.onStop(sample, null)).doesNotThrowAnyException(); - assertThatCode(() -> handler.onError(sample, null)).doesNotThrowAnyException(); - assertThatCode(() -> handler.onScopeOpened(sample, null)).doesNotThrowAnyException(); + assertThatCode(() -> handler.onStart(null)).doesNotThrowAnyException(); + assertThatCode(() -> handler.onStop(null)).doesNotThrowAnyException(); + assertThatCode(() -> handler.onError(null)).doesNotThrowAnyException(); + assertThatCode(() -> handler.onScopeOpened(null)).doesNotThrowAnyException(); assertThatCode(() -> handler.supportsContext(null)).doesNotThrowAnyException(); assertThat(handler.supportsContext(null)).as("Handler supports null context").isTrue(); } diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java index 342f73b52e..f595fae03d 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java @@ -29,7 +29,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -70,19 +69,19 @@ void recordWithHandlers() { Observation observation = registry.start("myObservation"); verify(handler).supportsContext(isA(Observation.Context.class)); - verify(handler).onStart(same(observation), isA(Observation.Context.class)); + verify(handler).onStart(isA(Observation.Context.class)); verify(handlerThatHandlesNothing).supportsContext(isA(Observation.Context.class)); verifyNoMoreInteractions(handlerThatHandlesNothing); try (Observation.Scope scope = observation.openScope()) { - verify(handler).onScopeOpened(same(observation), isA(Observation.Context.class)); + verify(handler).onScopeOpened(isA(Observation.Context.class)); assertThat(scope.getCurrentObservation()).isSameAs(observation); Throwable exception = new IOException("simulated"); observation.error(exception); - verify(handler).onError(same(observation), isA(Observation.Context.class)); + verify(handler).onError(isA(Observation.Context.class)); } - verify(handler).onScopeClosed(same(observation), isA(Observation.Context.class)); + verify(handler).onScopeClosed(isA(Observation.Context.class)); observation.stop(); } } diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKitTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKitTests.java index 9399cff4e1..bebed06af9 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKitTests.java +++ b/micrometer-test/src/test/java/io/micrometer/core/tck/AnyHandlerContextTimerRecordingHandlerCompatibilityKitTests.java @@ -24,22 +24,22 @@ class AnyHandlerContextTimerRecordingHandlerCompatibilityKitTests extends AnyHan public ObservationHandler handler() { return new ObservationHandler() { @Override - public void onStart(Observation sample, Observation.Context handlerContext) { + public void onStart(Observation.Context handlerContext) { } @Override - public void onError(Observation sample, Observation.Context handlerContext) { + public void onError(Observation.Context handlerContext) { } @Override - public void onScopeOpened(Observation sample, Observation.Context handlerContext) { + public void onScopeOpened(Observation.Context handlerContext) { } @Override - public void onStop(Observation sample, Observation.Context handlerContext) { + public void onStop(Observation.Context handlerContext) { } diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKitTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKitTests.java index 2bb5f238e0..68b8ce329b 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKitTests.java +++ b/micrometer-test/src/test/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKitTests.java @@ -24,22 +24,22 @@ class ConcreteHandlerContextObservationHandlerCompatibilityKitTests extends Conc public ObservationHandler handler() { return new ObservationHandler() { @Override - public void onStart(Observation sample, Observation.Context handlerContext) { + public void onStart(Observation.Context handlerContext) { } @Override - public void onError(Observation sample, Observation.Context handlerContext) { + public void onError(Observation.Context handlerContext) { } @Override - public void onScopeOpened(Observation sample, Observation.Context handlerContext) { + public void onScopeOpened(Observation.Context handlerContext) { } @Override - public void onStop(Observation sample, Observation.Context handlerContext) { + public void onStop(Observation.Context handlerContext) { } diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/NullContextTimerRecordingHandlerCompatibilityKitTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/NullContextTimerRecordingHandlerCompatibilityKitTests.java index d79f2f7c8f..aef4f9087c 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/tck/NullContextTimerRecordingHandlerCompatibilityKitTests.java +++ b/micrometer-test/src/test/java/io/micrometer/core/tck/NullContextTimerRecordingHandlerCompatibilityKitTests.java @@ -24,22 +24,22 @@ class NullContextTimerRecordingHandlerCompatibilityKitTests extends NullHandlerC public ObservationHandler handler() { return new ObservationHandler() { @Override - public void onStart(Observation sample, Observation.Context handlerContext) { + public void onStart(Observation.Context handlerContext) { } @Override - public void onError(Observation sample, Observation.Context handlerContext) { + public void onError(Observation.Context handlerContext) { } @Override - public void onScopeOpened(Observation sample, Observation.Context handlerContext) { + public void onScopeOpened(Observation.Context handlerContext) { } @Override - public void onStop(Observation sample, Observation.Context handlerContext) { + public void onStop(Observation.Context handlerContext) { } diff --git a/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java b/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java index c9be41b32b..47e5a09f68 100644 --- a/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java +++ b/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java @@ -65,31 +65,31 @@ public static void main(String[] args) throws InterruptedException { static class SampleHandler implements ObservationHandler { @Override - public void onStart(Observation observation, CustomContext context) { + public void onStart(CustomContext context) { if (context.getName().equals("sample.ignored")) { throw new AssertionError("Boom!"); } - System.out.println("start: " + observation); + System.out.println("start: " + context); } @Override - public void onError(Observation observation, CustomContext context) { - System.out.println("error: " + observation + " " + context.getError()); + public void onError(CustomContext context) { + System.out.println("error: " + context.getError() + " " + context.getError()); } @Override - public void onScopeOpened(Observation observation, CustomContext context) { - System.out.println("context-opened: " + observation); + public void onScopeOpened(CustomContext context) { + System.out.println("scope-opened: " + context); } @Override - public void onScopeClosed(Observation observation, CustomContext context) { - System.out.println("context-closed: " + observation); + public void onScopeClosed(CustomContext context) { + System.out.println("scope-closed: " + context); } @Override - public void onStop(Observation observation, CustomContext context) { - System.out.println("stop: " + observation); + public void onStop(CustomContext context) { + System.out.println("stop: " + context); } @Override From 65af70cd3a573951ebfe1d1e1d94e10cc2ea6c56 Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Tue, 1 Feb 2022 19:22:00 -0800 Subject: [PATCH 5/6] displayName -> contextualName --- .../api/instrument/NoopObservation.java | 2 +- .../instrument/observation/Observation.java | 20 +++++++++---------- .../observation/SimpleObservation.java | 6 +++--- .../samples/ObservationHandlerSample.java | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java index 8fa14d3d3e..8cfde27f92 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/NoopObservation.java @@ -25,7 +25,7 @@ private NoopObservation() { } @Override - public Observation displayName(String displayName) { + public Observation contextualName(String contextualName) { return this; } diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java index cbe9fd2e3e..0fe384fb86 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java @@ -30,20 +30,20 @@ /* We moved out all the getters - this API will provide a nice way to set things ON THE CONTEXT -* We've added additionalLowCardinality and high cardinliaty tags on the context, tagsprovider is immutable +We've added additionalLowCardinality and high cardinality tags on the context, tags provider is immutable We remove timing information - there's no gain in unifying the time (e.g. same time for metrics & spans). It's up to the handlers to take control of doing measurements. If a handler is buggy we will see that in its timing information. We removed the throwable getter to explicitly pass the throwable to the handler - that's the only parameter that will never change (onError - you have to have an error there) - */ +*/ public interface Observation { /** * Sets the display name (a more human-readable name). * - * @param displayName display name + * @param contextualName contextual name * @return this */ - Observation displayName(String displayName); + Observation contextualName(String contextualName); /** * Sets an additional low cardinality tag. @@ -125,7 +125,7 @@ class Context implements TagsProvider { private String name; - private String displayName; + private String contextualName; @Nullable private Throwable error; @@ -147,12 +147,12 @@ public Context setName(String name) { return this; } - public String getDisplayName() { - return this.displayName; + public String getContextualName() { + return this.contextualName; } - public Context setDisplayName(String displayName) { - this.displayName = displayName; + public Context setContextualName(String contextualName) { + this.contextualName = contextualName; return this; } @@ -212,7 +212,7 @@ public String toString() { return "Context{" + "map=" + map + ", name='" + name + '\'' + - ", displayName='" + displayName + '\'' + + ", contextualName='" + contextualName + '\'' + ", lowCardinalityTags=" + getLowCardinalityTags() + ", additionalLowCardinalityTags=" + additionalLowCardinalityTags + ", highCardinalityTags=" + getHighCardinalityTags() + diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java index dd607be212..e2bee80dc9 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java @@ -41,8 +41,8 @@ public SimpleObservation(String name, ObservationRegistry registry, Context cont } @Override - public Observation displayName(String displayName) { - this.context.setDisplayName(displayName); + public Observation contextualName(String contextualName) { + this.context.setContextualName(contextualName); return this; } @@ -85,7 +85,7 @@ public Scope openScope() { @Override public String toString() { return "{" - + "name=" + this.context.getName() + "(" + this.context.getDisplayName() + ")" + + "name=" + this.context.getName() + "(" + this.context.getContextualName() + ")" + ", error=" + this.context.getError() + ", context=" + this.context + '}'; diff --git a/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java b/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java index 47e5a09f68..cf7c450008 100644 --- a/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java +++ b/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java @@ -43,7 +43,7 @@ public static void main(String[] args) throws InterruptedException { }); Observation observation = observationRegistry.observation("sample.operation", new CustomContext()) - .displayName("CALL sampleOperation") + .contextualName("CALL sampleOperation") .lowCardinalityTag("a", "1") .highCardinalityTag("time", Instant.now().toString()) .start(); From d87cad12e89fd29252a55b18f9dc3ecb049df453 Mon Sep 17 00:00:00 2001 From: Tommy Ludwig <8924140+shakuzen@users.noreply.github.com> Date: Thu, 3 Feb 2022 02:20:29 +0900 Subject: [PATCH 6/6] MeterRegistry implements ObservationRegistry, static factory methods Keeps only one registry implementation while still providing a specific type for ObservationRegistry where Observation-specific concerns can be defined. Forces instantiation of Observation through its static methods so there is no direct instantiation by users. This provides fewer ways to do the same thing and exposes API, which should help guide users and hide implementation details. --- .../api/instrument/MeterRegistry.java | 30 +++++++++- .../instrument/observation/Observation.java | 28 +++++++++ .../observation/ObservationRegistry.java | 58 ++----------------- .../observation/SimpleObservation.java | 11 ++-- .../SimpleObservationRegistry.java | 43 -------------- .../observation/TimerObservationRegistry.java | 27 --------- ...ngCompositeTimerRecordingHandlerTests.java | 5 +- .../observation/CurrentObservationTest.java | 13 +++-- ...chingCompositeObservationHandlerTests.java | 5 +- .../observation/ObservationRegistryTest.java | 17 +++--- ...extObservationHandlerCompatibilityKit.java | 6 +- ...TimerRecordingHandlerCompatibilityKit.java | 6 +- .../ObservationRegistryCompatibilityKit.java | 6 +- .../tck/ObservationRegistryAssertTests.java | 6 +- ...ervationRegistryCompatibilityKitTests.java | 4 +- .../samples/ObservationHandlerSample.java | 17 +++--- 16 files changed, 108 insertions(+), 174 deletions(-) delete mode 100644 micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservationRegistry.java delete mode 100644 micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationRegistry.java diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/MeterRegistry.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/MeterRegistry.java index 34569af500..d061bf41a7 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/MeterRegistry.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/MeterRegistry.java @@ -32,6 +32,9 @@ import io.micrometer.api.instrument.noop.NoopMeter; import io.micrometer.api.instrument.noop.NoopTimeGauge; import io.micrometer.api.instrument.noop.NoopTimer; +import io.micrometer.api.instrument.observation.Observation; +import io.micrometer.api.instrument.observation.ObservationRegistry; +import io.micrometer.api.instrument.observation.TimerObservationHandler; import io.micrometer.api.instrument.search.MeterNotFoundException; import io.micrometer.api.instrument.search.RequiredSearch; import io.micrometer.api.instrument.search.Search; @@ -75,7 +78,7 @@ * @author Jon Schneider * @author Johnny Lim */ -public abstract class MeterRegistry { +public abstract class MeterRegistry implements ObservationRegistry { protected final Clock clock; private final Object meterMapLock = new Object(); private volatile MeterFilter[] filters = new MeterFilter[0]; @@ -85,6 +88,31 @@ public abstract class MeterRegistry { private final Config config = new Config(); private final More more = new More(); + private final ThreadLocal localObservation = new ThreadLocal<>(); + + private final ObservationConfig observationConfig = new ObservationConfig(); + + @Nullable + @Override + public Observation getCurrentObservation() { + return this.localObservation.get(); + } + + @Override + public void setCurrentObservation(@Nullable Observation current) { + this.localObservation.set(current); + } + + @Override + public ObservationConfig observationConfig() { + return this.observationConfig; + } + + //TODO Want this under observationConfig but not sure that's possible + public void withTimerObservationHandler() { + observationConfig().observationHandler(new TimerObservationHandler(this)); + } + // Even though writes are guarded by meterMapLock, iterators across value space are supported // Hence, we use CHM to support that iteration without ConcurrentModificationException risk private final Map meterMap = new ConcurrentHashMap<>(); diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java index 0fe384fb86..5b4b7917ad 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/Observation.java @@ -22,6 +22,7 @@ import java.util.Set; import java.util.function.Function; +import io.micrometer.api.instrument.NoopObservation; import io.micrometer.api.instrument.Tag; import io.micrometer.api.instrument.Tags; import io.micrometer.api.instrument.TagsProvider; @@ -37,6 +38,33 @@ */ public interface Observation { + static Observation start(String name, ObservationRegistry registry) { + return start(name, null, registry); + } + + static Observation start(String name, @Nullable Context context, ObservationRegistry registry) { + return createNotStarted(name, context, registry).start(); + } + + /** + * !!!!!!!!!!!!!!!!!!!! THIS IS NOT STARTED !!!!!!!!!!!!!!!!!!!! + * !!!!!!!!!!!!!!!!!!!! REMEMBER TO CALL START() OTHERWISE YOU WILL FILE ISSUES THAT STUFF IS NOT WORKING !!!!!!!!!!!!!!!!!!!! + */ + static Observation createNotStarted(String name, ObservationRegistry registry) { + return createNotStarted(name, null, registry); + } + + /** + * !!!!!!!!!!!!!!!!!!!! THIS IS NOT STARTED !!!!!!!!!!!!!!!!!!!! + * !!!!!!!!!!!!!!!!!!!! REMEMBER TO CALL START() OTHERWISE YOU WILL FILE ISSUES THAT STUFF IS NOT WORKING !!!!!!!!!!!!!!!!!!!! + */ + static Observation createNotStarted(String name, @Nullable Context context, ObservationRegistry registry) { + if (!registry.observationConfig().isObservationEnabled(name, context)) { + return NoopObservation.INSTANCE; + } + return new SimpleObservation(name, registry, context == null ? new Context() : context); + } + /** * Sets the display name (a more human-readable name). * diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationRegistry.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationRegistry.java index b1c95c6720..16fa9373c5 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationRegistry.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/ObservationRegistry.java @@ -31,60 +31,12 @@ public interface ObservationRegistry { void setCurrentObservation(@Nullable Observation current); - /** - * !!!!!!!!!!!!!!!!!!!! THIS IS NOT STARTED !!!!!!!!!!!!!!!!!!!! - * !!!!!!!!!!!!!!!!!!!! REMEMBER TO CALL START() OTHERWISE YOU WILL FILE ISSUES THAT STUFF IS NOT WORKING !!!!!!!!!!!!!!!!!!!! - * @param name observation name - * @return this - */ - default Observation observation(String name) { - if (config().isObservationEnabled(name, null)) { - return new SimpleObservation(name, this, new Observation.Context()); - } - return NoopObservation.INSTANCE; - } - - /** - * !!!!!!!!!!!!!!!!!!!! THIS IS NOT STARTED !!!!!!!!!!!!!!!!!!!! - * !!!!!!!!!!!!!!!!!!!! REMEMBER TO CALL START() OTHERWISE YOU WILL FILE ISSUES THAT STUFF IS NOT WORKING !!!!!!!!!!!!!!!!!!!! - * @param name observation name - * @param context context - * @return this - */ - default Observation observation(String name, Observation.Context context) { - if (config().isObservationEnabled(name, context)) { - return new SimpleObservation(name, this, context); - } - return NoopObservation.INSTANCE; - } - - /** - * Started observation. - * - * @param name observation name - * @return this - */ - default Observation start(String name) { - return observation(name).start(); - } - - /** - * Started observation. - * - * @param name observation name - * @param context context - * @return this - */ - default Observation start(String name, Observation.Context context) { - return observation(name, context).start(); - } - - Config config(); + ObservationConfig observationConfig(); /** * Access to configuration options for this registry. */ - class Config { + class ObservationConfig { private List> observationHandlers = new CopyOnWriteArrayList<>(); @@ -95,9 +47,8 @@ class Config { * * @param handler handler to add to the current configuration * @return This configuration instance - * @since 2.0.0 */ - public ObservationRegistry.Config observationHandler(ObservationHandler handler) { + public ObservationConfig observationHandler(ObservationHandler handler) { this.observationHandlers.add(handler); return this; } @@ -108,9 +59,8 @@ public ObservationRegistry.Config observationHandler(ObservationHandler handl * * @param predicate predicate * @return This configuration instance - * @since 2.0.0 */ - public ObservationRegistry.Config observationPredicate(BiPredicate predicate) { + public ObservationConfig observationPredicate(BiPredicate predicate) { this.observationPredicates.add(predicate); return this; } diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java index e2bee80dc9..d23516a571 100644 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java +++ b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservation.java @@ -22,20 +22,17 @@ import io.micrometer.api.instrument.Tag; import io.micrometer.api.lang.Nullable; -public class SimpleObservation implements Observation { +class SimpleObservation implements Observation { private final ObservationRegistry registry; private final Context context; @SuppressWarnings("rawtypes") private final Deque handlers; - public SimpleObservation(String name, ObservationRegistry registry) { - this(name, registry, new Context()); - } - - public SimpleObservation(String name, ObservationRegistry registry, Context context) { + // package private so only instantiated by us + SimpleObservation(String name, ObservationRegistry registry, Context context) { this.registry = registry; this.context = context.setName(name); - this.handlers = registry.config().getObservationHandlers().stream() + this.handlers = registry.observationConfig().getObservationHandlers().stream() .filter(handler -> handler.supportsContext(this.context)) .collect(Collectors.toCollection(ArrayDeque::new)); } diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservationRegistry.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservationRegistry.java deleted file mode 100644 index 5d6ebb0967..0000000000 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/SimpleObservationRegistry.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.api.instrument.observation; - -import io.micrometer.api.lang.Nullable; - -/** - */ -public class SimpleObservationRegistry implements ObservationRegistry { - private final ThreadLocal localObservation = new ThreadLocal<>(); - - private final Config config = new Config(); - - @Nullable - @Override - public Observation getCurrentObservation() { - return this.localObservation.get(); - } - - @Override - public void setCurrentObservation(@Nullable Observation current) { - this.localObservation.set(current); - } - - @Override - public Config config() { - return this.config; - } - -} diff --git a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationRegistry.java b/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationRegistry.java deleted file mode 100644 index 6ddff20a81..0000000000 --- a/micrometer-api/src/main/java/io/micrometer/api/instrument/observation/TimerObservationRegistry.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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.api.instrument.observation; - -import io.micrometer.api.instrument.MeterRegistry; - -/** - */ -public class TimerObservationRegistry extends SimpleObservationRegistry { - - public TimerObservationRegistry(MeterRegistry meterRegistry) { - config().observationHandler(new TimerObservationHandler(meterRegistry)); - } -} diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/AllMatchingCompositeTimerRecordingHandlerTests.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/AllMatchingCompositeTimerRecordingHandlerTests.java index a8026b8a98..c622d5fbad 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/AllMatchingCompositeTimerRecordingHandlerTests.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/AllMatchingCompositeTimerRecordingHandlerTests.java @@ -16,6 +16,7 @@ package io.micrometer.api.instrument.observation; import io.micrometer.api.instrument.observation.ObservationHandler.AllMatchingCompositeObservationHandler; +import io.micrometer.api.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -26,9 +27,9 @@ class AllMatchingCompositeTimerRecordingHandlerTests { MatchingHandler matchingHandler2 = new MatchingHandler(); - ObservationRegistry registry = new SimpleObservationRegistry(); + ObservationRegistry registry = new SimpleMeterRegistry(); - Observation sample = registry.start("hello"); + Observation sample = Observation.start("hello", registry); @Test void should_run_on_start_for_all_matching_handlers() { diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/CurrentObservationTest.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/CurrentObservationTest.java index c974246cda..e6fdd9e0cd 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/CurrentObservationTest.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/CurrentObservationTest.java @@ -15,6 +15,7 @@ */ package io.micrometer.api.instrument.observation; +import io.micrometer.api.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; import java.util.HashMap; @@ -24,13 +25,13 @@ import static org.assertj.core.api.Assertions.assertThat; class CurrentObservationTest { - private final ObservationRegistry registry = new SimpleObservationRegistry(); + private final ObservationRegistry registry = new SimpleMeterRegistry(); @Test void nestedSamples_parentChildThreadsInstrumented() throws ExecutionException, InterruptedException { ExecutorService taskRunner = Executors.newSingleThreadExecutor(); - Observation observation = registry.observation("test.observation"); + Observation observation = Observation.createNotStarted("test.observation", registry); System.out.println("Outside task: " + observation); assertThat(registry.getCurrentObservation()).isNull(); try (Observation.Scope scope = observation.openScope()) { @@ -48,7 +49,7 @@ void nestedSamples_parentChildThreadsInstrumented() throws ExecutionException, I void start_thenStopOnChildThread() throws InterruptedException, ExecutionException { ExecutorService executor = Executors.newSingleThreadExecutor(); - Observation observation = registry.observation("test.observation"); + Observation observation = Observation.createNotStarted("test.observation", registry); assertThat(registry.getCurrentObservation()).isNull(); executor.submit(() -> { try (Observation.Scope scope = observation.openScope()) { @@ -68,7 +69,7 @@ void startOnChildThread_thenStopOnSiblingThread() throws InterruptedException, E Map observationMap = new HashMap<>(); executor.submit(() -> { - Observation observation = registry.observation("test.observation"); + Observation observation = Observation.createNotStarted("test.observation", registry); assertThat(registry.getCurrentObservation()).isNull(); observationMap.put("myObservation", observation); }).get(); @@ -87,11 +88,11 @@ void startOnChildThread_thenStopOnSiblingThread() throws InterruptedException, E @Test void nestedSamples_sameThread() { - Observation observation = registry.observation("observation1"); + Observation observation = Observation.createNotStarted("observation1", registry); Observation observation2; assertThat(registry.getCurrentObservation()).isNull(); try (Observation.Scope scope = observation.openScope()) { - observation2 = registry.observation("observation2"); + observation2 = Observation.createNotStarted("observation2", registry); assertThat(registry.getCurrentObservation()).isSameAs(observation); } try (Observation.Scope scope = observation2.openScope()) { diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/FirstMatchingCompositeObservationHandlerTests.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/FirstMatchingCompositeObservationHandlerTests.java index 5ff3a3bf12..91838a6efc 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/FirstMatchingCompositeObservationHandlerTests.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/FirstMatchingCompositeObservationHandlerTests.java @@ -16,6 +16,7 @@ package io.micrometer.api.instrument.observation; import io.micrometer.api.instrument.observation.ObservationHandler.FirstMatchingCompositeObservationHandler; +import io.micrometer.api.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -24,9 +25,9 @@ class FirstMatchingCompositeObservationHandlerTests { MatchingHandler matchingHandler = new MatchingHandler(); - ObservationRegistry registry = new SimpleObservationRegistry(); + ObservationRegistry registry = new SimpleMeterRegistry(); - Observation sample = registry.start("hello"); + Observation sample = Observation.start("hello", registry); @Test void should_run_on_start_only_for_first_matching_handler() { diff --git a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/ObservationRegistryTest.java b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/ObservationRegistryTest.java index 8dd96a4cb7..d0d5074ece 100644 --- a/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/ObservationRegistryTest.java +++ b/micrometer-api/src/test/java/io/micrometer/api/instrument/observation/ObservationRegistryTest.java @@ -17,6 +17,7 @@ import io.micrometer.api.instrument.MeterRegistry; import io.micrometer.api.instrument.NoopObservation; +import io.micrometer.api.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -29,11 +30,11 @@ * @author Johnny Lim */ class ObservationRegistryTest { - private ObservationRegistry registry = new SimpleObservationRegistry(); + private ObservationRegistry registry = new SimpleMeterRegistry(); @Test void openingScopeShouldSetSampleAsCurrent() { - Observation sample = registry.start("test.timer"); + Observation sample = Observation.start("test.timer", registry); Observation.Scope scope = sample.openScope(); assertThat(registry.getCurrentObservation()).isSameAs(sample); @@ -49,19 +50,19 @@ void timerRecordingHandlerShouldAddThePassedHandler() { ObservationHandler handler1 = mock(ObservationHandler.class); ObservationHandler handler2 = mock(ObservationHandler.class); - registry.config().observationHandler(handler1); - assertThat(registry.config().getObservationHandlers()).containsExactly(handler1); + registry.observationConfig().observationHandler(handler1); + assertThat(registry.observationConfig().getObservationHandlers()).containsExactly(handler1); - registry.config().observationHandler(handler2); - assertThat(registry.config().getObservationHandlers()).containsExactlyInAnyOrder(handler1, handler2); + registry.observationConfig().observationHandler(handler2); + assertThat(registry.observationConfig().getObservationHandlers()).containsExactlyInAnyOrder(handler1, handler2); } @Test void observationShouldBeNoOpWhenPredicateApplicable() { - registry.config().observationPredicate((name, context) -> !name.equals("test.timer")); + registry.observationConfig().observationPredicate((name, context) -> !name.equals("test.timer")); - Observation sample = registry.start("test.timer"); + Observation sample = Observation.start("test.timer", registry); assertThat(sample).isSameAs(NoopObservation.INSTANCE); } diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKit.java index 502458100f..c62e3f5f80 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/ConcreteHandlerContextObservationHandlerCompatibilityKit.java @@ -18,7 +18,7 @@ import io.micrometer.api.instrument.observation.Observation; import io.micrometer.api.instrument.observation.ObservationHandler; import io.micrometer.api.instrument.observation.ObservationRegistry; -import io.micrometer.api.instrument.observation.SimpleObservationRegistry; +import io.micrometer.api.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -38,11 +38,11 @@ public abstract class ConcreteHandlerContextObservationHandlerCompatibilityKit handler; - protected ObservationRegistry meterRegistry = new SimpleObservationRegistry(); + protected ObservationRegistry meterRegistry = new SimpleMeterRegistry(); public abstract ObservationHandler handler(); - protected Observation sample = meterRegistry.observation("hello"); + protected Observation sample = Observation.createNotStarted("hello", meterRegistry); public abstract T context(); diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java index c59b7eeefa..944acfe9fd 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/NullHandlerContextTimerRecordingHandlerCompatibilityKit.java @@ -18,7 +18,7 @@ import io.micrometer.api.instrument.observation.Observation; import io.micrometer.api.instrument.observation.ObservationHandler; import io.micrometer.api.instrument.observation.ObservationRegistry; -import io.micrometer.api.instrument.observation.SimpleObservationRegistry; +import io.micrometer.api.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -38,11 +38,11 @@ public abstract class NullHandlerContextTimerRecordingHandlerCompatibilityKit { protected ObservationHandler handler; - protected ObservationRegistry meterRegistry = new SimpleObservationRegistry(); + protected ObservationRegistry meterRegistry = new SimpleMeterRegistry(); public abstract ObservationHandler handler(); - protected Observation sample = meterRegistry.observation("hello"); + protected Observation sample = Observation.createNotStarted("hello", meterRegistry); @BeforeEach void setup() { diff --git a/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java b/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java index f595fae03d..4ae89ac8fc 100644 --- a/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java +++ b/micrometer-test/src/main/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKit.java @@ -62,12 +62,12 @@ void recordWithHandlers() { ObservationHandler handler = mock(ObservationHandler.class); @SuppressWarnings("unchecked") ObservationHandler handlerThatHandlesNothing = mock(ObservationHandler.class); - registry.config().observationHandler(handler); - registry.config().observationHandler(handlerThatHandlesNothing); + registry.observationConfig().observationHandler(handler); + registry.observationConfig().observationHandler(handlerThatHandlesNothing); when(handler.supportsContext(any())).thenReturn(true); when(handlerThatHandlesNothing.supportsContext(any())).thenReturn(false); - Observation observation = registry.start("myObservation"); + Observation observation = Observation.start("myObservation", registry); verify(handler).supportsContext(isA(Observation.Context.class)); verify(handler).onStart(isA(Observation.Context.class)); verify(handlerThatHandlesNothing).supportsContext(isA(Observation.Context.class)); diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryAssertTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryAssertTests.java index 3c0971b1b0..5f3f9fc0b8 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryAssertTests.java +++ b/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryAssertTests.java @@ -17,7 +17,7 @@ import io.micrometer.api.instrument.observation.Observation; import io.micrometer.api.instrument.observation.ObservationRegistry; -import io.micrometer.api.instrument.observation.SimpleObservationRegistry; +import io.micrometer.api.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThatCode; @@ -25,13 +25,13 @@ class ObservationRegistryAssertTests { - ObservationRegistry registry = new SimpleObservationRegistry(); + ObservationRegistry registry = new SimpleMeterRegistry(); ObservationRegistryAssert registryAssert = new ObservationRegistryAssert(registry); @Test void assertionErrorThrownWhenRemainingSampleFound() { - Observation sample = this.registry.start("hello"); + Observation sample = Observation.start("hello", registry); try (Observation.Scope ws = sample.openScope()) { assertThatThrownBy(() -> registryAssert.doesNotHaveRemainingObservation()) diff --git a/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKitTests.java b/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKitTests.java index 765cd0ac6d..8e3f9e1ae6 100644 --- a/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKitTests.java +++ b/micrometer-test/src/test/java/io/micrometer/core/tck/ObservationRegistryCompatibilityKitTests.java @@ -18,12 +18,12 @@ import java.time.Duration; import io.micrometer.api.instrument.observation.ObservationRegistry; -import io.micrometer.api.instrument.observation.SimpleObservationRegistry; import io.micrometer.api.instrument.simple.SimpleConfig; +import io.micrometer.api.instrument.simple.SimpleMeterRegistry; class ObservationRegistryCompatibilityKitTests extends ObservationRegistryCompatibilityKit { - ObservationRegistry registry = new SimpleObservationRegistry(); + ObservationRegistry registry = new SimpleMeterRegistry(); @Override public ObservationRegistry registry() { diff --git a/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java b/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java index cf7c450008..3a061d0300 100644 --- a/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java +++ b/samples/micrometer-samples-core/src/main/java/io/micrometer/core/samples/ObservationHandlerSample.java @@ -22,19 +22,16 @@ import io.micrometer.api.instrument.observation.Observation; import io.micrometer.api.instrument.observation.ObservationHandler; import io.micrometer.api.instrument.Tags; -import io.micrometer.api.instrument.observation.ObservationRegistry; -import io.micrometer.api.instrument.observation.TimerObservationRegistry; import io.micrometer.prometheus.PrometheusConfig; import io.micrometer.prometheus.PrometheusMeterRegistry; public class ObservationHandlerSample { private static final PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); - private static final ObservationRegistry observationRegistry = new TimerObservationRegistry(registry); - public static void main(String[] args) throws InterruptedException { - observationRegistry.config().observationHandler(new SampleHandler()); - observationRegistry.config().observationPredicate((s, context) -> { + registry.withTimerObservationHandler(); + registry.observationConfig().observationHandler(new SampleHandler()); + registry.observationConfig().observationPredicate((s, context) -> { boolean observationEnabled = !"sample.ignored".equals(s); if (!observationEnabled) { System.out.println("Ignoring sample.ignored"); @@ -42,7 +39,7 @@ public static void main(String[] args) throws InterruptedException { return observationEnabled; }); - Observation observation = observationRegistry.observation("sample.operation", new CustomContext()) + Observation observation = Observation.createNotStarted("sample.operation", new CustomContext(), registry) .contextualName("CALL sampleOperation") .lowCardinalityTag("a", "1") .highCardinalityTag("time", Instant.now().toString()) @@ -54,10 +51,10 @@ public static void main(String[] args) throws InterruptedException { } observation.stop(); - observationRegistry.start("sample.operation").stop(); - observationRegistry.start("sample.operation", new UnsupportedHandlerContext()).stop(); + Observation.start("sample.operation", registry).stop(); + Observation.start("sample.operation", new UnsupportedHandlerContext(), registry).stop(); - observationRegistry.start("sample.ignored", new CustomContext()).stop(); + Observation.start("sample.ignored", new CustomContext(), registry).stop(); System.out.println(); System.out.println(registry.scrape());