【问题标题】:How do I get accurate Exception stack in WCF Task-based Operation如何在 WCF 基于任务的操作中获得准确的异常堆栈
【发布时间】:2014-11-05 16:19:22
【问题描述】:

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

在 System.ServiceModel.Dispatcher.TaskMethodInvoker.InvokeEnd(对象 实例,Object[]& 输出,IAsyncResult 结果)在 System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeEnd(MessageRpc& RPC)在 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");
}

作为一个仅供参考,以下是错误处理程序的方法:

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

请注意,异常类型和消息是正确的,所以就好像他们用“throw ex”而不是“throw”错误地在某处重新抛出我的异常;

有什么方法可以从 IErrorHandler 方法之一获取正确的异常堆栈跟踪?

【问题讨论】:

  • 您找到解决方案了吗?
  • 不幸的是没有。这不是世界末日,因为保留了异常类型和消息,我希望应用程序中发生的单独日志记录将帮助我对错误发生的位置进行三角测量。还没有上线,所以我不清楚这在实践中会有多有效
  • 此外,我的应用永远不会有任何错误,所以这是一个没有实际意义的问题 ;-)

标签: c# wcf


【解决方案1】:

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

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 接口的实现中记录错误,但那时堆栈跟踪已经搞砸了。我试图修改操作调用程序以使用正确的堆栈跟踪引发异常,但我从来没有让它正常工作。由于某种原因,我的自定义错误异常变成了通用错误异常,所以我放弃了这种方法。

【讨论】:

  • 谢谢。帮助我快速调试了我的问题。
  • 请把你自己的回答标记为答案——因为它就是答案。感谢您发布解决方案。
猜你喜欢
  • 1970-01-01
  • 2014-01-25
  • 2013-12-13
  • 2015-12-14
  • 2010-10-19
  • 2021-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多