web-dev-qa-db-ja.com

ELMAHを使用したWCFサービスの例外ログ

ASP.NET 3.5 Webアプリケーションで未処理の例外を処理するために、優れた [〜#〜] elmah [〜#〜] を使用しています。これは、REST機能を使用して消費されているWCFサービス以外のすべてのサイトで非常にうまく機能します。アプリケーションコードで処理されない操作メソッド内で例外が発生すると、WCFはこれは、サービスコントラクトと構成設定に応じて、さまざまな方法で行われます。つまり、例外がASP.NET HttpApplication.Errorイベントを発生させない [〜#〜] elmah [〜#〜] これに対処するために私が知っている2つのソリューションは次のとおりです。

  • すべてのメソッド呼び出しをtry {} catch(Exception ex){Elmah.ErrorSignal.FromCurrentContext()。Raise(ex);でラップします。スロー; catchブロック内で明示的にElmahを呼び出す。
  • IErrorHandler を使用して Will Hughes ' ブログ投稿 WCFとELMAHを一緒にうまく動作させる を使用して、別のErrorHandlerへのELMAHの呼び出しを除外します。

最初のオプションは非常に単純ですが、正確ではありません [〜#〜] dry [〜#〜] 。 2番目のオプションでは、属性とErrorHandlerを実装した後、カスタム属性で各サービスを装飾するだけで済みます。私は Will's の作業に基づいてこれを行いましたが、コードを投稿する前に、これが正しいアプローチであることを確認したいと思います。

見逃したより良い方法はありますか?

IErrorHandler のMSDNドキュメントでは、HandleErrorメソッドがロギングを行う場所であると述べていますが、 [〜# 〜] elmah [〜#〜] HttpContext.Current。ApplicationInstanceにアクセスします。これは、HttpContext.Currentが使用可能であっても、このメソッド内ではnullです。 。 ApplicationInstanceが設定されているため、ProvideFaultメソッド内でElmahを呼び出すことは回避策ですが、これはAPIドキュメントで説明されている意図と一致しません。 ここに何か不足していますか?ドキュメントには、操作スレッドで呼び出されるHandleErrorメソッドに依存するべきではないことが記載されています。この範囲で。

62

私のブログ投稿(OPで参照)のソリューションは、エラー状態の間にHTTP応答コードを変更するために使用していた既存のソリューションに基づいていました。

したがって、私たちにとって、例外をELMAHに渡すのは1行の変更でした。より良い解決策があれば、私もそれについて知りたいです。

後世/参照、および潜在的な改善のために-現在のソリューションからのコードは次のとおりです。

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
{
  // ...
}
88
user111013

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>

そうすることで、RIAサービスと組み合わせてELMAHを使用することもできます。

9
riezebosch

これは一部の人には明らかかもしれませんが、Will Hughesの優れた回答をすべてフォローしたにもかかわらず、HttpContext.Currentがnullである理由を理解するためにかなりの時間を費やしました。恥ずかしいことに、これは私のWCFサービスがMSMQメッセージによってアクティブ化されるためであることに気付きました。

ProvideFault()メソッドを書き直してしまいました。

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

私はウィルの仕事に基づいてこれを行いましたが、コードを投稿する前にこれが正しいアプローチであることを確認したいと思います。

これは素晴らしいアプローチだと思います(この投稿ではWillへのkudos!)。ウィルかあなたはここで何かを逃したとは思わない。 IErrorHandlerの実装は、そうでなければ通信チャネルに障害が発生する(破壊される)可能性があるすべての可能なサーバー側の例外をキャプチャするための推奨される方法であり、ELMAHのようなログにフックするのは自然な場所です。

マーク

2
marc_s

提案された回答をWCFデータサービスで機能させることができませんでした。ビヘイビアー属性などを配線しましたが、それでもエラーは記録されませんでした。代わりに、サービス実装に以下を追加することになりました。

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

RESTのものを使用してこれを明示的に試したことはなく、ELMAHを自分で使用したことはありませんが、 IErrorHandlerの代わりにIDispatchMessageInspector

0
tomasr