Trace WCF call from client
Asked Answered
D

3

1

I've built a windows forms client application which makes a WCF call. I'd like to be able to display the actual request and response, includeing SOAP header, and information about the POST/Get, from within the application. Is there a way to configure a trace listener on the client and consume it from within the client, displaying the contents in a text box?

I've configured tracing which outputs messages to a file. This configuration is on my client application, so it's logging all calls it is making to the wcf service and the response. So the sources have been added and for each source is an XmlTraceListener, which handles writing to an xml log file. What I'm looking to do now is utilize a trace listener from within the client application itself and write to a textbox control.

<system.diagnostics>
    <sources>
        <source name="System.ServiceModel" 
                switchValue="All">
            <listeners>
                <add name="xmlTraceListener" />
            </listeners>
        </source>
        <source name="System.ServiceModel.MessageLogging" 
                switchValue="All">
             <listeners>
                 <add name="xmlTraceListener" />
             </listeners>
        </source>
    </sources>
    <sharedListeners>
        <add name="xmlTraceListener" 
             type="System.Diagnostics.XmlWriterTraceListener" 
             initializeData="ClientLogBasic.svclog" />
    </sharedListeners>
    <trace autoflush="true" />
</system.diagnostics>

<!-- child of the <system.serviceModel> element -->
<diagnostics>
    <messageLogging maxMessagesToLog="10000"
                    logEntireMessage="true"
                    logMessagesAtServiceLevel="true"
                    logMalformedMessages="true"
                    logMessagesAtTransportLevel="true">
        <filters>
           <clear/>
        </filters>
    </messageLogging>
</diagnostics>

So now that I've got message logging working, I create my own trace listener that can write to a textbox:

public class MyTraceListender : System.Diagnostics.TraceListener
{
    private TextBox txt_m;
    public MyTraceListender(TextBox txt)
        : base()
    {
        txt_m = txt;
    }

    private delegate void delWrite(string sText);

    public override void Write(string message)
    {
        txt_m.Invoke(new delWrite(AsyncWrite), message);
    }

    public override void WriteLine(string message)
    {
        this.Write(message + System.Environment.NewLine);
    }

    private void AsyncWrite(string sMessage)
    {
        this.txt_m.AppendText(sMessage);
    }
}

Now that I've got my trace listener, I want to try using it from my windows forms client application.

public partial class Form1 : Form
{
    private MyTraceListender listener_m;

    public Form1()
    {
        InitializeComponent();

        listener_m = new MyTraceListender(this.txtOutput);

        System.Diagnostics.Trace.Listeners.Add(listener_m);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();
        string sValue = client.GetData(1234);
    }
}

At this point, I'm still not seeing logging to the textbox control. I'm thinking that the listener is not associated to the source, so I tried the following:

public partial class Form1 : Form
{
    private MyTraceListender listener_m;

    public static System.Diagnostics.TraceSource source = new System.Diagnostics.TraceSource("System.ServiceModel.MessageLogging", System.Diagnostics.SourceLevels.All);

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();
        string sValue = client.GetData(1234);
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        listener_m = new MyTraceListender("test", this.txtOutput);
        source.Listeners.Add(listener_m);
    }
}

After all of this, messages are not being logged to the textbox on the form. They are being logged to the file via the XmlTraceListener, so I'm assuming the issue is how I'm adding my custom listener via System.Diagnostics.Trace.Listeners.Add(). Is this the correct approach?

Disputable answered 21/5, 2013 at 20:40 Comment(3)
Please don't just ask us to solve the problem for you. Show us how you tried to solve the problem yourself, then show us exactly what the result was, and tell us why you feel it didn't work. See "What Have You Tried?" for an excellent article that you really need to read.Marj
The simplest way by far is to use wireshark. Add capture filter to limit the traffic.Twin
WCF is one of the few libraries that makes extensive use of System.Diagnostic trace & has a trace viewer specifically for examining WCF trace. Here is a good start: msdn.microsoft.com/en-us/library/ms733025.aspxMaramarabel
I
2

This solution is mostly through app.config. Let me know if you prefer a code-based solution.

In app.config, add your listener to the list of shared listeners, as well as a listener for the message logging source. In 'listeners' list, the name must match the name later on in the 'sharedListeners' list. In 'sharedListeners' list, the 'type' must include full class name with namespace, as well as assembly name:

<system.diagnostics>
<sources>
  <source name="System.ServiceModel" switchValue="All">
    <listeners>
      <add name="xmlTraceListener"/>
    </listeners>
  </source>
  <source name="System.ServiceModel.MessageLogging" switchValue="All">
    <listeners>
      <add name="xmlTraceListener"/>
      <add name="textBoxListener"/>
    </listeners>
  </source>
</sources>
<sharedListeners>
  <add name="xmlTraceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="ClientLogBasic.svclog"/>
  <add name="textBoxListener" type="WinTransmitterClient.MyTraceListener, WinTransmitterClient" initializeData=""/>
</sharedListeners>
<trace autoflush="true"/>

Because MyTraceListener will be constructed by the framework, its constructor must match the base class. So instead, make the textbox a property that can be set when needed.

In MyTraceListener.cs:

public class MyTraceListener : System.Diagnostics.TraceListener
{
    public TextBox txt_m
    {
        get;set;
    }          

    public MyTraceListener()
        : base()
    {}

    // rest as before ...

In Form1.cs, grab the custom listener after the client is created, and set the text box. No other code is needed. This is the entire Form1.cs, excluding 'using' and 'namespace' lines:

public partial class Form1 : Form
{
    public static System.Diagnostics.TraceSource source = new System.Diagnostics.TraceSource("System.ServiceModel.MessageLogging", System.Diagnostics.SourceLevels.All);

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        ServiceReference1.ContactManagerTextServiceClient client = new ServiceReference1.ContactManagerTextServiceClient();

        // identifier in quotes must match name from config file
        MyTraceListener mtl = source.Listeners["textBoxListener"] as MyTraceListener;

        // of course this doesn't need to be done at every button click, but you get the idea
        mtl.txt_m = this.txtOutput;

        string sValue = client.GetData(1234);

    }
}
Ia answered 28/5, 2013 at 18:54 Comment(1)
Works great! I'd be really interested in a code based solution, but I might be able to derive one now that I know a little more how it works.Disputable
L
5

Edit the Configuration file using WCF Configuration Editor, and enable tracing as shown below

enter image description here

Leucite answered 23/5, 2013 at 13:46 Comment(0)
I
2

This solution is mostly through app.config. Let me know if you prefer a code-based solution.

In app.config, add your listener to the list of shared listeners, as well as a listener for the message logging source. In 'listeners' list, the name must match the name later on in the 'sharedListeners' list. In 'sharedListeners' list, the 'type' must include full class name with namespace, as well as assembly name:

<system.diagnostics>
<sources>
  <source name="System.ServiceModel" switchValue="All">
    <listeners>
      <add name="xmlTraceListener"/>
    </listeners>
  </source>
  <source name="System.ServiceModel.MessageLogging" switchValue="All">
    <listeners>
      <add name="xmlTraceListener"/>
      <add name="textBoxListener"/>
    </listeners>
  </source>
</sources>
<sharedListeners>
  <add name="xmlTraceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="ClientLogBasic.svclog"/>
  <add name="textBoxListener" type="WinTransmitterClient.MyTraceListener, WinTransmitterClient" initializeData=""/>
</sharedListeners>
<trace autoflush="true"/>

Because MyTraceListener will be constructed by the framework, its constructor must match the base class. So instead, make the textbox a property that can be set when needed.

In MyTraceListener.cs:

public class MyTraceListener : System.Diagnostics.TraceListener
{
    public TextBox txt_m
    {
        get;set;
    }          

    public MyTraceListener()
        : base()
    {}

    // rest as before ...

In Form1.cs, grab the custom listener after the client is created, and set the text box. No other code is needed. This is the entire Form1.cs, excluding 'using' and 'namespace' lines:

public partial class Form1 : Form
{
    public static System.Diagnostics.TraceSource source = new System.Diagnostics.TraceSource("System.ServiceModel.MessageLogging", System.Diagnostics.SourceLevels.All);

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        ServiceReference1.ContactManagerTextServiceClient client = new ServiceReference1.ContactManagerTextServiceClient();

        // identifier in quotes must match name from config file
        MyTraceListener mtl = source.Listeners["textBoxListener"] as MyTraceListener;

        // of course this doesn't need to be done at every button click, but you get the idea
        mtl.txt_m = this.txtOutput;

        string sValue = client.GetData(1234);

    }
}
Ia answered 28/5, 2013 at 18:54 Comment(1)
Works great! I'd be really interested in a code based solution, but I might be able to derive one now that I know a little more how it works.Disputable
A
0

Try creating a message inspector. On the client side you'd have to implement IClientMessageInspector which will allow you to intercept the message before it is being sent as well as the response which is received prior to passing it further into the application. You'll find more information on MSDN

Anopheles answered 22/5, 2013 at 9:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.