I am currently using doTry/doCatch blocks in my routes due to which I cannot use a global onException block.
However, I want to perform some business logic if camel route ever breaks (because of bad code or unexpected/untested scenarios). This will hopefully never happen, but I still want to handle for worse case.
I cannot have a java.lang.Exception in global onException block, also, I don't want to put an addition catch on every route.
Is there a specific method Camel calls before throwing uncaught exceptions and breaking route.
I see following log for Uncaught Exceptions:
2015-04-20 15:11:35,279 [Camel (fulfillmentOrderProcessor) thread #5 - seda://FulfillmentSedaQueue] WARN o.a.c.component.aws.sqs.SqsConsumer [, ID-ip-10-180-252-213-54360-1429566855015-0-144]: Exchange failed, so rolling back message status: Exchange[Message: {... }]
java.lang.IllegalArgumentException: Unable to parse string argument null
I looked at UnitOfWork.afterprocess. But this will not help as exchange will have exception even if I have handled it in camel route.
by default, Camel will propagate the exception back to the caller, so you can catch the exception in whatever client code invokes the seda://FulfillmentSedaQueue route...
otherwise, the options on the server side (as you mentioned) are to use a global onException clause or route specific doTry/doCatch statements
Related
I am working on a microservice which does some calculation based on certain configurations stored in its own data store. The calculations apis are stored via REST APIs. The application is a spring boot application.
Now there are mainly 3 layers in the application :
REST Controller
Service layer
DAO layer - it used spring data.
I am planning to handle the logging and exception handling using below points:
Log each request that the service receives and response or at least
the response if the status is not in 2xx series.
If there are any checked exception in either DAO layer or Service
layer then log them and throw a custom exception derived from
RuntimeException.
Have Several custom exception which should be thrown from Service
layer mainly if we come across scenarios like invalid values, null
values etc.
Have a try catch block in the REST Controller and log the
exception i.e. message along with stacktrace and return the
response accordingly.
So overall idea is to let the RuntimeExceptions propagate all the way to REST Controller where they should be logged and accordingly the response should be sent. Incase of checked exceptions log them in the same method and throw custom exceptions instead.
Please suggest what should be the correct or a good approach for logging exception in such applications.
Write controller advice which will catch all the exceptions & logs the required contents. You may catch exceptions here. I implemented same what you asked here.
*/
/**
* Uncaught exception handler
* #param e - Exception
*/
#ExceptionHandler(Exception.class)
#ResponseStatus(code=HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public void handleError(Exception e,HttpServletRequest request, HttpServletResponse response){
logger.error("Exception occured : {}",e);
//logs request & response here
}
Also please check AbstractRequestLoggingFilter described here.
For all custom application specific exeptions create your own custom exception type & handle it with the help of #ExceptionHandler as specified in above code block.
Choose only one place to log the exceptions.
In your design, if an exception occurs in DAO, it will:
Get logged in DAO
Then trigger a runtime exception, which will be caught and logged in controller
Which should then return non-2xx response status via REST, which will trigger logging the response as per your first point
So you'll have either the same information in three places, or you will have the different bits of information regarding a single error scattered in two or three places across the log.
Choose a single place to log the errors, and make sure all relevant info is present at that place (i.e. set the underlying DAO exception as a cause of the runtime exception, and don't forget to log the runtime exception along with its cause).
Have the following camel route.
#Override
public void configure() throws Exception {
onException(java.lang.Exception.class).useOriginalMessage()
.beanRef("discoveryService", "updateConnection")
.redeliveryPolicyRef("redeliverMessagePolicy");
from(ENDPOINT_URI).to(queueName);
}
with Redelivery policy defined as following in xml-
<redeliveryPolicyProfile id="redeliverMessagePolicy"
retryAttemptedLogLevel="WARN" maximumRedeliveries="8"
redeliveryDelay="${redeliveryDelay}" />
However when an exception is thrown the redelivery attempts are made before the OnException block is executed(Some configuration properties get updated in the onException block. Have a debug point in DiscoveryService inside Onexception, it gets called after the redelivery attempts are made). Thus the current message gets lost without being redelivered. Not sure why this happens.
Using activemq-camel version 5.8.0
Thnks
Yes this is intended, the onException block is only executed when the exchange is exhausted (eg after all redelivery attempts have failed).
Read more about how error handling in Camel works in the docs
http://camel.apache.org/error-handling-in-camel.html
And if you have a copy of the Camel in Action book it has an entire chapter devoted to cover all about error handling (most complete documentation there is)
If you want to do some custom logic before each redelivery, then use the onRedelivery processor: http://camel.apache.org/exception-clause.html
How does the exception strategy works in mulesoft when we have a flow having its own exception strategy calling another flow with its exception strategy. What will happen if an exception occurs in the called flow.
The called flow will throw the exception and the exception strategy of the called flow will be executed.
the calling flow wont throw an exception until its expecting something from the called flow and the called flow is not returning that.
Refer to this link Mule_Exception for further detailed information.
If the exception occurs in the called flow,The exception strategy of the called flow will get execute and control pass to calling flow to execute next message processor.
Regards,
Purnachandra
I have a default catch exception strategy for my entire flow/subflows. However, I'd like to be able to tell what component/endpoint threw an exception so I can try to restart the flow at that point (I have yet to figure out how to do that as well.)
Is there any easy way to tell what component/endpoint threw the exception, and be able to tell if it was in a foreach, and at what point (by looking at the "counter" variable.)
Thanks!
You can set a variable at the start of flow like this:
<set-variable variableName="flowName" value="Your_flow_name"/>
And the get the flow name like #[flowName] in your exception strategy.
EDIT:
To trigger a flow, create a java component implementing Callable interface, from the context get the MuleClient and use send or dispatch method to send payload to flow. Send causes MuleClient to wait for response while dispatch doesn't.
More info here: http://www.mulesoft.org/documentation/display/current/Using+the+Mule+Client
Below is an example route take from Camel In Action book. There is one error handler at context scope and two route definitions. My questions
Is it correct to say that the context level error handler is applicable only for Route 1?
Does the dead letter error handler kick in for any exceptions thrown from any of the steps in route 2. i.e. from orderService.validate() and orderService.enrich().
What if I want different error handler for exceptions that arise from validate() and enrich() methods?
//context scope error handler
errorHandler(defaultErrorHandler()
.maximumRedeliveries(2)
.redeliveryDelay(1000)
.retryAttemptedLogLevel(LoggingLevel.WARN));
//Route 1
from("file://target/orders?delay=10000")
.beanRef("orderService", "toCsv")
.to("mock:file")
.to("seda:queue.inbox");
//Route 2 with route scope error handler
from("seda:queue.inbox")
.errorHandler(deadLetterChannel("log:DLC")
.maximumRedeliveries(5).retryAttemptedLogLevel(LoggingLevel.INFO)
.redeliveryDelay(250).backOffMultiplier(2))
.beanRef("orderService", "validate")
.beanRef("orderService", "enrich")
.to("mock:queue.order");
your assumptions on #1 & #2 are correct...
for #3, either define Exception Clauses to catch explicit exceptions thrown by your bean methods (OrderValidateException, EnrichException, etc.) or use inline try-catch blocks around each step in your route (I prefer the first approach myself)