How to use trace sources in application code? - c#

I have successfully configured logging in the App.config file of an application. The relevant parts are as follows.
<system.diagnostics>
<sources>
<source name="System.Net.Http">
<listeners>
<add name="PushTraceListener" />
</listeners>
</source>
</sources>
<switches>
<add name="System.Net.Http" value="Verbose"/>
</switches>
<sharedListeners>
<add name="PushTraceListener"
type="PushCore.Logging.LoggingTraceListener, PushCore" />
</sharedListeners>
</system.diagnostics>
<system.serviceModel>
<diagnostics>
<messageLogging logEntireMessage="true"
logMalformedMessages="false"
logMessagesAtServiceLevel="true"
logMessagesAtTransportLevel="true"
maxMessagesToLog="3000"
maxSizeOfMessageToLog="2000"/>
</diagnostics>
</system.serviceModel>
While this works fine, I seem unable to do the same in the application code, which I tried as follows.
var LoggingTraceListener = new LoggingTraceListener
{ Name = "PushTraceListener", Filter = new LoggingTraceFilter() };
Trace.AutoFlush = true;
var TraceSourceNames = new string[] { "System.Net.Http" };
foreach (var TraceSourceName in TraceSourceNames)
{
var TraceSource = new TraceSource(TraceSourceName, SourceLevels.Verbose);
TraceSource.Switch = new SourceSwitch(TraceSourceName, "Verbose");
TraceSource.Listeners.Add(LoggingTraceListener);
}
Trace.Listeners.Add(LoggingTraceListener);
The TraceListener itself seems to behave as expected, as it recieves messages written via Trace, but apparently not from the instantiated TraceSource instance. I somehow believe that instantiation of the TraceSource is not desired here, but instead an existing trace source would have to be used. However, I don't know how to do that.
Is it possible to achieve loggig as desired in the first place? If so, how?
A justified question would be why I would like to do access in code instead of using App.config if the configuration works fine. The reason is that App.config can only be used for an actual application but not for a Windows service.
Any suggestions?

Related

Using EventLogTraceListener inside a library DLL

I've written a library that gets called by several applications all residing in the same folder. Currently it's using log4net with the log4net.config file for event logging which works well. But log4net is now a dormant project with no foreseeable updates, and it appears that EventLogTraceListener can accomplish much the same thing without relying on a 3rd party library. However, I'm not sure there's a way to use an app.config file (out of the box) with a DLL. For example, the app.config file below works in a console app (MyApp.exe.config) but won't work when only using my DLL (MyLibrary.dll.config):
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
</startup>
<system.diagnostics>
<trace autoflush="true" indentsize="0">
<listeners>
<add name="MyListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="MyListener.log"/>
<add name="MyEventListener" type="System.Diagnostics.EventLogTraceListener" initializeData="MyLog"/>
</listeners>
</trace>
<switches>
<add name="MySwitch" value="Verbose"/>
</switches>
</system.diagnostics>
</configuration>
From doing some research it appears that config files for DLLs aren't really viable since the calling app's config file is normally used. But I don't want to have to edit the config file of multiple applications to change logging level, etc. I realize I could roll my own but it's nice that .NET has all this functionality built-in. Just need to know if there's a way to make it work using a DLL.config file?
var log = Trace.Listeners.OfType<EventLogTraceListener>().First(); // Works for an app but not a DLL (no listener)
log.TraceEvent(new TraceEventCache(), "Delme1", TraceEventType.Verbose, 123, "Testing");
UPDATE #1
Based on the answers here it doesn't look like this is supported by design. There also doesn't appear to be any user available objects for manipulating the systems.diagnostic entries. I think I'm going to just create my own XML file for storing the info I need.
UPDATE #2
Per Microsoft's recommendation, I switched to using the TraceSource class and updated my config file to match. I found a way to dynamically load the file from a DLL but that doesn't appear to update the TraceSource class settings, even after issuing a Trace.Refresh(). Everything works when I create a config file with the same settings at the application level, but that's not what I want. Is there any way to load a config file as shown below and also have its settings update the TraceSource class with Listeners, etc.? I find it curious that SystemDiagnosticsSection is marked internal and not accessible to user apps. Seems Microsoft has made this nearly(?) impossible (but I'm so close!)
Config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<sources>
<source name="MyExceptionLibrary" switchName="MySwitch" switchType="System.Diagnostics.SourceSwitch">
<listeners>
<remove name="Default"/>
<add name="MyListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="Logger.log">
<filter type="System.Diagnostics.EventTypeFilter" initializeData="Off"/>
</add>
<add name="MyEventListener" type="System.Diagnostics.EventLogTraceListener" initializeData="Blah">
<filter type="System.Diagnostics.EventTypeFilter" initializeData="Verbose"/>
</add>
</listeners>
</source>
</sources>
<switches>
<add name="MySwitch" value="Verbose"/>
</switches>
</system.diagnostics>
</configuration>
Code:
public static class Logger
{
private static readonly TraceSource s_traceSource = new TraceSource("MyExceptionLibrary");
static Logger()
{
var config = ConfigurationManager.OpenExeConfiguration("MyExceptionLibrary.dll"); // Confirmed config loaded
ConfigurationManager.RefreshSection("system.diagnostics"); // Doesn't seem to make any difference
Debug.WriteLine($"Config file: {config.FilePath}"); // Points to new config file
var section = config.GetSection("system.diagnostics"); // Contains data from file
Debug.Write(section.SectionInformation.Type); // SystemDiagnosticSection
Trace.Refresh(); // Doesn't seem to make any difference
Debug.WriteLine(s_traceSource.Listeners[0].Name); // Still shows Default
s_traceSource.TraceEvent(TraceEventType.Verbose, 123, "Testing"); // No event log entry is created
}
}

