Skip to content

Make CurrentTraceContext.Builder a bean#35424

Closed
be-hase wants to merge 3 commits intospring-projects:mainfrom
be-hase:improve-brave-auto-configuration
Closed

Make CurrentTraceContext.Builder a bean#35424
be-hase wants to merge 3 commits intospring-projects:mainfrom
be-hase:improve-brave-auto-configuration

Conversation

@be-hase
Copy link
Contributor

@be-hase be-hase commented May 15, 2023

When I did Micrometer Tracing/Observation support on my product, I noticed a part that I want to fix on the spring boot side, so I'm throwing a PR.
( I was wondering if I should start an issue first, but since it is a trivial fix, I will throw a PR directly. Sorry.)

Modifications

BraveAutoConfiguration.java

I would like to put CurrentTraceContext.Builder in the bean and use it.

Why:
Because the framework(armeria) I am using, requires the use of RequestContextCurrentTraceContext instead of ThereadLocalCurrentTraceContext.
This modification will help users who are using something other than the ThreadLocalCurrentTraceContext.

For example, it is also useful when you want to use StrictCurrentTraceContext for debugging purposes.
https://github.com/openzipkin/brave/blob/f4a2c7f7d0b8c2725ffef99fb4a5ccca222b6492/brave/src/main/java/brave/propagation/StrictCurrentTraceContext.java

ZipkinConfigurations.ReporterConfiguration

I want to use a Reporter<Span> that I created myself(bean configuration).

For some reason, the current AsyncReporter<Span> is used, so if I configure the bean by myself, two Reporter<Span> are created.

ToDo

  • If there is no problem with the modification, add the unit test

@pivotal-cla
Copy link

@be-hase Please sign the Contributor License Agreement!

Click here to manually synchronize the status of this Pull Request.

