Well, I was going to name this and a question of context, but apparently the word question isn't allowed in titles.
Anyway, here's the issue: I use IErrorHandler
in my WCF services in order to provide logging without cluttering up all of my service code. Until now, this has worked great. However, now that I'm trying to move to completely asynchronous services, I'm encountering the issue of the call stack
being a return stack instead of a causality chain.
Now, I tried using Stephen Cleary's logical call context MyStack, combined with Ninject
's Intercept
extensions..
Ninject:
Bind<IThing>().To<Thing>()
.Intercept()
.With<SimpleContextGenerator>();
SimpleContextGenerator:
public class SimpleContextGenerator : IInterceptor
{
public void Intercept(IInvocation invocation)
{
using (MyStack.Push(
string.Join(".",
invocation.Request.Method.DeclaringType.FullName,
invocation.Request.Method.Name)))
{
invocation.Proceed();
}
}
}
The problem, however, is twofold: 1) The using
completes before the error actually throws, and 2) 1 doesn't even matter because the entire context is cleared out by the time I get to IErrorHandler
. I can comment out the code in Pop
in MyStack
, and CurrentContext.IsEmpty
is true
when I hit ProvideFault
in IErrorHandler
.
So, my question is also two-part:
1) Is there a way to keep the context through to the IErrorHandler
calls?
2) If not, is there another way to log errors on a global scale that does have access to the context?
I am using .NET 4.5, Ninject 3.2, and DynamicProxy 3.2.
To be honest, I'd be happy just knowing where the Exception was thrown - current class and method are enough for my purposes; the full stack isn't required.
EDIT: If I put it in the OperationContext
using an IExtension<>
, I can keep it around until I get to the IErrorHandler
. However, I still don't know when a method ends, so I can't be sure where the exception occurred.
In order to track the stack in such a way as to be available in the
IErrorHandler
, use anIExtension<>
:Here's the
Frame
that is being tracked:And the
Parameter
:Now, you want to manage the stack using this
SimpleContextGenerator
:IInterceptor
here isNinject.Extensions.Interception.IInterceptor
.In order to keep the
OperationContext
available for each call, you need to use thisOperationContextSynchronizationContext
:Then you just need to hook it all up in your
Ninject
binding:Note that this can only be applied at an interface-to-concrete-class binding, which is why we can't get the service itself into the stack in this manner. We could wrap every service method (better than wrapping every single call), but I don't think we could even do it with a module, since the service frame wouldn't have the exception for the stack walk (below).
Finally, in the
IErrorHandler
:Here's the
Unpack
extension method: