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

时间:2014-11-05 16:19:22

标签: c# wcf

我使用WCF IErrorHandler接口来捕获和记录WCF服务的服务器端的错误。但是,传递给HandleError和ProvideFault的异常的StackTrace搞砸了:

  

在   System.ServiceModel.Dispatcher.TaskMethodInvoker.InvokeEnd(对象   实例,对象[]&输出,IAsyncResult结果)at   System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeEnd(MessageRpc&安培;   rpc)at   System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage7(MessageRpc&安培;   RPC)       ....更多

我在堆栈跟踪中看到随机的Dispatcher方法并不奇怪,但我假设我会在堆栈顶部看到自己的方法。

我已经确定这只会发生在看起来像

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

看起来像这样的服务有一个适当的堆栈跟踪供我记录:

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

作为一个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
  }
}

请注意,异常类型和消息是正确的,因此,好像他们错误地将某个异常重新抛出某个地方并使用&#39; throw ex&#39;而不只是“扔掉”;

有没有办法从其中一个IErrorHandler方法中获取正确的异常堆栈跟踪?

1 个答案:

答案 0 :(得分:14)

我最终使用以下自定义操作调用程序解决了这个问题。我唯一的目标是使用正确的堆栈跟踪记录错误,因此最终被抛出的错误会保留较差的堆栈跟踪。

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; } }
}

可以使用服务类或方法的属性附加它:

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) { }
}

以前我在IErrorHandler接口的实现中记录了错误,但此时堆栈跟踪已经搞砸了。我试图修改操作调用程序以使用正确的堆栈跟踪抛出异常,但我从来没有让它正常工作。出于某种原因,我的自定义故障异常变成了通用故障异常,因此我放弃了这种方法。