Description
Is your feature request related to a problem? Please describe.
When using SmartCompositeMessageConverter
in other spring-cloud projects (e.g. Spring Cloud Streams), errors are consumed and hidden by the JacksonMapper
when using the JsonMessageConverter
.
For example, when sending a NestedRuntimeException
through StreamBridge
on Spring Cloud Streams, it calls SmartCompositeMessageConverter
from the SimpleFunctionRegistry.
The converter that is successfully invoked on NestedRruntimeException
is the JsonMessageConverter
. It calls JacksonMapper
toJson on the outgoing payload.
This results in a InvalidDefinitionException: Direct self-reference leading to cycle (through reference chain: my.app.kafka.ErrorRecord["exception"]->org.springframework.web.server.ResponseStatusException["mostSpecificCause"])
That makes sense based on the NestedRuntimeException
potentially including a self reference.
However, the code for JacksonMapper looks like this:
public byte[] toJson(Object value) {
byte[] jsonBytes = super.toJson(value);
if (jsonBytes == null) {
try {
jsonBytes = this.mapper.writeValueAsBytes(value);
} catch (Exception var4) {
}
}
return jsonBytes;
}
Because the exception is empty, it silently fails and continues checking the other converters.
This eventually results in a null
being returned from SimpleFunctionRegistry.class
(I understand this may be a Spring Cloud Streams issue from this point forward, but I think the log would be extremely helpful to determine that the issue is a serialization issue within JacksonMapper, rather than stemming from the calling library)
This causes the following result in Spring Cloud Streams
// ... StreamBridge send method
Message<?> resultMessage;
synchronized(this) {
resultMessage = (Message)functionToInvoke.apply(messageToSend); // this becomes "null"
}
...
// This throws a NullPointerException deep in postProcessResult due to resultMessage being null
resultMessage = (Message)this.functionInvocationHelper.postProcessResult(resultMessage, (Object)null);
return messageChannel.send(resultMessage);
It therefore makes it very hard to tell what the actual issue was within spring-cloud-function.
Either way, the caller will have to simply assume that null
means some issue occurred calling a messageConverter, but in lieu of the exception being available, at least seeing it in the log will allow some information to leak to the developer.
This was discovered when sending a Record<String,Exception>() into Spring Cloud Streams, where only SOME exceptions contain the self reference that causes a failure and others do not. Considering this is somewhat deep in the spring-cloud-function converter workflow, it doesn't feel like a developer issue vs an opportunity to expose some additional information.
Using Spring Cloud Stream as a proxy for any caller, it would be beneficial for a debug/trace log to be available in the JacksonMapper.
Describe the solution you'd like
I'd like a debug log with the given exception in JacksonMapper so that developers are able to determine what caused the error (at least in the JacksonMapper) rather than have to debug through the entire call stack to determine the root problem.
public byte[] toJson(Object value) {
byte[] jsonBytes = super.toJson(value);
if (jsonBytes == null) {
try {
jsonBytes = this.mapper.writeValueAsBytes(value);
} catch (Exception var4) {
log.debug("Failed to convert value using " + converter.name() + " with error " + var4.getMessage());
}
}
return jsonBytes;
}