How can i handle own error codes with NLOG logging - database

Hi i try to Log error codes like in Log4Net
$logger.Error(3, "Converting task invocator's config file failed, exiting...")
If this error occurs it logs only the Error code "3" but not the message. it Looks like it takes the Error code as text und drops the message it self.
EventLog --> [TaskV1] (Automation_TaskInvocator.ps1 - 1.0.0.0) [] [ERROR] 3 --> message is missing
Same problem with Database and file.
the question is how i can handle that. it should takes the error Code as INT and the Message as text.
I don't want to go over my hundreds of lines of code to add the error code to the message.
especially since I have an extra column for the error codes in the database.
Hope someone can bring me on the right way
thanks a lot
With best regards

Example of custom extension-method for NLog-ILogger (Not tested with a compiler, so might need some tweaks):
public static class NLogLoggerExtensions
{
public static void Error(this NLog.ILogger logger, int eventId, string message)
{
Log(logger, NLog.LogLevel.Error, eventId, null, message);
}
public static void Error(this NLog.ILogger logger, int eventId, Exception exception, string message)
{
Log(logger, NLog.LogLevel.Error, eventId, exception, message);
}
private static void Log(NLog.ILogger logger, NLog.LogLevel loglevel, int eventId, Exception exception, string message)
{
var logEvent = NLog.LogEventInfo.Create(loglevel, logger.Name, exception, message);
if (eventId != 0)
logEvent.Properties["EventId"] = eventId;
logger.Log(logEvent);
}
}
You could also inject the "EventId" as a property like this:
logger.WithProperty("EventId", 3).Error("Converting task invocator's config file failed, exiting...")
Or using Fluent API like this:
logger.ForErrorEvent().Property("EventId, 3").Message("Converting task invocator's config file failed, exiting...").Log();
See also: https://github.com/NLog/NLog/wiki/EventProperties-Layout-Renderer

Related

Transactional Camel route marking exchange as handled based on expression not working

