JDE
JDE

Reputation: 294

C# enable/disable network tracing at runtime?

In the examples I can find the tracing is enabled via config file, for example

    <?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
  <system.diagnostics>
    <sources>
      <source name="System.Net" tracemode="includehex" maxdatasize="1024">
        <listeners>
          <add name="System.Net"/>
        </listeners>
      </source>
    </sources>
    <switches>
      <add name="System.Net" value="Verbose"/>
    </switches>
    <sharedListeners>
      <add name="System.Net"
        type="System.Diagnostics.TextWriterTraceListener"
        initializeData="network.log"
      />
    </sharedListeners>
    <trace autoflush="true" indentsize="4" />    
  </system.diagnostics>
</configuration>

But I don't want config file to be shipped with my dll. Moreover I need to be able to enable/disable tracing and change log name "on the fly" in my code. What I came up with:

FileStream stream = new FileStream("D:\\network1.log", FileMode.OpenOrCreate);
TextWriterTraceListener listener = new TextWriterTraceListener(stream);
Trace.Listeners.Add(listener);
Trace.AutoFlush = true;

TraceSwitch ts = new TraceSwitch("System.Net", ".Net");
ts.Level = TraceLevel.Verbose;

and it is not logging anything, and I don't know where to add the switch, and if that is correct at all. The listener works but it don't fetch any data.

I've been reading msdn and this blog post http://www.codeguru.com/csharp/.net/article.php/c19405/Tracing-in-NET-and-Implementing-Your-Own-Trace-Listeners.htm and from the way I understand it, once added to the Trace.Listeners a listener should log all thace info in the current exe, but that's obvioustly not the case.

So how to achieve that?

Edit: I've managed to hack around this limitation by dissasembling the TextWriterTraceListener and implementing my own TraceListener with the dissasembled code, adding if staatements in Write and WriteLine methods that check some static fields of another class, and adding my type in the config.

Upvotes: 4

Views: 2445

Answers (2)

Nathan Schubkegel
Nathan Schubkegel

Reputation: 905

For readers that are in .NET Core and beyond, you can achieve this by making a class that inherits from System.Diagnostics.Tracing.EventListener

See https://github.com/dotnet/runtime/issues/64977 for details + original suggestion.

Example:

private class SystemNetEventListener : EventListener
{
  protected override void OnEventSourceCreated(EventSource eventSource)
  {
    // take anything from System.Net
    if (eventSource.Name?.StartsWith("Private.InternalDiagnostics.System.Net.") == true)
    {
      EnableEvents(eventSource, EventLevel.LogAlways);
    }
  }

  protected override void OnEventWritten(EventWrittenEventArgs eventData)
  {
    try
    {
      // produce a string that can be logged
      var sb = new StringBuilder().Append($"{eventData.TimeStamp:HH:mm:ss.fffffff}[{eventData.EventName}] ");
      for (int i = 0; i < eventData.Payload?.Count; i++)
      {
        if (i > 0)
          sb.Append(", ");
        sb.Append(eventData.PayloadNames?[i]).Append(": ").Append(eventData.Payload[i]);
      }

      Console.WriteLine(sb.ToString().Trim());
    }
    catch
    {
      // on failure... well... we are the logger, so there's nobody to tell we failed
    }
  }
}

Usage:

var listener = new SystemNetEventListener(); // start logging
... // do HTTP/Websocket stuff
listener.Dispose(); // stop logging

Upvotes: 0

rene
rene

Reputation: 42434

You can't enable the tracing of System.Net from code, at least not in a supported way.

First let me point out that you mixed up a TraceSwitch and a TraceSource. The TraceSource is the one that gets used to trace messages to. The TraceSwitch only controls what gets logged.

When you instantiate a TraceSource it initializes itself by checking the <sources> collection of the system.diagnostics setting for a <source> with the same name. When there is one, the instance is build-up with the settings provided. Your program will then use that specific TraceSource instance to log its data by calling TraceEvent or any of the other methods.
If there is no config found, the TraceSource is configured with the DefaultTraceListener with the SwitchLevel set to Off.

What I described above is also done by the internal System.Net.Logging class in its InitializeLogging method. It creates its own instance of a TraceSource for System.Net that once created, either is configured as defined in the config file or with the Defaults. All the logging that is done by the classes in the System.Net namespace use that private/internal TraceSource.

There is no public interface that allows you to insert or intercept TraceSource construction so you can't control which listener to connect or set Switch levels on a shared/singleton like TraceSource. Nor is there an option to get to the internal System.Diagnostics.DiagnosticsConfiguration class instance.
I did venture a bit in System.Configuration.Internal but I gave-up on that.

If you do fancy reflection you can grab the internal static type System.Net.Logging and get a reference to the TraceSource from the static property Web. Once you have the reference, I imagine you can set the listeners and switchlevel. That is an internal implementation detail and might change without notice so I don't recommend this.

To get control over logging, create overloads for the classes in System.Net and add your own TraceSource instance and log info to that instance.

Upvotes: 7

Related Questions