かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

WCFのサービスが何処に対して呼び出しをしているかログを取りたい

ということでちょっと調べたことのまとめ。(正しいアプローチかどうかは謎です。詳しい人突っ込みどころあったらお願いしますm(_ _)m)
とりあえずクライアント側に自作ビヘイビアをつっこんで、そこでIClientMessageInspectorを継承したクラスをエンドポイントに差し込むイメージでした。

// 自作ビヘイビアとメッセージインスペクター
public class SampleBehavior : IEndpointBehavior
{
    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
        Console.WriteLine("AddBindingParameters");
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
    {
        Console.WriteLine("ApplyClientBehavior");
        // クライアントのメッセージインスペクターを仕掛ける
        clientRuntime.MessageInspectors.Add(new Inspector());
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
    {
        Console.WriteLine("ApplyDispatchBehavior");
    }

    public void Validate(ServiceEndpoint endpoint)
    {
        Console.WriteLine("Validate");
    }
}

// メッセージインスペクター
public class Inspector : IClientMessageInspector
{
    public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
    {
        Console.WriteLine("AfterReceiveReply");
    }

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
    {
        // 何処に接続してそうなのか見る
        Console.WriteLine("BeforeSendRequest : {0}", channel.RemoteAddress.Uri);
        return null;
    }
}

ポイントはApplyClientBehaviorで自作のIClientMessageInspectorを差し込んでるところとIClientMessageInspectorの実装クラスのBeforeSendRequestでchannel経由で接続先のアドレスをとっているところです。例ではUriを直接出力してますが、まぁUriをばらしてポートとか接続先のIPとかもとれると思います。

お試し

下記のようなサービスを作って試してみました。

// 公開するサービス
[ServiceContract]
public interface IService
{
    [OperationContract]
    string Greet(string name);
}

public class ServiceImpl : IService
{
    public string Greet(string name)
    {
        return string.Format("{0}さんこんにちは", name);
    }
}

あとは、Mainで以下のようにサービスを公開しつつクライアントも作って(作る過程で作成したビヘイビアを設定しています)呼び出しを行っています。

var address = new Uri("http://localhost:8888/Service");
            
// サービスの公開
var host = new ServiceHost(typeof(ServiceImpl), address);
host.AddServiceEndpoint(
    typeof(IService), new BasicHttpBinding(), address);
host.Open();

// クライアントの作成
var channel = new ChannelFactory<IService>(
    new BasicHttpBinding(),
    new EndpointAddress(address.ToString()));
// ここでエンドポイントに作成したビヘイビアを追加する
channel.Endpoint.Behaviors.Add(new SampleBehavior());

// サービスの呼び出し
var client = channel.CreateChannel();
Console.WriteLine(client.Greet("Tanaka"));
Console.WriteLine(client.Greet("Jiro"));
Console.WriteLine(client.Greet("Saburo"));

// 後始末
channel.Close();
host.Close();

実行すると以下のような結果になります。

Validate
AddBindingParameters
ApplyClientBehavior
BeforeSendRequest : http://localhost:8888/Service
AfterReceiveReply
Tanakaさんこんにちは
BeforeSendRequest : http://localhost:8888/Service
AfterReceiveReply
Jiroさんこんにちは
BeforeSendRequest : http://localhost:8888/Service
AfterReceiveReply
Saburoさんこんにちは
続行するには何かキーを押してください . . .

参考

やっぱ困ったときに頼りになるのはMSDNマガジン!