log all handled exception using log4net + AutoFac interceptor - c#

I'm using Log4Netto log my application. currently I want to log every methods enter in my app (for testing purpose). Therefor I'm using AutoFac interception capabilities, somethong like this:
builder.Register(c=> new MyClass()).As<IMyInterface>().EnableInterfaceInterceptors().InterceptedBy(typeof(LoggerClass));
builder.Build();
and my LoggerClass looks something like this:
public class LoggerClass : StandartInterceptor
{
ILog _log = LogManager.GetLogger(typeof(LoggerClass));
override void PreProceed(IInvocation invovation)
{
_log.Info(string.Format("entering method: {0}",invocation.Method.Name);
}
}
for now this implementation will print message for all method invocation (the interceptor catches all methods entries).
Question
I would like to use this interception mechanism to log every handled Exception.
For example instead of coding this:
catch (myException ex)
{
_log.Error(string.Format("catches exception {0}", ex.Message));
}
I'll have extra method in my LoggerClass that will wrap the catch statement and inject to it log message.
Is there any way to do it using Log4Net? since basically the interceptor work around the method, and I need it to work inside method.

On your caught exceptions, you will never enter into the catch block of your interceptor.
Therefore, in your method when you catch an exception, you can handle the caught exception in an additional intercepted method that will get logged appropriately. This will inflate your codebase a bit, but you will end up getting the information you want without sacrificing your architecture.
public void InterceptedMethod()
{
try
{
//Some code that fails.
}
catch
{
HandleException();
}
}
//Intercept this method also
public void HandleException()
{
}

Related

Good practice .Net Core API validation

So lets say we have a AccountController
And has a method like this for registering:
[HttpPost("register")]
public ActionResult Register(RegisterDto user)
{
try
{
accountService.Register(user);
return Ok();
}
catch(Exception ex)
{
return BadRequest(ex.Message);
}
}
For registering, your EmailAddress must not be used and Password must meed a criteria
The validation exists inside accountService and does something like this:
public void Register(RegisterDto user)
{
accountValidator.ValidateRegistration(user);
accountHandler.Register(user);
}
Notice the Register method is void, so it does not return anything, and the method ValidateRegistration looks like this
public void ValidateRegistration(RegisterDto user)
{
if (accountRepository.UserExists(user.Email))
{
throw new ExistingAccountAssociatedWithThisEmailAddressException();
}
if(!Regex.IsMatch(user.Password, passwordRegex))
{
throw new PasswordDoesNotMeetCriteriaException();
}
}
I am throwing exception with specific name so I can pass this back to the view, but I am wondering what is the best way to do something like this ? Creating specific Exceptions and throwing them ? I dont want to pass a string back
Using specific exceptions is a good practice, in my opinion, as they explain better a developer's intentions.
I can recommend two ways that may help refine your code:
Let all input-related exceptions inherit from a base exception named for example "BadInputException" (or "DomainException", from the perspective of clean architecture). This way you can catch the base exception then return BadRequest back to client.
Use asp.net core's middleware to handle such exception (here's an example), instead of try/catch block in every action. This way you would make your code cleaner.

How to log exceptions with structured arguments in .NET Core

