Controlling .NET System.Net Tracing via the Registry
Posted by Ben G on June 29, 2009
We have a Click Once-deployed project that I wanted to set up for instrumentation and tracing. Because the project deploys to a rather obscure folder defined by Click Once, it makes it a very difficult thing to have our support personnel troubleshoot issues in the field by applying a modified app.config file that enables tracing.
I set up our internal trace sources programmatically to read the switch value from the registry, which worked fine. However, when it came to configuring the System.Net trace sources, I ran into problems. After a lot of searching it appears impossible to gain access to those system trace sources without using some shady reflection techniques which I’m not comfortable with in a production environment. Without access to those TraceSource objects, it is impossible to programmitically add my own switches and listeners.
My eventual solution was to configure the System.Net trace sources in the app.config file, but use a custom switch that reads its value from the registry. It took me a bit to figure out how to do this, so I’m reposting it here:
My custom switch looks like this:
Public Class RegistrySwitch
Inherits SourceSwitch
Public Sub New(ByVal name As String)
MyBase.New(name)
Me.Value = GetSwitchValue()
End Sub
Public Sub New(ByVal name As String, ByVal value As String)
MyBase.New(name)
Me.Value = GetSwitchValue()
End Sub
Private Shared Function GetSwitchValue()
Dim switchValue As String = GetRegistryValue("TracingLevel", "Off")
Select Case switchValue
Case "True", "1", "-1", "On"
switchValue = "All"
Case "0", "False"
switchValue = "Off"
End Select
Return switchValue
End Function
End Class
The GetRegistryValue function (code not shown) simply reads the specified value from our product’s key in the registry.
Then my app.config file looks like this:
<system.diagnostics>
<trace autoflush="true" />
<sources>
<source name="System.Net" tracemode="includehex" maxdatasize="1024" switchName="NetworkSwitch" switchType="MyProject.RegistrySwitch, MyProject">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Sockets" switchName="NetworkSwitch" switchType="MyProject.RegistrySwitch, MyProject">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Cache" switchName="NetworkSwitch" switchType="MyProject.RegistrySwitch, MyProject">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
</sources>
<switches>
<add name="NetworkSwitch" value="Off"/>
</switches>
<sharedListeners>
<add name="System.Net" type="MyProject.MyTextWriterTraceListener, MyProject" initializeData="network.log" />
</sharedListeners>
</system.diagnostics>
Notice that I had to define the CLR type of my switch not in the <Switches> node where the switch is named, but in the switchType parameter on each <Source> that references the switch. Also note that even though the Value of the switch is set to Off in this configuration file, that value is overwritten when my RegistrySwitch subclass initializes with the value from the registry.
Also note that in this example, my listener is a custom type. It is a TextWriterTraceListener that places the log file in a valid user data path.