I am calling 2 different web services using two webservice outbound gateways in my Spring integration gateway flow.
I have implemented 2 fault Resolver , in each gateway to resolve SOAP faults
and want to create custom application exception and throw from resolver to my exception handler.
My FaultResolver is getting called whenever we receieve the SOAP fault from service call.
But I am not been able to throw custom excetion from my resolver as its only allowing me to throw IOException.
Due to this I am throwing a Runtime Exception with message from my resolver and catching this Runtime Exception in my Exception Handler.
Is it correct practice to throw a runtime exception and catching in Handler or is ther any other better way to handle this scenario or any other implementation to handle SOAP Fault and throw custom Exception.
The best way to determine if your solution is good or not, just try to find some out-of-the-box implementation on the matter. One of them is:
public class SimpleFaultMessageResolver implements FaultMessageResolver {
public void resolveFault(WebServiceMessage message) {
if (message instanceof FaultAwareWebServiceMessage) {
throw new WebServiceFaultException((FaultAwareWebServiceMessage) message);
}
else {
throw new WebServiceFaultException("Message has unknown fault: " + message);
}
}
}
where WebServiceFaultException is exactly RuntimeException.
So, I think you are fine to go ahead.
Related
I have a process where I need to upload file to Sftp server asynchronously. So after exploring more about Async in gateways I found that I need to have error channel defined in #MessagingGateway parameter then handler to handle the Exception propagated to error channel but I felt handling this way is complicated for me, as I will have to update Pojo field and persist into DB depending on the file upload, success or failure.
So I thought of having a custom method annotated with #Async and call the gateway method. Also surround gateway method with try block and catch any exception occurred in the downstream
Code Sample:
#Async
void upload(Resource file, FileStatus fileStatus){
try{
uploadGateway.upload(file,fileStatus.getFilePath(),fileStatus.getFileName());
}catch(RuntimeException e){
fileStatus.setUploadStatus("Failed");
//save into db
}
}
Upload Gateway without error channel so that error can be sent back to caller
#MessagingGateway
public interface UploadGateway {
#Gateway(requestChannel = "input.channel")
void upload(#Payload Resource file, #Header("path") String path, #Header("name") String fileName);
}
Handler:
#Bean
public IntegrationFlow uploadDocument() {
return IntegrationFlows.from("input.channel")
.log(LoggingHandler.Level.WARN)
.handle(Sftp.outboundAdapter(sftpSessionFactory(), FileExistsMode.FAIL)
.autoCreateDirectory(true)
.remoteDirectoryExpression("headers['path']")
.fileNameExpression("headers['name']"))
.get();
}
Question:
What will be the consequences if I'm handling error this way? Is this the right way to handle any error occurred in downstream flow?
Since #MessagingGateway is like an RPC in messaging, it is fully OK to catch an exception on its method call like that. Since you make your flow fully sync, it works like typical Java exceptions sub-system.
Your concern about async error handling with errorChannel really makes sense since it is similar in complexity with standard Java async method handling and its errors processing.
On the other hand it is really commended to handle errors downstream via errorChannel if that is going to be some complex logic in some other flow. Plus you are going to return back some compensation message.
However in the end of day the choice is yours: there is no drawbacks to handle errors yourself.
See Error Handling chapter for more food to think on.
I'm migrating a project that uses Spring AMQP to a project that uses Spring Cloud Stream with RabbitMQ.
In my old project, when some exception occurred while processing a message using #RabbitListener, that exception was thrown. If there was a dead letter queue binded, exception was still thrown (only once if there were retries, the last one I guess). This was very helpful for logging purposes.
In Spring Cloud there is a dead letter queue mechanism for #StreamListener if you define the properties:
spring.cloud.stream.bindings.input1.destination=dest1
spring.cloud.stream.rabbit.bindings.input1.consumer.auto-bind-dlq=true
spring.cloud.stream.rabbit.bindings.input1.consumer.republishToDlq=true
But if you have a method like this (is just an example):
#StreamListener("input1")
public void process(String message){
System.out.println("Trying...");
throw new RuntimeException();
}
Logs are:
Trying...
Trying...
Trying...
(end of log, no exception thrown)
Is there any way to throw the exception (only in the last retry)?
Thanks!
See the documentation about consumer properties.
Set ...consumer.max-attempts=1 to disable retry.
You can handle the exception, log it and then throw AmqpRejectAndDontRequeueException. This will send the message to dead letter queue
You are under #StreamListener where would you expect the exception to go? who is catch it?
you can do it something like that:
#StreamListener("input1")
public void process(String message){
try {
System.out.println("Trying...");
throw new RuntimeException();
// or the actual code that handle the message
} catch (RuntimeException re) {
// handle the exception, logging etc.
throw re
}
}
i am encountering a problem i'm not familiar with.
So i'm trying to log exceptions from a test Azure Function but when i throw an exception and return a 400 bad request, application insight registers the log as a successful request.
As i understand it it is probably register the function's successful run but what i don't understand is how i then should log the exception.
So what i have done so far is this.
(I Will be referring to Application Insights as AI from here on out)
I started by created an AI-Resource.
Then i took the instrument key and applied it to the app settings of my function.
After that i installed the AI NUGET to my function bu creating a Projet.json file and then pasting something like this which installed the necessary assemblies and such.
{
"frameworks": {
"net46":{
"dependencies": {
"Microsoft.ApplicationInsights": "2.4.0"
}
}
}
}
after this i initialize the TelemetryClient in the function and try to log and exception in a catch:
Initiation:
string key = TelemetryConfiguration.Active.InstrumentationKey = System.Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY", EnvironmentVariableTarget.Process);
TelemetryClient telemetry = new TelemetryClient() {
InstrumentationKey = key
};
Catch:
catch (Exception e)
{
Dictionary<string,string> properties = new Dictionary<string,string>();
properties.Add("Function Payload", data.ToString());
properties.Add("Function Exception", e.ToString());
telemetry.TrackException(e, properties);
return req.CreateResponse(HttpStatusCode.BadRequest, e);
}
Test running the function i get:
2018-03-07T14:24:36.171 [Info] Function started (Id=0292b455-314d-4c4c-872a-2b8137a72305)
2018-03-07T14:24:37.092 [Info] Function completed (Success, Id=0292b455-314d-4c4c-872a-2b8137a72305, Duration=931ms)
In Application insights i can can only see bad requests for StatusCode: 500
but 400 Bad requests gets logged as Successful requests.
And also the TrackException functionality doesn't log any of the custom properties...
So what am i missing?
MORE DETAILS ABOUT EXCEPTION LOGGING:
#Mikhail is right that we treat this as a success because the function is a success. We didn't want to use status codes to guess whether there was a successful operation or not, so we look for whether the function threw an exception.
Your exception isn't appearing in that screen because it hasn't been property correlated with this function execution. If you go to App Insights Analytics and query for that ExceptionTelemetry, you should see it.
In order to correlate it with a specific function, you'd need to set the OperationId, which is the same as the function's InvocationId. There is a sample that shows how to do this with Events, Metrics, and Dependencies, but doing it for Exceptions is the same (you can ignore the User.Id assignment): https://learn.microsoft.com/en-us/azure/azure-functions/functions-monitoring#custom-telemetry-in-c-functions
Update: From what you've shown of your function above, you may be able to get away with doing something like:
catch (Exception e)
{
log.Error("Function Payload " + data.ToString());
throw;
}
That would return a 500 (rather than a 400), and Functions would log the trace to Application Insights, and then log the exception as well as the Request failure. If you're not using your TelemetryClient anywhere else, you could remove that from your code.
The server (Azure Function) processed the request without errors, you returned a result from it, so from the point of view of Function App runtime the request was processed successfully. You can see that from the log too:
... Function completed (Success, ...
So, it makes sense that the Function App registers the call as success in Application Insights too. At least, that's how they chose to implement it.
the "for this operation" not showing exceptions implies that the exception that you sent does not have the same operationId as the azure function. operation
id is how application insights "links" related telemetry together.
your "exeption logging" screenshot is not an exception, but a request, so the custom properties logged on your exception won't be there.
if you want your azure function to fail, and show as a failed request, and log an exception, why are you catching the exception and logging it yourself? doesn't catching the exception then cause the azure function to succeed? why not just let the exception trickle out and let the function runtime do that part for you? (doesn't it?)
Log4net fails silently by design. Good. I don't want it taking down my app.
Is there a way to set up a notification when Log4net crashes and stops logging?
Is there some kind of event handler that I can hook into which will tell me that Log4net has gone silent?
I want to know when this happens so I can recycle the app pool as soon as possible.
Thanks!
If I'm understanding you correctly, you would like log4net to signal you when an appender fails. If we look at the logging code for the Logger implementation we see that the only point that takes into account the appenders failing is the internal logging mechanism:
// log4net.Repository.Hierarchy.Logger
public virtual void Log(Type callerStackBoundaryDeclaringType, Level level, object message, Exception exception)
{
try
{
if (this.IsEnabledFor(level))
{
this.ForcedLog((callerStackBoundaryDeclaringType != null) ? callerStackBoundaryDeclaringType : Logger.ThisDeclaringType, level, message, exception);
}
}
catch (Exception exception2)
{
LogLog.Error("Log: Exception while logging", exception2);
}
catch
{
LogLog.Error("Log: Exception while logging");
}
}
Of course this occurs only if the appender throws an exception. The LogLog component then forwards the message to the Console and Trace components:
// log4net.Util.LogLog
private static void EmitErrorLine(string message)
{
try
{
Console.Error.WriteLine(message);
Trace.WriteLine(message);
}
catch
{
}
}
So, by listening messages coming from the trace you can get an idea of what happens in your appenders. To turn this into an event you can add a trace listener that triggers in some specific cases: you can take a look at what is in this answer regarding custom trace listeners in order to trigger an event from one.
In a single threaded Spring Integration, application where the entire pipleine is running within the same transaction, an error channel is not applicable. Instead, exceptions will be thrown back to the caller.
The way I am doing this is to define a service activator which is the first of a chain of components which handle an incoming message.
#ServiceActivator
public Message handleException(Message message) {
try {
return message;
}
catch (Throwable throwable){
// HANDLE ERROR
}
return null;
}
Is this the correct approach, or is there a better strategy?
Thanks
No; that won't do anything at all; it will simply pass the message to the next element in the chain. If you don't need to handle the error in any way, and just want it thrown back to the caller, you need to do nothing. If the start of the flow is a gateway, the gateway will unwrap the cause from the MessagingException.
You can, of course, put an error-channel on the gateway and handle it there.
It's generally best to provide some more details about your flow (entry point etc), when asking these general questions.