I can't find a consistent way to log exceptions in my .NET Core microservice.
Informational messages logging guidelines are simple (Microsoft.Extension.Logging is used):
_logger.LogInformation($"Reading file {path}..."); // bad
_logger.LogInformation("Reading file {Path}...", path); // good
The benefit of the second variant is structured information: by using a clever log event router (like Serilog with RenderedCompactJsonFormatter) the path is written as separate property of the log event.
Things are going worse with errors logging. The requirements are obvious:
Errors are implemented as exceptions.
An error is logged in the catch block where it is handled.
Each error is logged in a structured form.
So, I'd expect the error reporting to look like
throw new MyException("Failed to read file {Path}", path);
and error logging - like
catch(MyException e)
{
_logger.LogError(e, "Processing error");
}
LogError method here logs the complete error description, but it is not structured: the path is not added as a property.
I tried to make MyException hold the message template and the arguments, but there are 2 problems with this approach:
How to render the exception message based on a template with named arguments?
An argument may be disposed when an exception is processed in a catch block.
Please tell me how you deal with this.
Exceptions in .NET don't support structured parameters like that. Since you are using custom exceptions, you could add this functionality to your own exception class.
For example:
public class MyException : Exception
{
public object[] Props { get; }
public MyException()
{
}
public MyException(string message)
: base(message)
{
}
// Add custom "props" parameter to your exception class
public MyException(string message, params object[] props)
: base(message)
{
Props = props;
}
public MyException(string message, Exception inner)
: base(message, inner)
{
}
}
Now in your code you could do something like this:
try
{
var file = "image.png";
var path = "./my/path/";
throw new MyException("Failed to read file '{file}' in path '{path}'", file, path);
}
catch (MyException e)
{
_logger.LogError(e, e.Message, e.Props);
}
If you look at your logs (I'm using InvisionLog here), then you should see that it is structured.
I think this is the only way to catch your exceptions and log them in a structured manner.

Log all handles exception

How can I log all handled exceptions?
I want that whenever I catch an exception I should be able to log it
I want it to work globally and not that i should have to write it each time I catch
I tried subscribing to AppDomain.CurrentDomain.FirstChanceException and it did work but I did not have the full stack trace and it called multiple times for each exception (I don't know why)
I also tried wrapping my controller with ActionFilterAttribute like below and it worked on all exception from the controller only and not if the exception was caught in a service that was called from the controller
public class ExceptionLoggingHandler : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if(filterContext.Exception !=null)
{
System.Diagnostics.Debug.WriteLine(filterContext.Exception.Message);
}
base.OnResultExecuted(filterContext);
}
}
In ASP.NET MVC, you can add your filter as a global filter in the RegisterGlobalFilters method inside FilterConfig.cs. It should then catch all exceptions in all controller actions, and in any methods called from those actions - unless of course those methods already have catch blocks inside them which swallow the exception. In that case the caught exception (unless it's then re-thrown) will inevitably go undetected higher up the stack, which is, naturally, the whole point of catching exceptions.
e.g.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
filters.Add(new ExceptionLoggingHandler());
}
}
Also, your attribute should inherit from HandleErrorAttribute, not ActionFilterAttribute.
Something like this:
public class ExceptionLoggingHandler : HandleErrorAttribute
{
public ExceptionLoggingHandler() : base()
{
}
public override void OnException(ExceptionContext context)
{
System.Diagnostics.Debug.WriteLine(context.Exception.Message);
context.ExceptionHandled = true;
//.... continue to produce a suitable response
}
}
(In the .... area you can continue to develop the handler to log more sophisticated data, and return a suitable response, perhaps along the lines of this one (other examples are also available online.)

AOP with Autofac and DynamicProxy2 Exception Handling

I'm trying to centrally manage Exception handling for a certain method but I can't seem to get there.
public class ExceptionInterceptor : IInterceptor
{
private readonly Logger _logger;
public ExceptionInterceptor(Logger logger)
{
_logger = logger;
Measure.Configure(new StatsdConfig());
}
public void Intercept(IInvocation invocation)
{
try
{
invocation.Proceed();
//if ((Task<System.Threading.Tasks.VoidTaskReturn>) invocation.ReturnValue.Status == "Failed")
//{
// throw new Exception(invocation.ReturnValue.Exception[0]);
//}
}
catch (Exception e)
{
var errorMessage =
String.Format(
"An error occurred while retrieving fund data. Error Message: {0} Inner Exception: {1}",
e.Message, e.InnerException != null ? e.InnerException.Message : "<None>");
_logger.Log(errorMessage);
Measure.Counter("Exception", 1);
Measure.Event("Exception", errorMessage);
throw;
}
}
I'm wiring this up in a module like so:
builder.RegisterType<DataConsumer>().
As<IConsumer<DataRequest>>().
EnableInterfaceInterceptors().
InterceptedBy(typeof(ExceptionInterceptor));
builder.RegisterType<ExceptionInterceptor>().AsSelf();
var loggingInterceptor = new LoggingInterceptor(Logger);
builder.Register(c => loggingInterceptor);
However when I throw an exception in the method invocation this doesn't bubble up to the interceptor as an exception thrown, so it never enters the catch block.
Is there any way to catch the intercepted method's exception in the interceptor?
I also can't access the invocation.ReturnValue.Status for some reason and as such can't test whether there was a thrown exception in order to re-throw.
Can anyone shed some light into what I might nor be doing right here?
Ta
I'm having a difficult time reproducing your issue due to a bit of incomplete information. For example, you noted that the IConsumer<T> interface is a MassTransit interface, but the interface in MassTransit isn't generic. It also specifically mentions that the interface is supposed to be a marker just for IoC containers, which may have some implications on your wire-up.
First, let's post a working exception handling example. To be self-contained, I'll create an IWorker<T> interface in place of IConsumer<T> and a simple implementation:
public interface IWorker<T>
{
bool DoWork(T message);
}
public class StringWorker : IWorker<string>
{
public bool DoWork(string message)
{
throw new DivideByZeroException();
}
}
Now I'll create a simple exception logger that just pipes info to the console.
public class ExceptionLogger : IInterceptor
{
private readonly TextWriter _output;
public ExceptionLogger(TextWriter output)
{
_output = output;
}
public void Intercept(IInvocation invocation)
{
try
{
invocation.Proceed();
}
catch(Exception ex)
{
_output.WriteLine("Logged Exception: {0}", ex.Message);
throw;
}
}
}
I can then wire it up and see it in action like this:
var builder = new ContainerBuilder();
builder.RegisterInstance(Console.Out).As<TextWriter>();
builder.RegisterType<ExceptionLogger>();
builder.RegisterType<StringWorker>()
.As<IWorker<string>>()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(ExceptionLogger));
var container = builder.Build();
var worker = container.Resolve<IWorker<string>>();
worker.DoWork("Test!");
When I run this, I see on the console just before the program dies (with the unhandled exception - note my handler didn't swallow it, just logged it):
Logged Exception: Attempted to divide by zero.
So it's working.
I think there's more in your environment that may be causing trouble here. It could be something you think is unrelated but is actually important.
General things to investigate:
Temporarily update your DataConsumer to throw an exception immediately inside one of the interface methods. After building your container, resolve a, IConsumer<DataRequest> and call that interface method. Does it get logged?
Look at the places that you're expecting to see logging occur. Are you resolving and working with IConsumer<DataRequest> or something else? It's wrapping the interface methods, not the object type, so not all methods are covered.
Set a breakpoint in the interceptor and see if any invocation at all is passing through it. It won't catch exceptions if it's not being hit. :)
Check to see if there's any other exception handling policies or code in action. For example, some folks use the Enterprise Library exception handling block to deal with exceptions and that may be interfering with your work here.
I've not used MassTransit, but check to see if there's any other object proxying going on. (Doubtful, but I know I've run into this with products like Glimpse, so you end up with proxies wrapped around proxies, which becomes challenging.)
Is the exception actually happening in the place you think it is? It may be happening and getting handled somewhere that isn't wrapped with the proxy.
Basically, reduce the working pieces to the smallest set possible until you can see it working, then slowly expand until you find the place it breaks down. I don't know if any of these apply to your situation, but these are the things I'd start looking at if I was troubleshooting.
But... exception handling in an AOP fashion using interceptors does work, so it's something else going on that's causing the challenge.
It seems that it isn't possible for the exception thrown in the target proxy to be pushed up to the interceptor, and as such what I was trying to do didn't work. I ended up handling the exceptions at the class they occur.
Disappointed I didn't manage to get this to work the way I was intending to.

Unit testing WCF Faults

What's the best way to unit test expected faults from WCF services?
I am attempting to unit test a WCF service which is (correctly) throwing FaultExceptions for a certain reproducible error. The unit tests get an instance of the WCF client and call the applicable service method, which throws a FaultException.
All of that works as you would expect, but I am having difficulty unit testing this, because the fault causes the IDE to break when the error isn't caught in the service implementation. Because I am using faults, and not exceptions, I was expecting the IDE to serialize the exception and send it to the client, where it would raise an exception.
I do see that there is a configuration option to disable breaking for specific user-unhandled exceptions, but I was hoping somebody could point out a better way to achieve the same results, as this isn't easily doable in a team environment.
Here's some sample code of what the implementation currently looks like...
The unit test project has a service reference to my WCF service, and I have defined the interface as such:
[OperationContract(Name = "DoSomething")]
[FaultContract(typeof(EpicFail))]
ResponseObject DoSomething(RequestObject requestObject);
The fault is defined as such:
[DataContract]
public class EpicFail
{
public EpicFail(string action)
{
this.Reason = "Epic Fail";
this.Action = action;
}
[DataMember]
public string Reason
{
get;
set;
}
[DataMember]
public string Action
{
get;
set;
}
}
The code that calls the service looks vaguely like this:
[TestMethod()]
[ExpectedException(typeof(FaultException<EpicFail>))]
public void FaultTest_Fails_Epicly()
{
bool testPassed = false;
try
{
ResponseObject resp = GetServiceClient().DoSomething(req);
}
catch (FaultException<EpicFail>)
{
testPassed = true;
}
Assert.IsTrue(testPassed);
}
I edited the code to show that I am using the ExpectedException attribute and it doesn't seem to be having much effect on keeping the IDE/Debugger from breaking when the exception is thrown in the service.
You can always use ExpectedExceptionAttribute (in NUnit) to make sure this is the exception thrown. MSTest has similar concept as well.
[ExpectedException(typeof(MyException))]
void my_test()
{
// test
}
If you have some Mock verification to do, I would use try/catch block and verify in the catch and then throw the exception.
UPDATE
When you are using ExpectedException attribute, you are not supposed to catch the exception, instead you need to let the NUnit that runs your test to catch it.
If you need to verify special information in the exception then you catch the exception, verify the information and then rethrow:
[ExpectedException(typeof(MyException))]
void my_test()
{
try
{
// call the service
}
catch(MyException ex)
{
Assert.IsTrue(ex.Message.Contains("error code 200"));
throw ex;
}
}
mattv,
Why does this test has to access the service remotely? From what I see your code:
ResponseObject resp = GetServiceClient().DoSomething(req);
Is somehow getting a service client, and not a service instance itself. I'd advise to test the service concrete class directly for unit tests.
However, if you need this scenario, have you tried NOT CATCHING the exception and running the test? Does it give the same result?
And by the way, if you need to catch and rethrow use the following pattern:
try {
//Do something
}
catch(SomeException e) {
//Do something with e
throw
}

Categories

Resources