【问题标题】:Exception Logging for WCF Services using ELMAH使用 ELMAH 的 WCF 服务的异常日志记录
【发布时间】:2010-10-28 02:38:23
【问题描述】:

我们正在使用出色的ELMAH 来处理 ASP.NET 3.5 Web 应用程序中未处理的异常。除了使用 REST 功能使用的 WCF 服务之外,这对于所有站点都非常有效。当应用程序代码未处理的操作方法中发生异常时,WCF 会根据服务协定和配置设置以各种方式处理它。这意味着异常不会最终触发 ELMAH 使用的 ASP.NET HttpApplication.Error 事件。我知道的两种解决方案是:

第一个选项非常简单,但不完全是DRY。第二个选项只需要您在实现属性和 ErrorHandler 之后使用自定义属性来装饰每个服务。我已经根据Will's 的工作完成了这项工作,但我想在发布代码之前验证这是正确的方法

我错过了更好的方法吗?

IErrorHandler 的 MSDN 文档说 HandleError 方法是进行日志记录的地方,但 ELMAH 访问的是 HttpContext.Current.ApplicationInstance,即即使 HttpContext.Current 可用,此方法中的 null 也是如此。在 ProvideFault 方法中调用 Elmah 是一种解决方法,因为 ApplicationInstance 已设置,但这与 API 文档中描述的意图不符。 我在这里遗漏了什么吗?文档确实声明您不应依赖在操作线程上调用的 HandleError 方法,这可能是 ApplicationInstance 在此范围内为 null 的原因。