See the FAQ for frequently asked questions.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label May 15, 2023
@mhalbritter mhalbritter added the theme: observability Issues related to observability label May 15, 2023
@mhalbritter mhalbritter changed the title Polish BraveAutoConfiguration & ZipkinConfigurations.ReporterConfiguration Make CurrentTraceContext.Builder a bean May 15, 2023
@ConditionalOnMissingBean
@ConditionalOnBean(Sender.class)
AsyncReporter<Span> spanReporter(Sender sender, BytesEncoder<Span> encoder) {
Reporter<Span> spanReporter(Sender sender, BytesEncoder<Span> encoder) {
Copy link
Member

Choose a reason for hiding this comment

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

I think this could be considered a bug and, therefore, it should be handled separately. Could you please split this out into a new PR, along with a test that verifies that the auto-configured spanReporter bean backs off when you define a custom Reporter bean?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK !

Copy link
Contributor Author

Choose a reason for hiding this comment

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

submit: #35455

scopeDecorators.forEach(currentTraceContextBuilder::addScopeDecorator);
for (CurrentTraceContextCustomizer currentTraceContextCustomizer : currentTraceContextCustomizers) {
currentTraceContextCustomizer.customize(builder);
currentTraceContextCustomizer.customize(currentTraceContextBuilder);
Copy link
Member

Choose a reason for hiding this comment

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

I think that any CurrentTraceContextCustomizer beans should be applied to the braveCurrentTraceContextBuilder bean rather than only applying them when creating the CurrentTraceContext. This will align with other areas where we define a builder bean such as Jackson2ObjectMapperBuilder and RestTemplateBuilder.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this what you intend to do?

@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public CurrentTraceContext.Builder braveCurrentTraceContextBuilder(List<CurrentTraceContextCustomizer> currentTraceContextCustomizers) {
	CurrentTraceContext.Builder builder =ThreadLocalCurrentTraceContext.newBuilder();
	for (CurrentTraceContextCustomizer currentTraceContextCustomizer : currentTraceContextCustomizers) {
		currentTraceContextCustomizer.customize(builder);
	}
	return builder;
}

But if we do, we will have to write code to iterate and apply the CurrentTraceContextCustomizers each time you want to change the type of CurrentTraceContext. I think this is inconvenient.

@Bean
@Scope("prototype")
public CurrentTraceContext.Builder braveCurrentTraceContextBuilder(List<CurrentTraceContextCustomizer> currentTraceContextCustomizers) {
	CurrentTraceContext.Builder builder = StrictCurrentTraceContext.newBuilder();
	for (CurrentTraceContextCustomizer currentTraceContextCustomizer : currentTraceContextCustomizers) {
		currentTraceContextCustomizer.customize(builder);
	}
	return builder;
}

Copy link
Member

Choose a reason for hiding this comment

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

Yes, for consistency with how other builders and applying customisers to them, I think we need to do something like this.

Taking a step back, perhaps we would be better offering a configuration property that allows control of the type of builder that is auto-configured. Its value could be thread local, strict, or standard which would map to ThreadLocalCurrentTraceContext.Builder, StrictCurrentTraceContext.Builder and CurrentTraceContext.Builder respectively.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmmm. How about putting Supplier<CurrentTraceContext.Builder> in the bean then?
This would make it easy to change the type of CurrentTraceContext.

@Bean
@ConditionalOnMissingBean
public Supplier<CurrentTraceContext.Builder> braveCurrentTraceContextBuilderSupplier() {
	return ThreadLocalCurrentTraceContext::newBuilder;
}

@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public CurrentTraceContext.Builder braveCurrentTraceContextBuilder(
		Supplier<CurrentTraceContext.Builder> builderSupplier,
		List<CurrentTraceContext.ScopeDecorator> scopeDecorators,
		List<CurrentTraceContextCustomizer> currentTraceContextCustomizers) {
	CurrentTraceContext.Builder builder = builderSupplier.get();
	scopeDecorators.forEach(builder::addScopeDecorator);
	for (CurrentTraceContextCustomizer currentTraceContextCustomizer : currentTraceContextCustomizers) {
		currentTraceContextCustomizer.customize(builder);
	}
	return builder;
}

@Bean
@ConditionalOnMissingBean
public CurrentTraceContext braveCurrentTraceContext(CurrentTraceContext.Builder currentTraceContextBuilder) {
	return currentTraceContextBuilder.build();
}

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, I don't think that's a good idea. I can't think of anywhere else where we have the indirection of a Supplier providing access to a builder. I don't think there's anything that's unique about this situation to justify it and it's likely to cause confusion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it is an idea that would make it easier to use different CurrentTraceContexts, but yes, Supplier may be confused.

Hmmm, ok, I'll close this PR, thanks.
Workaround by registering my own bean.

@Bean
fun braveCurrentTraceContext(
    scopeDecorators: List<CurrentTraceContext.ScopeDecorator>,
    currentTraceContextCustomizers: List<CurrentTraceContextCustomizer>,
): CurrentTraceContext {
    // See: https://armeria.dev/docs/advanced-zipkin
    val builder = RequestContextCurrentTraceContext.builder()
    scopeDecorators.forEach(Consumer { scopeDecorator: CurrentTraceContext.ScopeDecorator ->
        builder.addScopeDecorator(
            scopeDecorator
        )
    })
    for (currentTraceContextCustomizer in currentTraceContextCustomizers) {
        currentTraceContextCustomizer.customize(builder)
    }
    return builder.build()
}

@Scope("prototype")
@ConditionalOnMissingBean
public CurrentTraceContext braveCurrentTraceContext(List<CurrentTraceContext.ScopeDecorator> scopeDecorators,
public CurrentTraceContext.Builder braveCurrentTraceContextBuilder() {
Copy link
Member

Choose a reason for hiding this comment

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

The tests should be updated to assert that this new bean is defined and that it backs off when a custom CurrentTraceContext.Builder bean is defined.

@pivotal-cla
Copy link

@be-hase Thank you for signing the Contributor License Agreement!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

theme: observability Issues related to observability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants