Trace WCF call from client - wcf

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?

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

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

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

Related

WCF Custom trace listener to write logs in msmq without EnterpriseLibrary

How to write custom trace listener to write message logs in msmq?
I have added below custom MSMQTraceListener :
public class MSMQTraceListener : TraceListener
{
string _queueName;
public MSMQTraceListener(string queueName)
: base("MSMQListener")
{
_queueName = queueName;
if (!MessageQueue.Exists(_queueName))
MessageQueue.Create(_queueName);
}
public override void Write(string message)
{
SendMessageToQueue(message);
}
public override void WriteLine(string message)
{
SendMessageToQueue(message);
}
/// <summary>
/// Send message to queue.
/// </summary>
/// <param name="message">string: message</param>
private void SendMessageToQueue(string message)
{
try
{
MessageQueue messageQueue = new MessageQueue(_queueName, QueueAccessMode.Send);
messageQueue.Label = DateTime.Now.ToString();
messageQueue.Send(message);
messageQueue.Close();
}
catch (Exception ex)
{
}
}
}
And updated below diagnostic setting in my web.config file:
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="messages" type="Proj.Common.Diagnostics.MSMQTraceListener,Proj.Common" initializeData=".\private$\PerformanceTesting" />
</listeners>
</source>
</sources>
</system.diagnostics>
If you are in code hosted by MSMQ and want to write a message to say a log file
All .NET applications are the same as far as System.Diagnostics is concerned. Configure the listener in app.config, and use Trace or TraceSource to write to the listener. MSDN explains this better than I can.
If you want a trace listener that sends message to MSMSQ
Get this utility library, Essential Diagnostics, that makes working with System.Diagnostics less painful
Override the one TraceEvent() method on BaseTraceListener. Inside that method, you use the available parameters to send messages to whatever you'd like, for example an MSMQ destination.
Register your custom TraceListener in the usual way.

WCF service not working because of return data type?

I have created a WCF service with 2 methods :
[ServiceContract(Namespace = "")]
[SilverlightFaultBehavior]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MyDataService
{
[OperationContract]
public IQueryable<object> Service1()
{
PivotData pivot = new PivotData();
IQueryable<object> list = pivot.GeneratePivotData();
return list;
}
[OperationContract]
public string Service2()
{
return "hello";
}
}
The Service2 works perfectly fine. However , service1 returns the dreaded
"the remote server has returned an error : not found"
I believe it has to do with the return type IQueryable<object> , but I don't know what I should change to make it work. I tried List<string> , ObservableCollection<object> and a few others but to no avail.
What should I do the get my data back to the client ?
Thanks
depending on the question and conversation with Aron.
I supposed it is a WCF-Ria Services If so please retag the question, otherwise you may ignore this answer.
Try the below code.
Beside if you use ria services. you should use , [Association("FK_assos_name", "field", "field")] [Include] for complex properties and your base class should have at least one [Key] attributed field. Such as ID.
[OperationContract]
public BaseClass[] ServiceMethod1()
{
PivotData pivot = new PivotData();
IQueryable<object> list = pivot.GeneratePivotData();
return list.ToArray();
}
If you still get errors trace it;In your web.config add the lines below. Then open WcfDetailTrace.svclog with svclog viewer. Red parts will show you what goes wrong.
<system.diagnostics>
<trace autoflush="true">
<listeners>
</listeners>
</trace>
<sources>
<source name="System.ServiceModel"
switchValue="Information, ActivityTracing"
propagateActivity="true">
<listeners>
<add name="sdt"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData= "WcfDetailTrace.svclog" />
</listeners>
</source>
</sources>

WCF tracing & message logging - trace level warning

Assume I have a config file which looks like this:
...
<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="Warning,ActivityTracing" propagateActivity="true">
<listeners>
<add name="ServiceModelTraceListener" />
</listeners>
</source>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="ServiceModelTraceListener" />
</listeners>
</source>
</sources>
<sharedListeners>
<add initializeData="LogServer.svclog" type="System.Diagnostics.XmlWriterTraceListener" name="ServiceModelTraceListener" />
</sharedListeners>
<trace autoflush="true" />
</system.diagnostics>
When using this config file every activity the caller performs against the service and each corresponding message that's sent to the service will be logged in the svclog file. Everything fine so far.
If I modify the 3rd line from the above listing to <source name="System.ServiceModel" switchValue="Warning" propagateActivity="true"> (the ActivityTracing is removed) then only those activities are logged that are at least labeled level warning. But it's still every message logged...
So is there a way to only log those message that correspond to those activities that are at least warnings? Those messages that succeeded aren't very interesting in that moment, but those messages that belong to the unsuccessful activities are!
Edit
To filter messages beyond the options below you may want to look into writing your own TraceSource.
Below is one I am using for a project. You could easily customize it to filter out the messages you want or perhaps hide activity if it is not in DEBUG, etc.
class DB : TraceSource
{
public DB(string name) : base(name)
{
}
public DB(string name, SourceLevels sourceLevels) : base (name, sourceLevels)
{
}
public void Log(object value)
{
WriteLine(value);
}
public void Error(object value)
{
WriteLine(value, TraceEventType.Error);
}
public void Error(RecordingResponseData errorResponse)
{
string errorMessage = "[Error] Code: "+errorResponse.ErrorCode +" Message: "+errorResponse.ErrorMessage;
WriteLine(errorMessage, TraceEventType.Error);
}
public void Warn(object value)
{
WriteLine(value, TraceEventType.Warning);
}
public void WriteLine(object value, TraceEventType type = TraceEventType.Information)
{
TraceEvent(type, 0, value.ToString());
}
}
Original
Your options are:
Critical
Error
Warning
Information
ActivityTracing
Verbose
All
Or a combination there of. If you have it set to Warning but are still getting too many messages then you may want to try Error or Critical.
ref: https://msdn.microsoft.com/en-us/library/ms733025%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
There's a switchValue available for the System.ServiceModel.MessageLogging trace switch as well. Just add that attribe to that source element and set it to Warning also and you will only see messages logged that are related to warnings.
Get rid of System.ServiceModel.MessageLogging source to get rid of logging messages to resolve "still every message logged".

SOAP message is empty when catching MessageLoggingTraceRecords with CustomTraceListener

I want to write CustomTraceListener which writes all data to SQL Server DB.
Here's the stub for it:
public class SqlTraceListener : TraceListener
{
public SqlTraceListener()
: base()
{ }
public SqlTraceListener(String name)
: base(name)
{ }
protected override string[] GetSupportedAttributes()
{
List<string> attributes = new List<string>();
attributes.Add("connectionString");
attributes.Add("actionFilter");
attributes.Add("hostFilter");
return base.GetSupportedAttributes();
}
public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
{ }//Other empty methods...
}
In overridden method TraceData I want to catch SOAP messages sent to my WCF service. But when I check what is in "data" parameter I get this: (sorry for posting xml as pictures - it seems SO editor doesn't allow some xml keywords in posts):
But according to standard XmlWriterTraceListener I should get this:
How to configure TraceListener not to eliminate SOAP messages?
My config is here:
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="xml"/>
<add name="sql"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add initializeData="C:\logs\StockPriceService.svclog" type="System.Diagnostics.XmlWriterTraceListener" name="xml"/>
<add type="SqlTraceListener.SqlTraceListener, SqlTraceListener" name="sql"/>
</sharedListeners>
<trace autoflush="true"/>
Is there any reason you're not using the out of the box database trace listener? See: Enterprise Library Database Trace Listener?.

WCF slow ServiceHost.Open() call

This is a similar question as this one:
Win32Exception # ServiceHost.Open() for WCF service.
I have a machine that is very slow on the ServiceHost.Open call below. It consistently takes 7 seconds or so to open the service, every time. This machine is my home box, and it is not part of a domain.
I can run the same code on another box (my work box) which is part of a domain and the service host opens in about 3-4 seconds on the first call, but if I run the program again the service host opens in about 1 second or less.
I have worked with MS support on this, and we generated trace logs and the part it's hanging in is where is goes out and tries to connect to a domain, even on the machine that isn't part of a domain. And it gets the "The specified domain either does not exist or could not be contacted." exception in the trace log, and that's where all the time is getting eaten up.
But what's really weird is that even on my work machine, if I'm not connected to a domain (like if I'm not on my work network and just running from home) I still don't get the delay.
I rebuilt my machine using Windows 7 64-bit, and the same behavior occurs (was running XP SP3, which I restored when Windows 7 didn't seem to fix the problem).
I just wondered if anyone had any ideas of what could cause this. By the way, if I disable "Client for microsoft networks", the time is like 4 seconds to open the ServiceHost, but that's still not as fast as this machine used to be able to open the service. Somehow, it thinks it's supposed to be part of a domain or something.
static void RunServiceWithinEXE()
{
Uri baseAddress = new Uri("http://localhost:11111/Demo");
ServiceHost serviceHost = new ServiceHost(typeof(CalculatorService), baseAddress);
try
{
// Add a service endpoint
serviceHost.AddServiceEndpoint(
typeof(ICalculator),
new WSHttpBinding(),
"CalculatorService");
// Enable metadata exchange
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
serviceHost.Description.Behaviors.Add(smb);
serviceHost.Opening += new EventHandler(serviceHost_Opening);
serviceHost.Opened += new EventHandler(serviceHost_Opened);
serviceHost.Open();
Console.WriteLine("The service is ready.");
// Close the ServiceHostBase to shutdown the service.
serviceHost.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occured: {0}", ce.Message);
serviceHost.Abort();
}
}
static void serviceHost_Opened(object sender, EventArgs e)
{
TimeSpan timeToOpen = DateTime.Now - shOpening;
Console.WriteLine("Time To Open: :" + timeToOpen.Seconds);
}
static void serviceHost_Opening(object sender, EventArgs e)
{
shOpening = DateTime.Now;
}
Here is my app.config, but I don't have any special security configuration settings for the service in there, only some diagnostic settings to enable the WCF trace.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<diagnostics>
<messageLogging maxMessagesToLog="30000"
logEntireMessage="true"
logMessagesAtServiceLevel="false"
logMalformedMessages="true"
logMessagesAtTransportLevel="true">
<filters>
<clear/>
</filters>
</messageLogging>
</diagnostics>
</system.serviceModel>
<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="Warning, ActivityTracing" propagateActivity="true" >
<listeners>
<add name="xml" />
</listeners>
</source>
<source name="System.ServiceModel.MessageLogging" switchValue="Warning">
<listeners>
<add name="xml" />
</listeners>
</source>
</sources>
<sharedListeners>
<add name="xml" type="System.Diagnostics.XmlWriterTraceListener" initializeData="C:\Temp\Server.svclog" />
</sharedListeners>
<trace autoflush="true" indentsize="4">
<listeners>
<remove name="Default" />
<add name="ScottsConsoleListener" type="System.Diagnostics.ConsoleTraceListener" />
<add name="ScottsTextListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="C:\Temp\DebugLog.txt" />
</listeners>
</trace>
</system.diagnostics>
</configuration>
Note also that my service definition has SessionMode required (see below). If I take the SessionMode requirement out, I don't get the delays.
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace Microsoft.ServiceModel.Samples
{
// Define a service contract.
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode = SessionMode.Required)]
public interface ICalculator
{
[OperationContract]
double Add(double n1, double n2);
[OperationContract]
double Subtract(double n1, double n2);
[OperationContract]
double Multiply(double n1, double n2);
[OperationContract]
double Divide(double n1, double n2);
[OperationContract]
string PrintName(string firstName, string lastName);
[OperationContract]
Point MakePoint(double x, double y);
}
}
Okay, my suspicion is the fact you're using a binding (WsHttpBinding) which defaults to authenticating its callers using Windows credentials unless you specifically tell it not to.
In your service hosting code, you're just instantiating a default WsHttpBinding instance - no settings in config or code to change the default security behavior, in that case.
Just for testing purposes: try to change your service hosting code to:
// Add a service endpoint
serviceHost.AddServiceEndpoint(
typeof(ICalculator),
new WSHttpBinding(SecurityMode.None), // pass in SecurityMode.None
"CalculatorService");
This will effectively cancel out all security settings, e.g. the ServiceHost should no longer even attempt to find the Windows domain to authenticate callers against.
Does that change any of your observations?
It turns out that if I disable netbios on my network connections, I don't get the delays on the ServiceHost.Open calls. I am not sure why, but this seemed to fix the problem.
What's weird is that when I did a clean install of XP, I didn't have the delays even with the Netbios enabled, so I have no idea why it happens with my existing installation (I did the clean install on this same machine, and then restored my backup from an image to run these tests).
Also try stopping the Remote Access Auto Connection Manager service. For me, the issue came down to Dns.GetHostEntry(String.Empty) and this post http://social.msdn.microsoft.com/Forums/en/vbgeneral/thread/493d1b65-9ace-41de-b269-f178d27a8a1b
It seems I had the same Problem. Long Duration of Open call caused by an exception. I googled for configuration of security Settings for Wcf Service. Found the following solution which worked for me:
Under <wsHttpBinding> element in the Web.config file place the following entry:
<security mode="None" />
The Service Reference in the Client must be updated!