Skip to content

Commit 83787ed

Browse files
Scoped checked actions (#3323)
* Add methods similar to scoped and tryScoped that can handle checked calls * Add tests * Add more verifications to scoped tests in TCK
1 parent 06a237b commit 83787ed

File tree

2 files changed

+240
-20
lines changed

2 files changed

+240
-20
lines changed

micrometer-observation-test/src/main/java/io/micrometer/observation/tck/ObservationRegistryCompatibilityKit.java

Lines changed: 168 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -293,54 +293,172 @@ void callableThrowingErrorShouldBeObserved() {
293293

294294
@Test
295295
void runnableShouldBeScoped() {
296-
registry.observationConfig().observationHandler(c -> true);
296+
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
297+
when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true);
298+
registry.observationConfig().observationHandler(handler);
297299
Observation observation = Observation.start("myObservation", registry);
298300
Runnable runnable = () -> assertThat(registry.getCurrentObservation()).isSameAs(observation);
299301
observation.scoped(runnable);
300302
assertThat(registry.getCurrentObservation()).isNull();
303+
assertThat(observation.getContext().getError()).isEmpty();
304+
305+
InOrder inOrder = inOrder(handler);
306+
inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class));
307+
inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class));
308+
inOrder.verify(handler, times(0)).onError(isA(Observation.Context.class));
309+
inOrder.verify(handler, times(0)).onStop(isA(Observation.Context.class));
301310
}
302311

303312
@Test
304313
void errorShouldBeReportedOnFailingScopedRunnable() {
305-
registry.observationConfig().observationHandler(c -> true);
306-
Observation.Context context = new Observation.Context();
307-
Observation observation = Observation.start("myObservation", context, registry);
314+
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
315+
when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true);
316+
registry.observationConfig().observationHandler(handler);
317+
Observation observation = Observation.start("myObservation", registry);
308318
RuntimeException error = new RuntimeException("simulated");
309319

310320
Runnable runnable = () -> {
321+
assertThat(registry.getCurrentObservation()).isSameAs(observation);
311322
throw error;
312323
};
313324
assertThatThrownBy(() -> observation.scoped(runnable)).isSameAs(error);
314-
assertThat(context.getError()).containsSame(error);
315325
assertThat(registry.getCurrentObservation()).isNull();
326+
assertThat(observation.getContext().getError()).containsSame(error);
327+
328+
InOrder inOrder = inOrder(handler);
329+
inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class));
330+
inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class));
331+
inOrder.verify(handler).onError(isA(Observation.Context.class));
332+
inOrder.verify(handler, times(0)).onStop(isA(Observation.Context.class));
333+
}
334+
335+
@Test
336+
void checkedRunnableShouldBeScoped() throws Throwable {
337+
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
338+
when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true);
339+
registry.observationConfig().observationHandler(handler);
340+
Observation observation = Observation.start("myObservation", registry);
341+
Observation.CheckedRunnable runnable = () -> assertThat(registry.getCurrentObservation()).isSameAs(observation);
342+
observation.scopedChecked(runnable);
343+
assertThat(registry.getCurrentObservation()).isNull();
344+
assertThat(observation.getContext().getError()).isEmpty();
345+
346+
InOrder inOrder = inOrder(handler);
347+
inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class));
348+
inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class));
349+
inOrder.verify(handler, times(0)).onError(isA(Observation.Context.class));
350+
inOrder.verify(handler, times(0)).onStop(isA(Observation.Context.class));
351+
}
352+
353+
@Test
354+
void errorShouldBeReportedOnFailingScopedCheckedRunnable() {
355+
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
356+
when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true);
357+
registry.observationConfig().observationHandler(handler);
358+
Observation observation = Observation.start("myObservation", registry);
359+
RuntimeException error = new RuntimeException("simulated");
360+
361+
Observation.CheckedRunnable runnable = () -> {
362+
assertThat(registry.getCurrentObservation()).isSameAs(observation);
363+
throw error;
364+
};
365+
assertThatThrownBy(() -> observation.scopedChecked(runnable)).isSameAs(error);
366+
assertThat(registry.getCurrentObservation()).isNull();
367+
assertThat(observation.getContext().getError()).containsSame(error);
368+
369+
InOrder inOrder = inOrder(handler);
370+
inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class));
371+
inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class));
372+
inOrder.verify(handler).onError(isA(Observation.Context.class));
373+
inOrder.verify(handler, times(0)).onStop(isA(Observation.Context.class));
316374
}
317375

