Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Observe instantaneous events #3247

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import java.util.stream.Collectors;

/**
* Handler for {@link Timer.Sample}.
* Handler for {@link Timer.Sample} and {@link Counter}
*
* WARNING: Since the {@link LongTaskTimer} needs to be created in the {@code onStart}
* method, it can only contain tags that are available by that time. This means that if
Expand All @@ -36,11 +36,11 @@
* @author Jonatan Ivanov
* @since 1.10.0
*/
public class TimerObservationHandler implements MeterObservationHandler<Observation.Context> {
public class DefaultMeterObservationHandler implements MeterObservationHandler<Observation.Context> {

private final MeterRegistry meterRegistry;

public TimerObservationHandler(MeterRegistry meterRegistry) {
public DefaultMeterObservationHandler(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}

Expand All @@ -65,8 +65,8 @@ public void onStop(Observation.Context context) {
}

@Override
public boolean supportsContext(Observation.Context context) {
return true;
public void onEvent(Observation.Event event, Observation.Context context) {
Counter.builder(event.getName()).tags(createTags(context)).register(meterRegistry).increment();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the event is scoped to the Observation, I wonder if the Counter name should somehow include both. observation name + event name, perhaps? I'm not sure how this looks given typical use cases, though, which is probably the best gauge of what makes sense. Unless events are supposed to be standalone things that just happen to occur during an observation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes a lot of sense to me. Since we are attaching an Event to an Observation, I think it is a fair assumption that they belong together.

I can imagine a use-case where Events are just happening standalone, not connected to an Observation but to cover that use-case, I would use a different component (if we want to cover that at all).

A real-life example I can think of is applying the same business logic for two different endpoints or sources: let's say we are accepting messages over HTTP and AMQP but the workflow/business logic is the same for both.

}

private Tags createErrorTags(Observation.Context context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@
*/
public interface MeterObservationHandler<T extends Observation.Context> extends ObservationHandler<T> {

@Override
default boolean supportsContext(Observation.Context context) {
return true;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import io.micrometer.common.KeyValues;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.MockClock;
import io.micrometer.core.instrument.observation.TimerObservationHandler;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
import io.micrometer.core.instrument.simple.SimpleConfig;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import io.micrometer.observation.Observation;
Expand Down Expand Up @@ -76,7 +76,7 @@ private OkHttpObservationInterceptor.Builder defaultInterceptorBuilder() {
@BeforeEach
void setup() {
observationRegistry.observationConfig().observationHandler(testHandler);
observationRegistry.observationConfig().observationHandler(new TimerObservationHandler(registry));
observationRegistry.observationConfig().observationHandler(new DefaultMeterObservationHandler(registry));
observationRegistry.observationConfig().observationHandler(new PropagatingHandler());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ void handlerSupportsAnyContext() {
assertThatCode(() -> handler.onStart(testContext)).doesNotThrowAnyException();
assertThatCode(() -> handler.onStop(testContext)).doesNotThrowAnyException();
assertThatCode(() -> handler.onError(testContext)).doesNotThrowAnyException();
assertThatCode(() -> handler.onEvent(new Observation.Event("testEvent"), testContext))
.doesNotThrowAnyException();
assertThatCode(() -> handler.onScopeOpened(testContext)).doesNotThrowAnyException();
assertThatCode(() -> handler.supportsContext(testContext)).doesNotThrowAnyException();
assertThat(handler.supportsContext(testContext)).as("Handler supports any context").isTrue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ void handlerSupportsConcreteContextForHandlerMethods() {
assertThatCode(() -> handler.onStart(context())).doesNotThrowAnyException();
assertThatCode(() -> handler.onStop(context())).doesNotThrowAnyException();
assertThatCode(() -> handler.onError(context())).doesNotThrowAnyException();
assertThatCode(() -> handler.onEvent(new Observation.Event("testEvent"), context())).doesNotThrowAnyException();
assertThatCode(() -> handler.onScopeOpened(context())).doesNotThrowAnyException();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ void handlerSupportsNullContext() {
assertThatCode(() -> handler.onStart(null)).doesNotThrowAnyException();
assertThatCode(() -> handler.onStop(null)).doesNotThrowAnyException();
assertThatCode(() -> handler.onError(null)).doesNotThrowAnyException();
assertThatCode(() -> handler.onEvent(null, null)).doesNotThrowAnyException();
assertThatCode(() -> handler.onScopeOpened(null)).doesNotThrowAnyException();
assertThatCode(() -> handler.supportsContext(null)).doesNotThrowAnyException();
assertThat(handler.supportsContext(null)).as("Handler supports null context").isTrue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import io.micrometer.common.lang.Nullable;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationRegistry;
Expand Down Expand Up @@ -80,6 +81,10 @@ void observeWithHandlers() {
inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class));
assertThat(scope.getCurrentObservation()).isSameAs(observation);

Observation.Event event = new Observation.Event("testEvent", "event for testing");
observation.event(event);
inOrder.verify(handler).onEvent(same(event), isA(Observation.Context.class));

Throwable exception = new IOException("simulated");
observation.error(exception);
inOrder.verify(handler).onError(isA(Observation.Context.class));
Expand Down Expand Up @@ -455,10 +460,6 @@ public boolean supportsContext(Observation.Context context) {
return context instanceof TestContext;
}

public String getId() {
return this.id;
}

}

static class UnsupportedKeyValuesProvider implements Observation.GlobalKeyValuesProvider<Observation.Context> {
Expand Down Expand Up @@ -490,6 +491,7 @@ static class AssertingHandler implements ObservationHandler<Observation.Context>

private boolean stopped = false;

@Nullable
private Observation.Context context = null;

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public void onStart(Observation.Context context) {
public void onError(Observation.Context context) {
}

@Override
public void onEvent(Observation.Event event, Observation.Context context) {
}

@Override
public void onScopeOpened(Observation.Context context) {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ public void onStart(Observation.Context context) {
public void onError(Observation.Context context) {
}

@Override
public void onEvent(Observation.Event event, Observation.Context context) {
}

@Override
public void onScopeOpened(Observation.Context context) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public void onStart(Observation.Context context) {
public void onError(Observation.Context context) {
}

@Override
public void onEvent(Observation.Event event, Observation.Context context) {
}

@Override
public void onScopeOpened(Observation.Context context) {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ public Observation error(Throwable error) {
return this;
}

@Override
public Observation event(Event event) {
return this;
}

@Override
public Observation start() {
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public interface Observation {
* @param registry observation registry
* @return started observation
*/
static Observation start(String name, @Nullable ObservationRegistry registry) {
static Observation start(String name, ObservationRegistry registry) {
return start(name, null, registry);
}

Expand Down Expand Up @@ -322,12 +322,19 @@ default boolean isNoop() {
Observation keyValuesProvider(KeyValuesProvider<?> keyValuesProvider);

/**
* Sets an error.
* Signals an error.
* @param error error
* @return this
*/
Observation error(Throwable error);

/**
* Signals an arbitrary {@link Event}.
* @param event event
* @return this
*/
Observation event(Event event);

/**
* Starts the observation. Remember to call this method, otherwise timing calculations
* will not take place.
Expand Down Expand Up @@ -576,6 +583,7 @@ class Context implements ContextView {

private String name;

@Nullable
private String contextualName;

@Nullable
Expand Down Expand Up @@ -826,6 +834,56 @@ private String toString(Map<Object, Object> map) {

}

/**
* An arbitrary event that you can extend and signal during an {@link Observation}.
* This helps you to tell to the {@link ObservationHandler} that something happened.
* If you want to signal an exception/error, please use
* {@link Observation#error(Throwable)} instead.
*/
class Event {
jonatan-ivanov marked this conversation as resolved.
Show resolved Hide resolved

private final String name;

private final String contextualName;

/**
* @param name The name of the event.
*/
public Event(String name) {
this(name, name);
}

/**
* @param name The name of the event (should have low cardinality).
* @param contextualName The contextual name of the event (can have high
* cardinality).
*/
public Event(String name, String contextualName) {
this.name = name;
this.contextualName = contextualName;
}

/**
* @return the name of the event.
jonatan-ivanov marked this conversation as resolved.
Show resolved Hide resolved
*/
public String getName() {
return this.name;
}

/**
* @return the contextual name of the event.
jonatan-ivanov marked this conversation as resolved.
Show resolved Hide resolved
*/
public String getContextualName() {
return this.contextualName;
}

@Override
public String toString() {
return "event.name='" + this.name + "', event.contextualName='" + this.contextualName + '\'';
}

}

/**
* Read only view on the {@link Context}.
*/
Expand All @@ -842,6 +900,7 @@ interface ContextView {
* context (e.g. name derived from HTTP request).
* @return contextual name
*/
@Nullable
String getContextualName();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ default void onStart(T context) {
default void onError(T context) {
}

/**
* Reacts to arbitrary {@link Observation.Event}.
* @param event the {@link Observation.Event} that was signaled
* @param context a {@link Observation.Context}
*/
default void onEvent(Observation.Event event, T context) {
}

/**
* Reacts to opening of an {@link Observation.Scope}.
* @param context an {@link Observation.Context}
Expand Down Expand Up @@ -131,6 +139,11 @@ public void onError(Observation.Context context) {
getFirstApplicableHandler(context).ifPresent(handler -> handler.onError(context));
}

@Override
public void onEvent(Observation.Event event, Observation.Context context) {
getFirstApplicableHandler(context).ifPresent(handler -> handler.onEvent(event, context));
}

@Override
public void onScopeOpened(Observation.Context context) {
getFirstApplicableHandler(context).ifPresent(handler -> handler.onScopeOpened(context));
Expand Down Expand Up @@ -200,6 +213,11 @@ public void onError(Observation.Context context) {
getAllApplicableHandlers(context).forEach(handler -> handler.onError(context));
}

@Override
public void onEvent(Observation.Event event, Observation.Context context) {
getAllApplicableHandlers(context).forEach(handler -> handler.onEvent(event, context));
}

@Override
public void onScopeOpened(Observation.Context context) {
getAllApplicableHandlers(context).forEach(handler -> handler.onScopeOpened(context));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ public void onError(Observation.Context context) {
publish("ERROR", context);
}

@Override
public void onEvent(Observation.Event event, Observation.Context context) {
publishUnformatted(String.format("%5s - %s, %s", "EVENT", event, converter.apply(context)));
}

@Override
public void onScopeOpened(Observation.Context context) {
publish("OPEN", context);
Expand All @@ -109,7 +114,11 @@ public boolean supportsContext(Observation.Context context) {
}

private void publish(String event, Observation.Context context) {
this.consumer.accept(String.format("%5s - %s", event, converter.apply(context)));
publishUnformatted(String.format("%5s - %s", event, converter.apply(context)));
}

private void publishUnformatted(String event) {
this.consumer.accept(event);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ public Observation error(Throwable error) {
return this;
}

@Override
public Observation event(Event event) {
this.notifyOnEvent(event);
return this;
}

@Override
public Observation start() {
this.notifyOnObservationStarted();
Expand Down Expand Up @@ -177,6 +183,11 @@ private void notifyOnError() {
this.handlers.forEach(handler -> handler.onError(this.context));
}

@SuppressWarnings("unchecked")
private void notifyOnEvent(Event event) {
this.handlers.forEach(handler -> handler.onEvent(event, this.context));
}

@SuppressWarnings("unchecked")
private void notifyOnScopeOpened() {
this.handlers.forEach(handler -> handler.onScopeOpened(this.context));
Expand Down
Loading