【问题讨论】:

    标签: asp.net wcf exception logging elmah


    【解决方案1】:

    我的博客文章中的解决方案(在 OP 中引用)基于我们曾经/正在使用的现有解决方案,以在错误状态下更改 HTTP 响应代码。

    因此,对我们来说,将异常传递给 ELMAH 只需一行代码。如果有更好的解决方案,我也很想知道。

    供后代/参考和潜在改进 - 这是当前解决方案的代码。

    HttpErrorHandler 和 ServiceErrorBehaviourAttribute 类

    using System;
    using System.ServiceModel;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.Collections.ObjectModel;
    using System.Net;
    using System.Web;
    using Elmah;
    namespace YourApplication
    {
        /// <summary>
        /// Your handler to actually tell ELMAH about the problem.
        /// </summary>
        public class HttpErrorHandler : IErrorHandler
        {
            public bool HandleError(Exception error)
            {
                return false;
            }
    
            public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
            {
                if (error != null ) // Notify ELMAH of the exception.
                {
                    if (System.Web.HttpContext.Current == null)
                        return;
                    Elmah.ErrorSignal.FromCurrentContext().Raise(error);
                }
            }
        }
        /// <summary>
        /// So we can decorate Services with the [ServiceErrorBehaviour(typeof(HttpErrorHandler))]
        /// ...and errors reported to ELMAH
        /// </summary>
        public class ServiceErrorBehaviourAttribute : Attribute, IServiceBehavior
        {
            Type errorHandlerType;
    
            public ServiceErrorBehaviourAttribute(Type errorHandlerType)
            {
                this.errorHandlerType = errorHandlerType;
            }
    
            public void Validate(ServiceDescription description, ServiceHostBase serviceHostBase)
            {
            }
    
            public void AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters)
            {
            }
    
            public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
            {
                IErrorHandler errorHandler;
                errorHandler = (IErrorHandler)Activator.CreateInstance(errorHandlerType);
                foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
                {
                    ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
                    channelDispatcher.ErrorHandlers.Add(errorHandler);
                }
            }
        }
    }
    

    使用示例

    使用 ServiceErrorBehaviour 属性装饰您的 WCF 服务:

    [ServiceContract(Namespace = "http://example.com/api/v1.0/")]
    [ServiceErrorBehaviour(typeof(HttpErrorHandler))]
    public class MyServiceService
    {
      // ...
    }
    

    【讨论】:

    • 我在 WCF 服务上尝试过这个解决方案并得到这个错误:( System.ArgumentNullException: Value cannot be null. Parameter name: context at Elmah.ErrorSignal.FromContext(HttpContext context) in c:\ builds\ELMAH\src\Elmah\ErrorSignal.cs:line 67 at Elmah.ErrorSignal.FromCurrentContext() in c:\builds\ELMAH\src\Elmah\ErrorSignal.cs:line 61 请帮忙
    • 听起来您不是在 HttpContext 中操作(即在 ASP.NET 下)——这意味着 ELMAH 不适合您。如果您实际上是在 ASP.NET 下运行,请将您的问题作为单独的问题发布
    • 会的!我在 HttpContext 下运行。仍然没有工作。这是我想出的解决方案。 stackoverflow.com/questions/2997076/…
    • 嗨,当 WCF 服务使用 basicHttpBinding 托管在 IIS 中时,这对我不起作用,这是我的帖子 stackoverflow.com/questions/4470362/…。请帮忙。
    【解决方案2】:

    在创建 BehaviorExtensionElement 时,甚至可以使用 config 来激活该行为:

    public class ErrorBehaviorExtensionElement : BehaviorExtensionElement
    {
        public override Type BehaviorType
        {
            get { return typeof(ServiceErrorBehaviourAttribute); }
        }
    
        protected override object CreateBehavior()
        {
            return new ServiceErrorBehaviourAttribute(typeof(HttpErrorHandler));
        }
    }
    

    配置:

    <system.serviceModel>
        <extensions>
          <behaviorExtensions>
            <add name="elmah" type="Namespace.ErrorBehaviorExtensionElement, YourAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
          </behaviorExtensions>
        </extensions>
        <behaviors>
          <serviceBehaviors>
            <behavior>
              <elmah />
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>
    

    这样也可以将 ELMAH 与 RIA 服务结合使用!

    【讨论】:

    • WCF RIA 服务 WCF 服务。因此您也可以在 RIA 服务中应用相同的属性,而无需创建额外的行为。
    【解决方案3】:

    这对某些人来说可能很明显,但我只是花了很长时间试图弄清楚为什么我的 HttpContext.Current 为空,尽管遵循了 Will Hughes 的所有出色答案。尴尬的是,我意识到这是因为我的 WCF 服务是由 MSMQ 消息激活的。

    我最终重写了ProvideFault() 方法:

    if (HttpContext.Current == null)
    {
        ErrorLog.GetDefault(null).Log(new Error(error));
    }
    else
    {
        ErrorSignal.FromCurrentContext().Raise(error);
    }
    

    【讨论】:

      【解决方案4】:

      我是根据 Will 的工作完成的 但我想验证这是 发帖前的正确做法 代码。

      我认为这是一个很好的方法(感谢 Will 发表这篇文章!)。我不认为威尔或你在这里错过了什么。实现 IErrorHandler 是捕获所有可能的服务器端异常的首选方法,否则这些异常可能会导致通信通道出现故障(拆除),因此它是挂钩某些日志记录(如 ELMAH)的自然场所。

      马克

      【讨论】:

        【解决方案5】:

        我无法使用 WCF 数据服务获得建议的答案。我连接了行为属性等,但仍然没有记录任何错误。相反,我最终将以下内容添加到服务实现中:

        protected override void HandleException(HandleExceptionArgs args)
        {
            Elmah.ErrorSignal.FromCurrentContext().Raise(args.Exception);
            base.HandleException(args);
        }
        

        【讨论】:

          【解决方案6】:

          我没有尝试过使用 REST 明确地执行此操作,并且我自己也没有使用过 ELMAH,但另一个值得研究的选项可能是使用 IDispatchMessageInspector 而不是 IErrorHandler 连接到 WCF。

          【讨论】:

            猜你喜欢
            • 2010-10-23
            • 2011-03-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-10-28
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多