Sleuth annotations with Spring Webflux gives CglibAopProxy warnings - spring-webflux

I have a reactive application using Spring Webflux. I have used sleuth annotations like #NewSpan to create spans, but I am getting warning like
CglibAopProxy|Unable to proxy interface-implementing method
[public final void reactor.core.publisher.Flux.subscribe(org.reactivestreams.Subscriber)]
because it is marked as final: Consider using interface-based JDK proxies instead!
I know Flux.subscribe is a final method so proxies are not generated correctly, but I can still see those spans on Zipkin.
I need to know what are the implications of this warning. And how can I avoid this?

Related

Sleuth traceId and spanId not logged in activeMQ Listener

I'm trying to configure a microservice with Sleuth and ActiveMQ.
When starting a request I can properly see appName, traceId and spanId in logs of producer, but after dequeuing the message in listener I find only appName, without traceId and spanId.
How can I get this fields filled?
Right now I'm working with spring.sleuth.messaging.jms.enabled=false to avoid this exception at startup:
Bean named 'connectionFactory' is expected to be of type 'org.apache.activemq.ActiveMQConnectionFactory' but was actually of type 'org.springframework.cloud.sleuth.instrument.messaging.LazyConnectionFactory'
My dependencies:
org.springframework.boot.spring-boot-starter-activemq 2.5.1
org.springframework.cloud.spring-cloud-sleuth 3.0.3
Thank you all!
My understanding is that the properties you're looking for are set on the JMS message when the message is sent and then retrieved from the message when it is consumed. Since you're setting spring.sleuth.messaging.jms.enabled=false you're disabling this functionality. See the documentation which states:
We instrument the JmsTemplate so that tracing headers get injected into the message. We also support #JmsListener annotated methods on the consumer side.
To block this feature, set spring.sleuth.messaging.jms.enabled to false.
You'll need to find an alternate solution for the connection factory problem if you want to use Sleuth with Spring JMS. If you're injecting org.apache.activemq.ActiveMQConnectionFactory somewhere then you should almost certainly be using javax.jms.ConnectionFactory instead. Using the concrete type is bad for portability and use-cases like this where wrapper implementations are used dynamically.

Using Byte Buddy to expand Spring Boot's classpath

The AWS SDK can locate API call interceptors via classpath scanning (looking for software/amazon/awssdk/global/handlers/execution.interceptors and instantiating classes specified there).
I'm writing a Java Agent with the intention of causing my interceptors to be locatable by the AWS SDK.
My interceptor is bundled with the Java Agent.
My interceptor implements AWS's ExecutionInterceptor.
The AWS SDK is not bundled with my agent, because I'd like the end-user to provide their own AWS SDK version.
For regular standalone applications, this is a no-brainer, as the Java Agent is automatically added to the runtime classpath of the application. The AWS SDK finds my interceptors with no problem.
However, this approach completely breaks with Spring Boot applications where the AWS SDK is bundled as a dependency under BOOT-INF/lib. The reason boils down to Spring Boot's classloading hierarchy. My interceptor class can be found, but its loading fails due to inability to find AWS's ExecutionInterceptor, as it is loaded in a "lower" classloader in the hierarchy.
So I figured that my approach should be to somehow modify Spring Boot's classloader search. However, I'm facing these issues:
At the time of the agent being called, Spring Boot's "lower" classloader isn't created yet.
I am not entirely sure what it is that I need to instrument.
I've read of Byte Buddy being able to help in such "interesting" circumstances but haven't found a way to make this work yet. Any ideas?
(EDIT: I'm looking for a solution that doesn't require code/packaging changes, hence the Java Agent approach)
(EDIT: Things I've tried)
Following Rafael's answer: The method in the SDK that resolves all interceptors is in the class SdkDefaultClientBuilder, and is called resolveExecutionInterceptors.
The following, then, works for standalone JARs which are not SpringBoot applications:
public static void installAgent(Instrumentation inst) {
new AgentBuilder.Default()
.with(RedefinitionStrategy.DISABLED)
.type(ElementMatchers.nameEndsWith("SdkDefaultClientBuilder"))
.transform(
new Transformer() {
#Override
public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule module) {
return builder.visit(Advice.to(MyAdvice.class).on(ElementMatchers.named("resolveExecutionInterceptors")));
}
}
).installOn(inst);
}
For SpringBoot applications, however, it looks like the advice isn't applied at all. I am guessing that this is because the SdkDefaultClientBuilder type isn't even available at the time when the agent starts. It is available during SpringBoot's runtime, in a different classloader.
Byte Buddy allows you to inject code in any method of any class, so the first and only major thing you would need to find out would be where your interceptor is instantiated. This can typically be done by setting a breakpoint in the constructor of the interceptor in the working scenario and investigating the methods in the stack. Find out where the classes are discovered, for example the method where software/amazon/awssdk/global/handlers/execution.interceptors is read.
Once you have identified this method, you would need to find a way to manually extract the interceptors defined by your agent and to manually add them. For example, if the file-extracted interceptors are added to an argument of type List<Interceptor>, you could use Byte Buddy to modify this method to also add those of your agent.
Normally, you use Byte Buddy's AgentBuilder in conjunction with Advice to do so. Advice let's you inline code into another method as for example, assuming you find a method with an argument of type List<Interceptor>:
class MyAdvice {
#Advice.OnMethodEnter
static void enter(#Advice.Argument(0) List<Interceptor> interceptors) {
interceptors.addAll(MyAgent.loadMyInterceptors());
}
}
You can now inline this code into the method in question by:
class MyAgent {
public static void premain(String arg, Instrumentation inst) {
new AgentBuilder.Default().type(...).transform((builder, ...) -> builder
.visit(Advice.to(MyAdvice.class).on(...))).install(inst);
}
}
You might need to use AgentBuilder.Transformer.ForAdvice if the classes in question are not available on the agent's class loader where Byte Buddy resolves the advice using both the target and the agent class loader.

