2016-07-28 7 views
4

完全な要求と応答の本文を含むサービス参照に送信されるすべての送信要求をログに記録しようとしています。私は、behaviorExtensionsを使って解決策があると思っていましたが、展開した後、拡張が複数の要求間で共有されていることが明らかになりました。IEndpointBehaviorライフサイクル/ロギングサービスの呼び出し

はここに私の現在のコードです:私は、ログに記録するポイントに到達したとき、私は行動を抽出し、その後

public class LoggingBehaviorExtender : BehaviorExtensionElement 
{ 
    public override Type BehaviorType => typeof(LoggingRequestExtender); 
    protected override object CreateBehavior() { return new LoggingRequestExtender(); } 
} 

public class LoggingRequestExtender : IClientMessageInspector, IEndpointBehavior 
{ 
    public string Request { get; private set; } 
    public string Response { get; private set; } 

    #region IClientMessageInspector 

    public virtual object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) 
    { 
     Request = request.ToString(); 
     Response = null; 
     return null; 
    } 
    public virtual void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) 
    { 
     Response = reply.ToString(); 
    } 

    #endregion 

    #region IEndpointBehavior 

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } 

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 
    { 
     clientRuntime.MessageInspectors.Add(this); 
    } 

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { } 

    public void Validate(ServiceEndpoint endpoint) { } 

    #endregion 
} 

... LoggingRequestExtenderにデバッグログを追加

var lre = client.Endpoint.Behaviors.OfType<LoggingRequestExtender>().FirstOrDefault(); 
var req = lre?.Request; 
var resp = lre?.Response; 

は、私はそれを見つけました複数の要求に対して一度しかインスタンス化されませんでした。

この動作クラスがスレッドごとに新しくインスタンス化されることを確認する方法はありますか?あるいは、サービスコールを行うときに、完全なリクエスト/レスポンスボディを得るためのより良い方法がありますか?

編集/部分の答え:

これを書いて以来、私は、GUIDを使用して、要求と応答を接続できるようにBeforeSendRequestによって返された値がcorrelationStateとしてAfterReceiveReplyに渡されることを発見した:

public virtual object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) 
{ 
    var guid = Guid.NewGuid(); 
    WebServiceLog.LogCallStart(guid, channel.RemoteAddress.ToString(), request.ToString()); 
    return guid; 
} 

public virtual void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) 
{ 
    Guid guid = (Guid)correlationState; 
    WebServiceLog.LogCallEnd(guid, reply.ToString()); 
} 

このアプローチには2つの欠陥があります。これは、ログインサートを必要とし、1つのインサートではなく更新することです。

例外が発生した場合(タイムアウトなど)、AfterRecieveSupplyには一度も当たっていないので、ログには何が起こったのか分からない。私は、サービス要求のログに例外ログを結びつけることは何もない私は別に...

try 
{ 
    response = client.SomeFunction(request); 
} 
catch (Exception ex) 
{ 
    AppLog.Error("Some function failed", ex); 
} 

を例外をログに記録することができます...しかし、私はBeforeSendRequest/AfterReceiveReplyの外でGUIDをアクセスする方法を見ることができません。

答えて

0

これにはいくつかのアプローチがあります。

1、コールを個別にログに記録しなければならない状況は、そうである必要はありません。 WCFサービスが負荷分散されていないサーバーにある場合は、GuidをキーとしてMemoryCacheに要求を追加します。リクエストが来たら、リクエストを取り出して一度ログインします。タイムアウトした呼び出しをキャプチャするには、メモリキャッシュをチェックしてx分ごとに取り出してログする(スレッドセーフを保証する適切なロックを使用して)スレッドでプロセスを実行できます。

WCFサービスが負荷分散環境にある場合は、上記と同じですが、no sql型データストアに格納します。

2、あなたのスコープ内で発信コールを変更するコードはありますか?そうであれば、ビヘイビア拡張を作成せずに、代わりにメッセージロガーを作成することができます。あなたは、このような素敵なコードを書くことができますIDisposableを実装するクラスを使用して...

RequestMessage request = new RequestMessage(); 
ResponseMessage response = null; 

using (_messageLogger.LogMessage(request,() => response, CallContextHelper.GetContextId(), enabled)) 
{ 
    response = _outboundService.DoSomething(request); 
} 

、このことは、その後の任意のDisposeメソッドで処理されるスレッドをタイムアウトしキャプチャするために別のプロセスを必要としません。

もっと明快さが必要な場合は、私に教えてください。うまくいけば、これはあなたに役立ちます。

関連する問題