My camel route is consuming and producing from/to JMS in a transactional way. Our requirement is to discard a poison message if failing to process a number of times. I know that a much better option would be to move the message to a dead letter queue but for the purpose of this exercise discarding it is just good.
Below is the route definition to simulate the issue:
package com.my.comp.playground;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
#Component
public class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() {
onException(Exception.class)
.process(new Processor() {
private int failureCounter = 0;
#Override
public void process(Exchange exchange) {
exchange.getIn().setHeader("failureCounter", ++failureCounter);
}
})
.log("failureCounter = ${header.failureCounter}")
//.handled(true);
.handled(header("failureCounter").isGreaterThan(3));
from("jms:test.queue")
.routeId("test-route")
.transacted()
.process(exchange -> {
throw new RuntimeException("No good Pal!");
})
.to("mock:discard");
}
}
So what I am trying to do is to keep a counter of the failures and if that counter is greater than a certain number mark the exception as handled and commit the transaction.
Note the two lines of code at the end of the exception handling:
//.handled(true);
.handled(header("failureCounter").isGreaterThan(3));
When I run my route with the header("failureCounter").isGreaterThan(3) handled condition the message rollbacks again and again forever, and I can see in the logs the failureCounter correctly being increased:
...
[mer[test.queue]] test-route : failureCounter = 402
[mer[test.queue]] o.a.c.p.e.DefaultErrorHandler : Failed delivery for (MessageId: ...
...
[mer[test.queue]] test-route : failureCounter = 403
...
[mer[test.queue]] test-route : failureCounter = 404
...
However when I run the route with the true handled condition the transaction gets committed straight away after the first failure as shown below:
[mer[test.queue]] test-route : failureCounter = 1
[mer[test.queue]] o.a.c.s.spi.TransactionErrorHandler : Transaction commit (0x52b2f795) redelivered(true)
So my question is: Am I doing something wrong or is my understanding about how to use handled exception incorrect? If so what would be the correct way?
I have no idea if this is by design or a bug.
When I debug your case I see that the predicate in handled() is evaluated against the Camel Exchange.
However, your failureCounter header is not present in the Exchange. Therefore the expression header("failureCounter") evaluates to null and your predicate is always false.
In a short test I saw that headers set before the exception are present, but headers set after the exception (i.e. set inside the error handler), are not present on the Exchange that is used to evaluate the predicate.
Burki's comments were good observations which helped me identify the root cause.
Despite the fact that camel DSL suggests the .handled(header("failureCounter").isGreaterThan(3)) check would happen after running the failure counter increase processor and the log output in reality the .handled is the first thing to be evaluated in that onException(...) branch. It seems a bit misleading and I must admit I feel a bit disappointed with this approach. It must be a reason though.
Given camel will create a new Exchange for every message delivery no surprise this will rollback forever.
To address this there are two solutions that came immediately in my mind.
Solution 1:
It is based on the fact that the message broker will set the JMSRedelivered message header so it is OK to increase message counter based on that. So the new route config will look like below:
#Override
public void configure() {
onException(Exception.class)
.handled(header("failureCounter").isGreaterThan(3));
from("jms:test.queue")
.routeId("test-route")
.transacted()
.process(new Processor() {
private int failureCounter = 0;
#Override
public void process(Exchange exchange) throws Exception {
if (exchange.getIn().getHeader("JMSRedelivered", Boolean.class)) {
exchange.getIn().setHeader("failureCounter", ++failureCounter);
}
}
})
.log("failureCounter = ${header.failureCounter}")
.process(exchange -> {
throw new RuntimeException("No good Pal!");
})
.to("mock:discard");
}
After applying this change the message will be discarded after more than three consecutive failures to deliver the message as shown in the logs below:
46849 --- [mer[test.queue]] test-route : failureCounter = 4
46849 --- [mer[test.queue]] o.a.c.s.spi.TransactionErrorHandler : Transaction commit (0x31b273b2) redelivered(true) for (MessageId: ID:414d5120514d31202020202020202020c083da5f02bd1d22 on ExchangeId: ID-C02XN27DJG5H-1608604365426-0-4))
Solution 2: This is even easier but it is message broker specific in this case IBM MQ.
#Override
public void configure() {
onException(Exception.class)
.log("failureCounter = ${header.JMSXDeliveryCount}")
.handled(header("JMSXDeliveryCount").isGreaterThan(3));
from("jms:test.queue")
.routeId("test-route")
.transacted()
.process(exchange -> {
throw new RuntimeException("No good Pal!");
})
.to("mock:discard");
}
And the logs will show again that the message gets delivered four times before giving up.

How to Implement Retry analyzer in try catch Block?

Below is my code after got the exception immediately it should trigger one more time for execution but didn't execute.Without try,catch it was executing but after keep the code in try and catch RetryAnalyzer is not working as expected please anyone help me how to implement RetryAnalyzer in try,catch block
#Test(priority=4,enabled=true,retryAnalyzer=RetryAnalyzer.class)
public void TC_04() throws InterruptedException
{
try
{
extent=new
ExtentReports("/Users/anakin/workspace/Reports/MKE.html");
Login_Objects.Switch_PB2U(driver).click();
String screenshot_path = Screenshot.createScreenshot(driver);
String image = log.addScreenCapture(screenshot_path);
log.log(LogStatus.PASS, "To Stocks",image);
extent.endTest(log);
driver.closeApp();
}
catch(Exception e)
{
String screenshot_path = Screenshot.createScreenshot(driver);
String image = log.addScreenCapture(screenshot_path);
log.log(LogStatus.FAIL, "Verify Suggest Stocks ", image);
extent.endTest(log);
}
}
Catching exception in your complete test code is a bad practice. Even as in your case you are capturing screenshot in case of failure, you should use listener than catching exception.
Since retry analyzer works for failed tests, and you are cathing exceptions and thus supressing the errors, it might give the impression that your test never failed and thus the retryAnalyzer is not working.
Solution : Move code in Exception to Listeners and remove unnecessary Exception handling.

codenameone: how to handle exception java.net.ConnectionException explicitly