318376
@Test
319377
void supplierShouldBeScoped() {
320-
registry.observationConfig().observationHandler(c -> true);
378+
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
379+
when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true);
380+
registry.observationConfig().observationHandler(handler);
321381
Observation observation = Observation.start("myObservation", registry);
322382
Supplier<String> supplier = () -> {
323383
assertThat(registry.getCurrentObservation()).isSameAs(observation);
324384
return "test";
325385
};
326386
String result = observation.scoped(supplier);
327-
assertThat(result).isEqualTo("test");
328387
assertThat(registry.getCurrentObservation()).isNull();
388+
assertThat(result).isEqualTo("test");
389+
390+
InOrder inOrder = inOrder(handler);
391+
inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class));
392+
inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class));
393+
inOrder.verify(handler, times(0)).onError(isA(Observation.Context.class));
394+
inOrder.verify(handler, times(0)).onStop(isA(Observation.Context.class));
329395
}
330396

331397
@Test
332398
void errorShouldBeReportedOnFailingScopedSupplier() {
333-
registry.observationConfig().observationHandler(c -> true);
334-
Observation.Context context = new Observation.Context();
335-
Observation observation = Observation.start("myObservation", context, registry);
399+
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
400+
when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true);
401+
registry.observationConfig().observationHandler(handler);
402+
Observation observation = Observation.start("myObservation", registry);
336403
RuntimeException error = new RuntimeException("simulated");
337404

338405
Supplier<String> supplier = () -> {
406+
assertThat(registry.getCurrentObservation()).isSameAs(observation);
339407
throw error;
340408
};
341409
assertThatThrownBy(() -> observation.scoped(supplier)).isSameAs(error);
342-
assertThat(context.getError()).containsSame(error);
343410
assertThat(registry.getCurrentObservation()).isNull();
411+
assertThat(observation.getContext().getError()).containsSame(error);
412+
413+
InOrder inOrder = inOrder(handler);
414+
inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class));
415+
inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class));
416+
inOrder.verify(handler).onError(isA(Observation.Context.class));
417+
inOrder.verify(handler, times(0)).onStop(isA(Observation.Context.class));
418+
}
419+
420+
@Test
421+
void checkedCallableShouldBeScoped() throws Throwable {
422+
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
423+
when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true);
424+
registry.observationConfig().observationHandler(handler);
425+
Observation observation = Observation.start("myObservation", registry);
426+
Observation.CheckedCallable<String> callable = () -> {
427+
assertThat(registry.getCurrentObservation()).isSameAs(observation);
428+
return "test";
429+
};
430+
String result = observation.scopedChecked(callable);
431+
assertThat(registry.getCurrentObservation()).isNull();
432+
assertThat(result).isEqualTo("test");
433+
434+
InOrder inOrder = inOrder(handler);
435+
inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class));
436+
inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class));
437+
inOrder.verify(handler, times(0)).onError(isA(Observation.Context.class));
438+
inOrder.verify(handler, times(0)).onStop(isA(Observation.Context.class));
439+
}
440+
441+
@Test
442+
void errorShouldBeReportedOnFailingScopedCheckedCallable() {
443+
ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
444+
when(handler.supportsContext(isA(Observation.Context.class))).thenReturn(true);
445+
registry.observationConfig().observationHandler(handler);
446+
Observation observation = Observation.start("myObservation", registry);
447+
RuntimeException error = new RuntimeException("simulated");
448+
449+
Observation.CheckedCallable<String> callable = () -> {
450+
assertThat(registry.getCurrentObservation()).isSameAs(observation);
451+
throw error;
452+
};
453+
assertThatThrownBy(() -> observation.scopedChecked(callable)).isSameAs(error);
454+
assertThat(registry.getCurrentObservation()).isNull();
455+
assertThat(observation.getContext().getError()).containsSame(error);
456+
457+
InOrder inOrder = inOrder(handler);
458+
inOrder.verify(handler).onScopeOpened(isA(Observation.Context.class));
459+
inOrder.verify(handler).onScopeClosed(isA(Observation.Context.class));
460+
inOrder.verify(handler).onError(isA(Observation.Context.class));
461+
inOrder.verify(handler, times(0)).onStop(isA(Observation.Context.class));
344462
}
345463

