diff --git a/micrometer-observation-test/src/main/java/io/micrometer/observation/tck/ObservationRegistryCompatibilityKit.java b/micrometer-observation-test/src/main/java/io/micrometer/observation/tck/ObservationRegistryCompatibilityKit.java index 080b298408..788890ff25 100644 --- a/micrometer-observation-test/src/main/java/io/micrometer/observation/tck/ObservationRegistryCompatibilityKit.java +++ b/micrometer-observation-test/src/main/java/io/micrometer/observation/tck/ObservationRegistryCompatibilityKit.java @@ -116,6 +116,27 @@ void runnableShouldBeObserved() { inOrder.verify(handler).onStop(isA(Observation.Context.class)); } + @Test + void wrappedRunnableShouldBeObserved() { + @SuppressWarnings("unchecked") + ObservationHandler handler = mock(ObservationHandler.class); + when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true); + registry.observationConfig().observationHandler(handler); + Observation observation = Observation.createNotStarted("myObservation", registry); + + Runnable runnable = () -> assertThat(registry.getCurrentObservation()).isSameAs(observation); + observation.wrap(runnable).run(); + assertThat(registry.getCurrentObservation()).isNull(); + + InOrder inOrder = inOrder(handler); + inOrder.verify(handler).supportsContext(isA(Observation.Context.class)); + inOrder.verify(handler).onStart(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class)); + inOrder.verify(handler, times(0)).onError(isA(Observation.Context.class)); + inOrder.verify(handler).onStop(isA(Observation.Context.class)); + } + @Test void runnableThrowingErrorShouldBeObserved() { @SuppressWarnings("unchecked") @@ -142,6 +163,32 @@ void runnableThrowingErrorShouldBeObserved() { inOrder.verify(handler).onStop(isA(Observation.Context.class)); } + @Test + void wrappedRunnableThrowingErrorShouldBeObserved() { + @SuppressWarnings("unchecked") + ObservationHandler handler = mock(ObservationHandler.class); + when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true); + registry.observationConfig().observationHandler(handler); + Observation observation = Observation.createNotStarted("myObservation", registry); + + Runnable runnable = () -> { + assertThat(registry.getCurrentObservation()).isSameAs(observation); + throw new RuntimeException("simulated"); + }; + assertThatThrownBy(() -> observation.wrap(runnable).run()).isInstanceOf(RuntimeException.class) + .hasMessage("simulated").hasNoCause(); + + assertThat(registry.getCurrentObservation()).isNull(); + + InOrder inOrder = inOrder(handler); + inOrder.verify(handler).supportsContext(isA(Observation.Context.class)); + inOrder.verify(handler).onStart(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class)); + inOrder.verify(handler).onError(isA(Observation.Context.class)); + inOrder.verify(handler).onStop(isA(Observation.Context.class)); + } + @Test void checkedRunnableShouldBeObserved() throws Throwable { @SuppressWarnings("unchecked") @@ -164,6 +211,28 @@ void checkedRunnableShouldBeObserved() throws Throwable { inOrder.verify(handler).onStop(isA(Observation.Context.class)); } + @Test + void wrappedCheckedRunnableShouldBeObserved() throws Throwable { + @SuppressWarnings("unchecked") + ObservationHandler handler = mock(ObservationHandler.class); + when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true); + registry.observationConfig().observationHandler(handler); + Observation observation = Observation.createNotStarted("myObservation", registry); + + Observation.CheckedRunnable checkedRunnable = () -> assertThat(registry.getCurrentObservation()) + .isSameAs(observation); + observation.wrapChecked(checkedRunnable).run(); + assertThat(registry.getCurrentObservation()).isNull(); + + InOrder inOrder = inOrder(handler); + inOrder.verify(handler).supportsContext(isA(Observation.Context.class)); + inOrder.verify(handler).onStart(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class)); + inOrder.verify(handler, times(0)).onError(isA(Observation.Context.class)); + inOrder.verify(handler).onStop(isA(Observation.Context.class)); + } + @Test void checkedRunnableThrowingErrorShouldBeObserved() { @SuppressWarnings("unchecked") @@ -190,6 +259,32 @@ void checkedRunnableThrowingErrorShouldBeObserved() { inOrder.verify(handler).onStop(isA(Observation.Context.class)); } + @Test + void wrappedCheckedRunnableThrowingErrorShouldBeObserved() { + @SuppressWarnings("unchecked") + ObservationHandler handler = mock(ObservationHandler.class); + when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true); + registry.observationConfig().observationHandler(handler); + Observation observation = Observation.createNotStarted("myObservation", registry); + + Observation.CheckedRunnable checkedRunnable = () -> { + assertThat(registry.getCurrentObservation()).isSameAs(observation); + throw new IOException("simulated"); + }; + assertThatThrownBy(() -> observation.wrapChecked(checkedRunnable).run()).isInstanceOf(IOException.class) + .hasMessage("simulated").hasNoCause(); + + assertThat(registry.getCurrentObservation()).isNull(); + + InOrder inOrder = inOrder(handler); + inOrder.verify(handler).supportsContext(isA(Observation.Context.class)); + inOrder.verify(handler).onStart(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class)); + inOrder.verify(handler).onError(isA(Observation.Context.class)); + inOrder.verify(handler).onStop(isA(Observation.Context.class)); + } + @Test void supplierShouldBeObserved() { @SuppressWarnings("unchecked") @@ -215,6 +310,31 @@ void supplierShouldBeObserved() { inOrder.verify(handler).onStop(isA(Observation.Context.class)); } + @Test + void wrappedSupplierShouldBeObserved() { + @SuppressWarnings("unchecked") + ObservationHandler handler = mock(ObservationHandler.class); + when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true); + registry.observationConfig().observationHandler(handler); + Observation observation = Observation.createNotStarted("myObservation", registry); + + Supplier supplier = () -> { + assertThat(registry.getCurrentObservation()).isSameAs(observation); + return "test"; + }; + String result = observation.wrap(supplier).get(); + assertThat(result).isEqualTo("test"); + assertThat(registry.getCurrentObservation()).isNull(); + + InOrder inOrder = inOrder(handler); + inOrder.verify(handler).supportsContext(isA(Observation.Context.class)); + inOrder.verify(handler).onStart(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class)); + inOrder.verify(handler, times(0)).onError(isA(Observation.Context.class)); + inOrder.verify(handler).onStop(isA(Observation.Context.class)); + } + @Test void supplierThrowingErrorShouldBeObserved() { @SuppressWarnings("unchecked") @@ -241,6 +361,32 @@ void supplierThrowingErrorShouldBeObserved() { inOrder.verify(handler).onStop(isA(Observation.Context.class)); } + @Test + void wrappedSupplierThrowingErrorShouldBeObserved() { + @SuppressWarnings("unchecked") + ObservationHandler handler = mock(ObservationHandler.class); + when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true); + registry.observationConfig().observationHandler(handler); + Observation observation = Observation.createNotStarted("myObservation", registry); + + Supplier supplier = () -> { + assertThat(registry.getCurrentObservation()).isSameAs(observation); + throw new RuntimeException("simulated"); + }; + assertThatThrownBy(() -> observation.wrap(supplier).get()).isInstanceOf(RuntimeException.class) + .hasMessage("simulated").hasNoCause(); + + assertThat(registry.getCurrentObservation()).isNull(); + + InOrder inOrder = inOrder(handler); + inOrder.verify(handler).supportsContext(isA(Observation.Context.class)); + inOrder.verify(handler).onStart(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class)); + inOrder.verify(handler).onError(isA(Observation.Context.class)); + inOrder.verify(handler).onStop(isA(Observation.Context.class)); + } + @Test void callableShouldBeObserved() throws Throwable { @SuppressWarnings("unchecked") @@ -266,6 +412,31 @@ void callableShouldBeObserved() throws Throwable { inOrder.verify(handler).onStop(isA(Observation.Context.class)); } + @Test + void wrappedCallableShouldBeObserved() throws Throwable { + @SuppressWarnings("unchecked") + ObservationHandler handler = mock(ObservationHandler.class); + when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true); + registry.observationConfig().observationHandler(handler); + Observation observation = Observation.createNotStarted("myObservation", registry); + + Observation.CheckedCallable callable = () -> { + assertThat(registry.getCurrentObservation()).isSameAs(observation); + return "test"; + }; + String result = observation.wrapChecked(callable).call(); + assertThat(result).isEqualTo("test"); + assertThat(registry.getCurrentObservation()).isNull(); + + InOrder inOrder = inOrder(handler); + inOrder.verify(handler).supportsContext(isA(Observation.Context.class)); + inOrder.verify(handler).onStart(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class)); + inOrder.verify(handler, times(0)).onError(isA(Observation.Context.class)); + inOrder.verify(handler).onStop(isA(Observation.Context.class)); + } + @Test void callableThrowingErrorShouldBeObserved() { @SuppressWarnings("unchecked") @@ -292,6 +463,32 @@ void callableThrowingErrorShouldBeObserved() { inOrder.verify(handler).onStop(isA(Observation.Context.class)); } + @Test + void wrappedCallableThrowingErrorShouldBeObserved() { + @SuppressWarnings("unchecked") + ObservationHandler handler = mock(ObservationHandler.class); + when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true); + registry.observationConfig().observationHandler(handler); + Observation observation = Observation.createNotStarted("myObservation", registry); + + Observation.CheckedCallable callable = () -> { + assertThat(registry.getCurrentObservation()).isSameAs(observation); + throw new IOException("simulated"); + }; + assertThatThrownBy(() -> observation.wrapChecked(callable).call()).isInstanceOf(IOException.class) + .hasMessage("simulated").hasNoCause(); + + assertThat(registry.getCurrentObservation()).isNull(); + + InOrder inOrder = inOrder(handler); + inOrder.verify(handler).supportsContext(isA(Observation.Context.class)); + inOrder.verify(handler).onStart(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class)); + inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class)); + inOrder.verify(handler).onError(isA(Observation.Context.class)); + inOrder.verify(handler).onStop(isA(Observation.Context.class)); + } + @Test void runnableShouldBeScoped() { ObservationHandler handler = mock(ObservationHandler.class); diff --git a/micrometer-observation/src/main/java/io/micrometer/observation/Observation.java b/micrometer-observation/src/main/java/io/micrometer/observation/Observation.java index 487e5775cc..14cb14efa1 100644 --- a/micrometer-observation/src/main/java/io/micrometer/observation/Observation.java +++ b/micrometer-observation/src/main/java/io/micrometer/observation/Observation.java @@ -411,6 +411,10 @@ default void observe(Runnable runnable) { } } + default Runnable wrap(Runnable runnable) { + return () -> observe(runnable); + } + /** * Observes the passed {@link CheckedRunnable}, this means the followings: * @@ -440,6 +444,10 @@ default void observeChecked(CheckedRunnable checkedRunn } } + default CheckedRunnable wrapChecked(CheckedRunnable checkedRunnable) throws E { + return () -> observeChecked(checkedRunnable); + } + /** * Observes the passed {@link Supplier}, this means the followings: * @@ -470,6 +478,10 @@ default T observe(Supplier supplier) { } } + default Supplier wrap(Supplier supplier) { + return () -> observe(supplier); + } + /** * Observes the passed {@link CheckedCallable}, this means the followings: * @@ -501,6 +513,10 @@ default T observeChecked(CheckedCallable checkedC } } + default CheckedCallable wrapChecked(CheckedCallable checkedCallable) throws E { + return () -> observeChecked(checkedCallable); + } + /** * Wraps the given action in a scope and signals an error. * @param runnable the {@link Runnable} to run