かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

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マガジン!