codenameone: how to handle exception java.net.ConnectionException explicitly
I want to handle exception explicitly.Currently when I am handling exception It handled implicitly first in which shows the exception message on screen in detail.I don't want to show in detail error message on screen(pop up dialog).
right now it shows the exception Java.net.Connection Exception: Connection refused for URL http:localhost/login connection refused.instead of this message i just want to show "connection refused" message on pop-up dialog
Can you please let me know how to resolve it.
On mobile devices the error might be quite different to the one on the simulator since we are dealing with native API's under the surface. See the error handling section of the networking section in the developer guide:
There are two distinct placed where you can handle a networking error:
The ConnectionRequest - by overriding callback methods
The NetworkManager error handler
Notice that the NetworkManager error handler takes precedence thus allowing you to define a global policy for network error handling by consuming errors.
E.g. if I would like to block all network errors from showing anything to the user I could do something like this:
NetworkManager.getInstance().addToQueue(request);
NetworkManager.getInstance().addErrorListener((e) -> e.consume());
The error listener is invoked first with the NetworkEvent matching the error. Consuming the event prevents it from propagating further down the chain into the ConnectionRequest callbacks.
We can also override the error callbacks of the various types in the request e.g. in the case of a server error code we can do:
ConnectionRequest request = new ConnectionRequest(url, false) {
protected void handleErrorResponseCode(int code, String message) {
if(code == 444) {
// do something
}
}
protected void handleException(Exception err) {
// handle exception that occurred. Notice you can either have this or have the listener on the NetworkManager
}
protected void readResponse(InputStream input) {
// just read from the response input stream
}
};
NetworkManager.getInstance().addToQueue(request);

stop apache.camel route (RoutePolicy)