346464
@Test
@@ -359,6 +477,22 @@ void runnableShouldNotBeParentScopedIfParentIsNull() {
359477
assertThat(registry.getCurrentObservation()).isNull();
360478
}
361479

480+
@Test
481+
void checkedRunnableShouldBeParentScoped() throws Throwable {
482+
registry.observationConfig().observationHandler(c -> true);
483+
Observation parent = Observation.start("myObservation", registry);
484+
Observation.CheckedRunnable runnable = () -> assertThat(registry.getCurrentObservation()).isSameAs(parent);
485+
Observation.tryScopedChecked(parent, runnable);
486+
assertThat(registry.getCurrentObservation()).isNull();
487+
}
488+
489+
@Test
490+
void checkedRunnableShouldNotBeParentScopedIfParentIsNull() throws Throwable {
491+
Observation.CheckedRunnable runnable = () -> assertThat(registry.getCurrentObservation()).isNull();
492+
Observation.tryScopedChecked(null, runnable);
493+
assertThat(registry.getCurrentObservation()).isNull();
494+
}
495+
362496
@Test
363497
void supplierShouldBeParentScoped() {
364498
registry.observationConfig().observationHandler(c -> true);
@@ -382,6 +516,29 @@ void supplierShouldNotBeParentScopedIfParentIsNull() {
382516
assertThat(registry.getCurrentObservation()).isNull();
383517
}
384518

519+
@Test
520+
void checkedCallableShouldBeParentScoped() throws Throwable {
521+
registry.observationConfig().observationHandler(c -> true);
522+
Observation parent = Observation.start("myObservation", registry);
523+
Observation.CheckedCallable<String> callable = () -> {
524+
assertThat(registry.getCurrentObservation()).isSameAs(parent);
525+
return "test";
526+
};
527+
String result = Observation.tryScopedChecked(parent, callable);
528+
assertThat(result).isEqualTo("test");
529+
assertThat(registry.getCurrentObservation()).isNull();
530+
}
531+
532+
@Test
533+
void checkedCallableShouldNotBeParentScopedIfParentIsNull() throws Throwable {
534+
Observation.CheckedCallable<String> callable = () -> {
535+
assertThat(registry.getCurrentObservation()).isNull();
536+
return "test";
537+
};
538+
Observation.tryScopedChecked(null, callable);
539+
assertThat(registry.getCurrentObservation()).isNull();
540+
}
541+
385542
@Test
386543
void observationFieldsShouldBeSetOnContext() {
387544
AssertingHandler assertingHandler = new AssertingHandler();

micrometer-observation/src/main/java/io/micrometer/observation/Observation.java

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -492,13 +492,13 @@ default <T> T observeChecked(CheckedCallable<T> checkedCallable) throws Throwabl
492492
}
493493

494494
/**
495-
* Wraps the given action in scope.
496-
* @param action action to run
495+
* Wraps the given action in a scope and signals an error.
496+
* @param runnable the {@link Runnable} to run
497497
*/
498498
@SuppressWarnings("unused")
499-
default void scoped(Runnable action) {
499+
default void scoped(Runnable runnable) {
500500
try (Scope scope = openScope()) {
501-
action.run();
501+
runnable.run();
502502
}
503503
catch (Exception exception) {
504504
error(exception);
@@ -507,21 +507,54 @@ default void scoped(Runnable action) {
507507
}
508508

509509
/**
510-
* Wraps the given action in scope.
511-
* @param action action to run
512-
* @return result of the action
510+
* Wraps the given action in a scope and signals an error.
511+
* @param checkedRunnable the {@link CheckedRunnable} to run
512+
*/
513+
@SuppressWarnings("unused")
514+
default void scopedChecked(CheckedRunnable checkedRunnable) throws Throwable {
515+
try (Scope scope = openScope()) {
516+
checkedRunnable.run();
517+
}
518+
catch (Throwable throwable) {
519+
error(throwable);
520+
throw throwable;
521+
}
522+
}
523+
524+
/**
525+
* Wraps the given action in a scope and signals an error.
526+
* @param supplier the {@link Supplier} to call
527+
* @param <T> the type parameter of the {@link Supplier}
528+
* @return the result from {@link Supplier#get()}
513529
*/
514530
@SuppressWarnings("unused")
515-
default <T> T scoped(Supplier<T> action) {
531+
default <T> T scoped(Supplier<T> supplier) {
516532
try (Scope scope = openScope()) {
517-
return action.get();
533+
return supplier.get();
518534
}
519535
catch (Exception exception) {
520536
error(exception);
521537
throw exception;
522538
}
523539
}
524540

541+
/**
542+
* Wraps the given action in a scope and signals an error.
543+
* @param checkedCallable the {@link CheckedCallable} to call
544+
* @param <T> the type parameter of the {@link CheckedCallable}
545+
* @return the result from {@link CheckedCallable#call()}
546+
*/
547+
@SuppressWarnings("unused")
548+
default <T> T scopedChecked(CheckedCallable<T> checkedCallable) throws Throwable {
549+
try (Scope scope = openScope()) {
550+
return checkedCallable.call();
551+
}
552+
catch (Throwable error) {
553+
error(error);
554+
throw error;
555+
}
556+
}
557+
525558
/**
526559
* Tries to run the action against an Observation. If the Observation is null, we just
527560
* run the action, otherwise we run the action in scope.
@@ -537,6 +570,21 @@ static void tryScoped(@Nullable Observation parent, Runnable action) {
537570
}
538571
}
539572

573+
/**
574+
* Tries to run the action against an Observation. If the Observation is null, we just
575+
* run the action, otherwise we run the action in scope.
576+
* @param parent observation, potentially {@code null}
577+
* @param checkedRunnable the {@link CheckedRunnable} to run
578+
*/
579+
static void tryScopedChecked(@Nullable Observation parent, CheckedRunnable checkedRunnable) throws Throwable {
580+
if (parent != null) {
581+
parent.scopedChecked(checkedRunnable);
582+
}
583+
else {
584+
checkedRunnable.run();
585+
}
586+
}
587+
540588
/**
541589
* Tries to run the action against an Observation. If the Observation is null, we just
542590
* run the action, otherwise we run the action in scope.
@@ -551,6 +599,21 @@ static <T> T tryScoped(@Nullable Observation parent, Supplier<T> action) {
551599
return action.get();
552600
}
553601

602+
/**
603+
* Tries to run the action against an Observation. If the Observation is null, we just
604+
* run the action, otherwise we run the action in scope.
605+
* @param parent observation, potentially {@code null}
606+
* @param checkedCallable the {@link CheckedCallable} to call
607+
* @param <T> the type parameter of the {@link CheckedCallable}
608+
* @return the result from {@link CheckedCallable#call()}
609+
*/
610+
static <T> T tryScopedChecked(@Nullable Observation parent, CheckedCallable<T> checkedCallable) throws Throwable {
611+
if (parent != null) {
612+
return parent.scopedChecked(checkedCallable);
613+
}
614+
return checkedCallable.call();
615+
}
616+
554617
/**
555618
* Scope represent an action within which certain resources (e.g. tracing context) are
556619
* put in scope (e.g. in a ThreadLocal). When the scope is closed the resources will

0 commit comments

Comments
 (0)