diff --git a/micrometer-observation/src/main/java/io/micrometer/observation/annotation/Observed.java b/micrometer-observation/src/main/java/io/micrometer/observation/annotation/Observed.java index 89bafaf7fb..174d0571bb 100644 --- a/micrometer-observation/src/main/java/io/micrometer/observation/annotation/Observed.java +++ b/micrometer-observation/src/main/java/io/micrometer/observation/annotation/Observed.java @@ -27,6 +27,7 @@ * Annotation to mark classes and methods that you want to observe. * * @author Jonatan Ivanov + * @author Yanming Zhou * @since 1.10.0 */ @Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD }) @@ -52,4 +53,10 @@ */ String[] lowCardinalityKeyValues() default {}; + /** + * Returns {@code true} if {@link Observation} should not be started without parent. + * @return {@code true} if {@link Observation} should not be started without parent. + */ + boolean withParentOnly() default false; + } diff --git a/micrometer-observation/src/main/java/io/micrometer/observation/aop/ObservedAspect.java b/micrometer-observation/src/main/java/io/micrometer/observation/aop/ObservedAspect.java index 7595a51621..447d29036f 100644 --- a/micrometer-observation/src/main/java/io/micrometer/observation/aop/ObservedAspect.java +++ b/micrometer-observation/src/main/java/io/micrometer/observation/aop/ObservedAspect.java @@ -68,6 +68,7 @@ * * * @author Jonatan Ivanov + * @author Yanming Zhou * @since 1.10.0 */ @Aspect @@ -131,6 +132,9 @@ public Object observeMethod(ProceedingJoinPoint pjp) throws Throwable { private Object observe(ProceedingJoinPoint pjp, Method method, Observed observed) throws Throwable { Observation observation = ObservedAspectObservationDocumentation.of(pjp, observed, this.registry, this.observationConvention); + if (observed.withParentOnly() && observation.getContextView().getParentObservation() == null) { + return pjp.proceed(); + } if (CompletionStage.class.isAssignableFrom(method.getReturnType())) { observation.start(); Observation.Scope scope = observation.openScope(); diff --git a/micrometer-observation/src/test/java/io/micrometer/observation/aop/ObservedAspectTests.java b/micrometer-observation/src/test/java/io/micrometer/observation/aop/ObservedAspectTests.java index 99cda206af..242b0b7b41 100644 --- a/micrometer-observation/src/test/java/io/micrometer/observation/aop/ObservedAspectTests.java +++ b/micrometer-observation/src/test/java/io/micrometer/observation/aop/ObservedAspectTests.java @@ -334,6 +334,35 @@ void skipPredicateShouldTakeEffectForClass() { TestObservationRegistryAssert.assertThat(registry).doesNotHaveAnyObservation(); } + @Test + void withParentOnlyObservationShouldNotBeStartedWithoutParent() { + registry.observationConfig().observationHandler(new ObservationTextPublisher()); + + AspectJProxyFactory pf = new AspectJProxyFactory(new InternalService()); + pf.addAspect(new ObservedAspect(registry)); + + InternalService internalService = pf.getProxy(); + internalService.call(); + + TestObservationRegistryAssert.assertThat(registry).doesNotHaveAnyObservation(); + } + + @Test + void withParentOnlyObservationShouldBeStartedIfParentPresent() { + registry.observationConfig().observationHandler(new ObservationTextPublisher()); + + AspectJProxyFactory pf = new AspectJProxyFactory(new InternalService()); + pf.addAspect(new ObservedAspect(registry)); + InternalService internalService = pf.getProxy(); + pf = new AspectJProxyFactory(new ExternalService(internalService)); + pf.addAspect(new ObservedAspect(registry)); + + ExternalService externalService = pf.getProxy(); + externalService.call(); + + TestObservationRegistryAssert.assertThat(registry).hasNumberOfObservationsEqualTo(2); + } + static class ObservedService { @Observed(name = "test.call", contextualName = "test#call", @@ -443,4 +472,28 @@ public boolean supportsContext(@NonNull Observation.Context context) { } + static class ExternalService { + + private final InternalService internalService; + + ExternalService(InternalService internalService) { + this.internalService = internalService; + } + + @Observed + void call() { + internalService.call(); + } + + } + + static class InternalService { + + @Observed(withParentOnly = true) + void call() { + System.out.println("call"); + } + + } + }