I have a Route like the following.
SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy();
policy.setRouteStartDate(new Date());
policy.setRouteStartRepeatInterval(1000);
from("file:data/in")
.routePolicy(policy)
.to("direct:validateInput")
.to("direct:changeInput")
.to("file:data/out");
So the Route takes an file from the inputfolder every second. After some validation and changing it writes it out to an out folder.
Now I would like to be able to close the actual route at every point. So If some error happens at the direct route validateInput the following two parts should not be excecuted.
I could do this with some doTry() and doCatch() but this will look ugly and hard to read.
Question: is it somehow possible to stop on loop of the main route without stopping the route complete? Like this the actual file won't be printed to the outfolder but the file comming in 5 seconds can be processed in normal way.
Creating a new Process and stopping the main Route in a seperate Thread doesn't work.
The file is still written into the date/out folder
The route is stopped complet and won't take any files anymore.
just have your validateInput step throw an Exception and use an onException() clause to handle the exception, that will short-circuit the flow for just that message and allow for processing future files dropped into the 'data/in' directory normally
I believe you best option is using doCatch and doFinally. I think anything different would be much more difficult to read and very ugly.
http://camel.apache.org/validation.html
This is the most logical approach to solving this problem.
It works good, but 1 Problem is still left. My actual Example looks like the following:
SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy();
policy.setRouteStartDate(new Date());
policy.setRouteStartRepeatInterval(1000);
//start main route
from("file:test-data/in?delete=true")
.routePolicy(policy)
.onException(Exception.class)
.log(LoggingLevel.ERROR, "${exception}")
.handled(true).useOriginalMessage()
.to("file://test-data/error?autoCreate=true")
.end()
.log(LoggingLevel.DEBUG, "Processing file ${file:name}")
.to("direct:validateInput")
.to("direct:validateContent")
.to("direct:validateOutput")
.to("file:test-data/out");
from("direct:validateInput")
.doTry()
.to("validator:message.xsd")
.doCatch(ValidationException.class)
.process(getErrorProcessor("Could not validate import against the xsd. Message: ${exception}; File: ${body}"));
//...
}
}
private Processor getErrorProcessor(final String message) {
return new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
throw new Exception(message);
}
};
}
If I put an incorrect file into the in folder I get the following error message:
ERROR route1:145 - java.lang.Exception: Could not validate import against the xsd. Message: ${exception}; File: ${body}
As you can see, camel replaced the ${...} attribute one time. But after this I won't replace the new ${...} elements. Any Idear how I can tell camel replace any new {...} parts or how I can replace them before by my self?
found an answer which is good. For all folks who find this question here an "complete" solution.
public void start() {
try {
CamelContext camelContext = new DefaultCamelContext();
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
errorHandler(loggingErrorHandler().level(LoggingLevel.ERROR));
SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy();
policy.setRouteStartDate(new Date());
policy.setRouteStartRepeatInterval(1000);
//Exception Handling in case the xsd validation fails
onException(SomeSpecialException.class)
.log(LoggingLevel.ERROR, "${exception}")
.handled(true).useOriginalMessage()
.to("file:test-data/error?autoCreate=true")
.end();
//start main route
from("file:test-data/in?delete=true")
.routePolicy(policy)
.log(LoggingLevel.DEBUG, "Processing file ${file:name}")
.to("direct:validateInput")
.to("direct:validateContent")
.to("direct:validateOutput")
.to("file:test-data/out");
//start of separate Routes
from("direct:validateInput")
.doTry()
.to("validator:message.xsd")
.doCatch(ValidationException.class)
.process(getErrorProcess());
//...
}
});
camelContext.start();
} catch (Exception e) {
LOGGER.error("Error while processing camel route: " + e.getMessage());
}
}
/**
* throws an SomeSpecialException in case of calling
*
* #return a Processor which is supposed to be called in case an {#link org.apache.camel.ValidationException} happens
*/
private Processor getErrorProcess() {
return new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
StringBuilder message = new StringBuilder();
String fileContent = exchange.getIn().getBody(String.class);
String originalErrorMessage;
try {
SchemaValidationException schemaValidationException = (SchemaValidationException) exchange.getProperties().get("CamelExceptionCaught");
originalErrorMessage = schemaValidationException.getMessage();
} catch (Exception e) {
originalErrorMessage = "Could not retrieve original error message.";
}
message.append("Could not validate import against the xsd. ")
.append("Original message: ").append(originalErrorMessage).append("; ")
.append("File:").append(fileContent);
throw new SomeSpecialException(message.toString());
}
};
}
Thanks for everyone that helped me.

Logging exceptions to database in NServiceBus

If an exception occurs in my MessageHandler I want to write the exception details to my database.
How do I do this?
Obviously, I cant just catch the exception, write to database, and rethrow it since NSB rollbacks all changes. (IsTransactional is set to true)
I tried adding logging functionality in a seperate handler, which I caledl using SendLocal if an exception occured, but this does not work:
public void Handle(MessageItem message)
{
try
{
DoWork();
}
catch(Exception exc)
{
Bus.SendLocal(new ExceptionMessage(exc.Message));
throw;
}
}
I also tried using Log4Net with a custom appender, but this also rolled back.
Configure.With()
.Log4Net<DatabaseAppender>(a => a.Log = "Log")
appender:
public class DatabaseAppender : log4net.Appender.AppenderSkeleton
{
public string Log { get; set; }
protected override void Append(log4net.Core.LoggingEvent loggingEvent)
{
if (loggingEvent.ExceptionObject != null)
WriteToDatabase(loggingEvent.ExceptionObject);
}
}
Is there anyway to log unhandled exceptions in the messagehandler when IsTransactional is true?
Thanks in advance.
I recommend to configure appenders with a configuration file and not in code. This way whoever operates your software can decide how stuff should be logged. The standard AdoNetAppender has the following configuration switch:
<param name="UseTransactions" value="False" />
I think this would work the way you want. If you really want to use your own appender you can either check in log4net source code how they do it. They probably use the TransactionScope class with the TransactionScopeOption.Suppress option.
NServiceBus already logs handler and saga exceptions OOTB. Those log entries can be configured to log to any of the common logging libraries
log4net
common-logging
nlog
You can also subscribe to error notifications and handle them programmatic

Resources