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

Add observe method to the Observation for Runnable and Supplier #3040

Merged
merged 2 commits into from
Feb 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,58 @@ default Observation highCardinalityTag(String key, String value) {
*/
Scope openScope();

/**
* Observes the passed {@link Runnable}, this means the followings:
* - Starts the {@code Observation}
* - Opens a {@code Scope}
* - Calls {@link Runnable#run()}
* - Closes the {@code Scope}
* - Signals the error to the {@code Observation} if any
* - Stops the {@code Observation}
*
* @param runnable the {@link Runnable} to run
*/
default void observe(Runnable runnable) {
jonatan-ivanov marked this conversation as resolved.
Show resolved Hide resolved
this.start();
try (Scope scope = openScope()) {
runnable.run();
}
catch (Exception exception) {
this.error(exception);
throw exception;
}
finally {
this.stop();
}
}

/**
* Observes the passed {@link Supplier}, this means the followings:
* - Starts the {@code Observation}
* - Opens a {@code Scope}
* - Calls {@link Supplier#get()}
* - Closes the {@code Scope}
* - Signals the error to the {@code Observation} if any
* - Stops the {@code Observation}
*
* @param supplier the {@link Supplier} to call
* @param <T> the type parameter of the {@link Supplier}
* @return the result from {@link Supplier#get()}
*/
default <T> T observe(Supplier<T> supplier) {
jonatan-ivanov marked this conversation as resolved.
Show resolved Hide resolved
this.start();
try (Scope scope = openScope()) {
return supplier.get();
}
catch (Exception exception) {
this.error(exception);
throw exception;
}
finally {
this.stop();
}
}

/**
* Wraps the given action in scope.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -90,6 +91,98 @@ void observeWithHandlers() {
observation.stop();
}

@Test
void runnableShouldBeObserved() {
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true);
registry.observationConfig().observationHandler(handler);
Observation observation = Observation.createNotStarted("myObservation", registry);

observation.observe((Runnable) () -> assertThat(registry.getCurrentObservation()).isSameAs(observation));
assertThat(registry.getCurrentObservation()).isNull();

verify(handler).supportsContext(isA(Observation.Context.class));
verify(handler).onStart(isA(Observation.Context.class));
verify(handler).onScopeOpened(isA(Observation.Context.class));
verify(handler).onScopeClosed(isA(Observation.Context.class));
verify(handler, times(0)).onError(isA(Observation.Context.class));
verify(handler).onStop(isA(Observation.Context.class));
}

@Test
void runnableThrowingErrorShouldBeObserved() {
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true);
registry.observationConfig().observationHandler(handler);
Observation observation = Observation.createNotStarted("myObservation", registry);

assertThatThrownBy(() ->
observation.observe((Runnable) () -> {
assertThat(registry.getCurrentObservation()).isSameAs(observation);
throw new RuntimeException("simulated");
})
).isInstanceOf(RuntimeException.class)
.hasMessage("simulated")
.hasNoCause();

assertThat(registry.getCurrentObservation()).isNull();

verify(handler).supportsContext(isA(Observation.Context.class));
verify(handler).onStart(isA(Observation.Context.class));
verify(handler).onScopeOpened(isA(Observation.Context.class));
verify(handler).onScopeClosed(isA(Observation.Context.class));
verify(handler).onError(isA(Observation.Context.class));
verify(handler).onStop(isA(Observation.Context.class));
}

@Test
void supplierShouldBeObserved() {
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
jonatan-ivanov marked this conversation as resolved.
Show resolved Hide resolved
when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true);
registry.observationConfig().observationHandler(handler);
Observation observation = Observation.createNotStarted("myObservation", registry);

String result = observation.observe((Supplier<String>) () -> {
assertThat(registry.getCurrentObservation()).isSameAs(observation);
return "test";
});
assertThat(result).isEqualTo("test");
assertThat(registry.getCurrentObservation()).isNull();

verify(handler).supportsContext(isA(Observation.Context.class));
verify(handler).onStart(isA(Observation.Context.class));
verify(handler).onScopeOpened(isA(Observation.Context.class));
verify(handler).onScopeClosed(isA(Observation.Context.class));
verify(handler, times(0)).onError(isA(Observation.Context.class));
verify(handler).onStop(isA(Observation.Context.class));
}

@Test
void supplierThrowingErrorShouldBeObserved() {
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true);
registry.observationConfig().observationHandler(handler);
Observation observation = Observation.createNotStarted("myObservation", registry);

assertThatThrownBy(() ->
observation.observe((Supplier<String>) () -> {
assertThat(registry.getCurrentObservation()).isSameAs(observation);
throw new RuntimeException("simulated");
})
).isInstanceOf(RuntimeException.class)
.hasMessage("simulated")
.hasNoCause();

assertThat(registry.getCurrentObservation()).isNull();

verify(handler).supportsContext(isA(Observation.Context.class));
verify(handler).onStart(isA(Observation.Context.class));
verify(handler).onScopeOpened(isA(Observation.Context.class));
verify(handler).onScopeClosed(isA(Observation.Context.class));
verify(handler).onError(isA(Observation.Context.class));
verify(handler).onStop(isA(Observation.Context.class));
}

@Test
void runnableShouldBeScoped() {
Observation observation = Observation.start("myObservation", registry);
Expand Down