简体   繁体   English

如何在WCF基于任务的操作中获得准确的异常堆栈

[英]How do I get accurate Exception stack in WCF Task-based Operation

I'm using the WCF IErrorHandler interface to trap and log errors on the server side of a WCF service. 我正在使用WCF IErrorHandler接口来捕获和记录WCF服务的服务器端的错误。 However, the StackTrace of the exception passed to HandleError and ProvideFault is messed up: 但是,传递给HandleError和ProvideFault的异常的StackTrace搞砸了:

at System.ServiceModel.Dispatcher.TaskMethodInvoker.InvokeEnd(Object instance, Object[]& outputs, IAsyncResult result) at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeEnd(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage7(MessageRpc& rpc) ....lots more at System.ServiceModel.Dispatcher.TaskMethodInvoker.InvokeEnd(Object instance,Object []&outputs,IAsyncResult result)at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeEnd(MessageRpc&rpc)at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage7(MessageRpc&rpc) )......还有更多

I'm not surprised to see random Dispatcher methods in the stack trace, but I assumed I would see my own method at the top of the stack. 我在堆栈跟踪中看到随机的Dispatcher方法并不奇怪,但我想我会在堆栈顶部看到自己的方法。

I've determined that this only happens on operations that look like 我已经确定这只会发生在看起来像的操作上

[OperationContract]
public Task<int> MyOperation()
{
  throw new ApplicationException("test");
}

Services that look like this have a proper stack trace for me to log: 看起来像这样的服务有一个适当的堆栈跟踪供我记录:

[OperationContract]
public int MySyncOperation()
{
  throw new ApplicationException("test");
}

As an FYI, here's what the error handler methods are like: 作为一个FYI,这是错误处理程序方法的样子:

public class MyErrorHandler : IErrorHandler
{
  public bool HandleError(Exception error)
  {
    //variable 'error' has wrong stack trace if exception sourced from Task<int> operation
    return false;
  }
  public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
  {
    //variable 'error' has wrong stack trace if exception sourced from Task<int> operation
  }
}

Note that the exception type and message are correct, so it's as if they're incorrectly rethrowing my exception somewhere with a 'throw ex' rather than just 'throw'; 请注意,异常类型和消息是正确的,所以就好像他们错误地在某处使用'throw ex'而不仅仅是'throw'重新抛出我的异常;

Is there any way to get the correct stack trace of the exception from one of the IErrorHandler methods? 有没有办法从IErrorHandler方法之一获取正确的异常堆栈跟踪?

I ended up solving this using the following custom operation invoker. 我最终使用以下自定义操作调用程序解决了这个问题。 My only goal was to log the errors with the proper stack trace, so the error that ends up being thrown retains the poor stack trace. 我唯一的目标是使用正确的堆栈跟踪记录错误,因此最终被抛出的错误会保留较差的堆栈跟踪。

public class ErrorLoggingOperationInvokerFacade : IOperationInvoker
{
    private readonly IOperationInvoker _invoker;
    private readonly ILog _log;

    public ErrorLoggingOperationInvokerFacade(IOperationInvoker invoker, ILog log)
    {
        _invoker = invoker;
        _log = log;
    }

    public object[] AllocateInputs()
    {
        return _invoker.AllocateInputs();
    }

    public object Invoke(object instance, object[] inputs, out object[] outputs)
    {
        return _invoker.Invoke(instance, inputs, out outputs);
    }

    public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
    {
        return _invoker.InvokeBegin(instance, inputs, callback, state);
    }

    public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
    {
        var task = result as Task;
        if (task != null && task.IsFaulted && task.Exception != null)
        {
            foreach (var error in task.Exception.InnerExceptions)
            {
                _log.Log(error);
            }
        }

        return _invoker.InvokeEnd(instance, out outputs, result);
    }

    public bool IsSynchronous { get { return _invoker.IsSynchronous; } }
}

It can be attached with an attribute on your service class or method: 它可以附加您的服务类或方法的属性:

public class LogErrorsAttribute : Attribute, IServiceBehavior, IOperationBehavior
{
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (var operation in serviceHostBase.Description.Endpoints.SelectMany(endpoint => endpoint.Contract.Operations))
        {
            if (!operation.Behaviors.Any(b => b is LogErrorsAttribute))
                operation.Behaviors.Add(this);
        }
    }

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
    {
        dispatchOperation.Invoker = new ErrorLoggingOperationInvokerFacade(dispatchOperation.Invoker, WcfDependencyManager.ResolveLogger());
    }

    public void Validate(OperationDescription operationDescription) { }
    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { }
    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { }
    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { }
}

Previously I was logging the errors in an implementation of the IErrorHandler interface, but at that point the stack trace was already messed up. 以前我在IErrorHandler接口的实现中记录了错误,但此时堆栈跟踪已经搞砸了。 I tried to modify the operation invoker to throw an exception with the proper stack trace but I never got it to work just right. 我试图修改操作调用程序以使用正确的堆栈跟踪抛出异常,但我从来没有让它正常工作。 For some reason, my custom fault exceptions turned into the generic fault exceptions, so I abandoned that approach. 出于某种原因,我的自定义故障异常变成了通用故障异常,因此我放弃了这种方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM