wcftracelistener

Trace WCF call from client


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?


Solution

  • 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);
    
        }
    }