WebClient instrumentation in spring sleuth

I'm wondering whether sleuth has reactive WebClient instrumentation supported.
I did't find it from the document:
Instruments common ingress and egress points from Spring applications (servlet filter, async endpoints, rest template, scheduled actions, message channels, Zuul filters, and Feign client).
My case:
I may use WebClient in either a WebFilter or my rest resource to produce Mono.
And I want:
A sub span auto created as child of root span
trace info propagated via headers
If the instrumentation is not supported at the moment, Am I supposed to manually get the span from context and do it by myself like this:
OpenTracing instrumentation on reactive WebClient
Thanks
Leon
Even though this is an old question this would help others...
WebClient instrumentation will only work if new instance is created via Spring as a Bean. Check Spring Cloud Sleuth reference guide.
You have to register WebClient as a bean so that the tracing instrumentation gets applied. If you create a WebClient instance with a new keyword, the instrumentation does NOT work.
If you go to Sleuth's documentation for the Finchley release train, and you do find and you search for WebClient you'll find it - https://cloud.spring.io/spring-cloud-static/Finchley.RC2/single/spring-cloud.html#__literal_webclient_literal . In other words we do support it out of the box.
UPDATE:
New link - https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/html/integrations.html#sleuth-http-client-webclient-integration
let me paste the contents
3.2.2. WebClient
This feature is available for all tracer implementations.
We inject a ExchangeFilterFunction implementation that creates a span
and, through on-success and on-error callbacks, takes care of closing
client-side spans.
To block this feature, set spring.sleuth.web.client.enabled to false.
You have to register WebClient as a bean so that the tracing
instrumentation gets applied. If you create a WebClient instance with
a new keyword, the instrumentation does NOT work.

How to write single objectMapper for spring-data-rest and to my #RestController class

I wrote a UnwrappingBeanSerializer for my entity. Currently this serializer was registered using ConfigureJacksonObjectMapper
This serializer is working fine for REST APIs generated from spring-data-rest. But I have a custom #RestController for the same entity, But it doesn't know about the serializer registered in spring-data-rest configuration.
I want to serialize my response with UnwrappingBeanSerializer both in spring-data-rest APIs and also to my custom controllers.
How to achieve this?
I also tried with #JsonSerialize on my entity class. But I am unable to create bean for unWrappingBeanSerializer with BeanSerializerBase
Regular #RestController and Spring Data REST controllers have different flows and configuration. If you are using Spring Data REST, you'd better use #RepositoryRestController for custom endpoints of the same resource, this will use the same Spring Data REST chain and its configuration, like the one you used in ConfigureJacksonObjectMapper, otherwise your ObjectMapper is visible only for Spring Data REST.
If you want to have #RestController and use the same ObjectMapper for both - you need to have two configurations: one for Spring Data REST (like you already have) and another for regular controllers, so just register it in Spring context (for instance, if you are using Spring MVC, see Customize the Jackson ObjectMapper).

spring.net and WCF Rest Service

I am building a WCF Rest Service using VS2010 .net 4.0 and the Rest service template. I would like to introduce spring.net - IoC but I am not able to get spring initialized when the InstanceContextMode.Single is set. For all other settings I can use IInstanceProvider interface and introduce a custom behaviour.
My question is:
Is there any other way I can get spring initialized?
Sorry I just saw the comments in the main response
If your only problem is the name of the reference, you could create your own and fix the reference parameter, or add a dictionary where you set some aliases in the xml config and do a lookup against that.
As far as I know you can't use Spring when the InstanceContextMode is single; from the doc:
While integrating 'natively' with WCF does seem to be the most natural
approach there is one 'gotya' that needs to be investigated further to
see if there is an acceptable workaround in order for this approach to
be viable. The issue is that if the service is configured to be a
singleton, for example using
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] then
the invocation of the IInstanceProvider is short-circuited.
The documentation suggests this:
One workaround, which is not very appealing, is to use the PerCall instancing mode but set the
singleton attribute in the Spring configuration to true, this way the
same instance is always returned.
More info here: http://www.springframework.net/docs/1.2.0-M1/reference/html/wcf.html