WCF Diagnostics Message Logging With Unique Id

I'm logging messages for WCF web service using System.Diagnostics System.ServiceModel.MessageLogging with configuration below. And I extended
TraceListener class as:
public class FormattedTraceListener : TraceListener
{
static readonly Logger logger = new Logger();
public FormattedTraceListener()
: base(string.Empty)
{
}
public override void TraceData(TraceEventCache eventCache,String source,TraceEventType eventType,**Int32 id,**Object data)
{
//....
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging" >
<listeners>
<add name="pretty" />
<remove name="Default"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add name="pretty" lockItem="true" type="LoggingTest.FormattedXmlWriterTraceListener,LoggingTest" />
</sharedListeners>
<trace autoflush="true" indentsize="4">
</trace>
</system.diagnostics>
....
<diagnostics >
<messageLogging
logEntireMessage="true"
logMalformedMessages="false"
logMessagesAtServiceLevel="true"
logMessagesAtTransportLevel="false"
maxMessagesToLog="-1"
maxSizeOfMessageToLog="134217728">
<filters >
<add xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">/s:Envelope</add>
<add xmlns:s="http://www.w3.org/2003/05/soap-envelope">/s:Envelope</add>
</filters>
</messageLogging>
<endToEndTracing propagateActivity="true" activityTracing="true" messageFlowTracing="true" />
</diagnostics>
</system.serviceModel>
TraceData method fired twice (for request and response) for each service call.
However, when method is hit, the id parameter is always 0.
I need a unique id in logging text to match request and response log. How can I specify that?
Your approach is not right. The TraceData(int id) field is not a unique id for the WCF request. It's the application defined code for the event type.
Just use the standard WCF trace classes and view correlated traces. If I understand what you are trying to accomplish in matching up request to response, the default behavior already meets your requirements.

Can I turn off tracing for a single assembly that my code references?

I have a certain framework of code, and I have a TraceListener defined for two reasons:
Back-compatibility with a lot of the old logging that was done via Trace.Write until we update it, and
It's nice to be able to instrument the other assemblies our code references if we need to.
However, I have one assembly (not ours) that logs a lot of pointless data that doesn't help us debug anything. How can I turn off tracing for this one assembly (or, alternately, the facade project we built around it), while leaving it on for the rest of the application?
I've tried various flavors of configuration in our facade project, usually looking like the following, to no avail. I've tried adding <remove> elements that match the <add> elements which setup the logging in the first place, tried <clear>ing them, setting <trace enabled="false"> and at least three other attempts. Thanks for any help you can provide!
<system.diagnostics>
<trace autoflush="true" indentsize="4">
<listeners>
<clear/>
</listeners>
</trace>
<switches>
</switches>
</system.diagnostics>
You can write your own trace filter, for use with your TraceListener. Inside this filter you can look for your assembly in stackTrace and turn off event tracing.
In my case I wrote filter (see: DotNetOpenAuthFilter) based on EventTypeFilter, which filters events only from the DotNetOpenAuth library.
Then connect the filter to the listener in the web.config:
<configuration>
<system.diagnostics>
<trace>
<listeners>
<add name="console" type="System.Diagnostics.ConsoleTraceListener" >
<filter type="Common.Log.DotNetOpenAuthFilter, Common" initializeData="Warning" />
</add>
</listeners>
</trace>
</system.diagnostics>
</configuration>
Use TraceSource.
Initialize it in your trace source.
TraceSource logger = new TraceSource("Class1");
Call it from critical points in code:
logger.TraceInformation("Hello from Class1");
Be sure to edit your application configuration:
<system.diagnostics>
<trace autoflush="true"/>
<sources>
<source name="Class1" switchName="Class1Switch" switchType="System.Diagnostics.SourceSwitch">
<listeners>
<add name="console"></add>
<add name="csv" />
<!-- or you can add your own listener here -->
</listeners>
</source>
</sources>
<switches>
<add name="Class1Switch" value="Information" />
</switches>
<sharedListeners>
<add name="console" type="System.Diagnostics.ConsoleTraceListener" />
<add name="csv" type="System.Diagnostics.DelimitedListTraceListener"
delimiter="|" initializeData="d:\data\tracing\trace.log"
traceOutputOptions="Timestamp, ThreadId, LogicalOperationStack, DateTime, ProcessId">
</add>
</sharedListeners>
</system.diagnostics>
If say, you want to only log errors, change the switch:
<add name="Class1Switch" value="Error" />
To switch it completely off:
<add name="Class1Switch" value="Off" />

WCF Service fails to start when adding trace logging config

I have a working WCF service that I wanted to hook into so I can look at the logs. So, I added a .config file, and now my service will not start at all.
In my code I do the following:
private ServiceHost _myHost;
....
_myHost= new ServiceHost(typeof (MyType), new Uri(baseUri));
_myHost.AddServiceEndpoint(typeof (myInterface), new NetNamedPipeBinding(), hostName);
_myHost.Open();
And, as mentioned, this works when I do not have a .config file. However, to view the logs I have to add a .config file that looks like this:
<system.diagnostics>
<sources>
<source propagateActivity="true" name="System.ServiceModel" switchValue="Verbose,ActivityTracing">
<listeners>
<add type="System.Diagnostics.DefaultTraceListener" name="Default">
<filter type="" />
</add>
<add initializeData="c:\logs\Traces.svclog" type="System.Diagnostics.XmlWriterTraceListener"
name="traceListener" traceOutputOptions="LogicalOperationStack, DateTime, Timestamp, ProcessId, ThreadId, Callstack">
<filter type="" />
</add>
</listeners>
</source>
</sources>
</system.diagnostics>
<system.serviceModel>
<diagnostics>
<messageLogging logMalformedMessages="true" logMessagesAtServiceLevel="true"
logMessagesAtTransportLevel="true" />
<endToEndTracing activityTracing="true" messageFlowTracing="true" />
</diagnostics>
</system.serviceModel>
The error I now get in the logger is Failed to open System.ServiceModel.ServiceHost and it occurs at the instance creation of my ServiceHost. So, why does adding a logging config make any difference?
UPDATE
The logger is the ServiceTraceViewer, that is where I am seeing the error once I throw in the config. So, the logging is working, it is just breaking the functionality of the code. I read somewhere that if I use a .config that I MUST have an endpoint in the config. Is that true, and if so, is there a workaround to this since I am doing that via code

WCF Tracing From Code

I have all of my connections set up from my code, as opposed to using my config file. How does one go about setting up WCF Tracing on a connection built from code. I tried adding the tracing to the config file, as explained here, but it produces no logs whatsoever.
I need to know either how to make it work from the config file for connections set up in code, or how to configure it manually in code if anyone has any info. Thanks!
EDIT: To add a little more information:
The application is a C# Console application, and my binding is declared as:
private Binding getBinding()
{
NetTcpBinding tcp = new NetTcpBinding();
tcp.ReaderQuotas.MaxArrayLength = 65535;
tcp.TransferMode = TransferMode.Streamed;
tcp.ReaderQuotas.MaxArrayLength = int.MaxValue;
tcp.ReaderQuotas.MaxDepth = int.MaxValue;
tcp.ReaderQuotas.MaxStringContentLength = int.MaxValue;
tcp.ReaderQuotas.MaxBytesPerRead = int.MaxValue;
tcp.ReaderQuotas.MaxNameTableCharCount = int.MaxValue;
tcp.MaxReceivedMessageSize = int.MaxValue;
return tcp;
}
And I then add services to my app using a generic function:
private List<ServiceHost> m_Hosts = new List<ServiceHost>();
private static List<string> m_Services = new List<string>();
public void AddHost<T1, T2>(string uri)
where T1 : class
where T2 : class
{
m_Services.Add("net.tcp://<ipaddress>:<port>/" + uri);
m_Hosts.Add(new ServiceHost(typeof(T1)));
m_Hosts[m_Hosts.Count - 1].AddServiceEndpoint(typeof(T2), getBinding(), m_Services[m_Services.Count - 1]);
}
There's obviously a little more code to make this all work, but this should give any relevant parts.
The following is an .config example to enable tracing, if you want to give it another attempt. Make sure the .config file is located in the same folder of your WCF service host.
<configuration>
<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="Warning" propagateActivity="true" >
<listeners>
<add name="xml"/>
</listeners>
</source>
<source name="myUserTraceSource" switchValue="Warning, ActivityTracing">
<listeners>
<add name="xml"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add name="xml"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="TraceLog.svclog" />
</sharedListeners>
</system.diagnostics>
</configuration>
Microsoft provides a Service Trace Viewer Tool to read .svclog files.
Make sure the path where you will be saving the .svclog has the necessary write permissions.
Just for the records here is how to change the log file name by code
http://geekswithblogs.net/FlippinIT/archive/2009/11/12/wcf-tracing-in-code.aspx

Categories