System.Diagnostics Traces wildcard for source name - c#

I have my Logging set up to use a different TraceSource for each Class.
Is it possible to Configure a wildcard that writes events for all Sources?
<system.diagnostics>
<sources>
<source name ="wildcard" switchValue="Warning">
<listeners>
<add name="textlog" />
</listeners>
</source>
<source name="MySpecificClass" switchValue="All">
<listeners>
<add name="textlog" />
</listeners>
</source>
</sources>
<sharedListeners>
<add name="textlog"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="Log.log">
</add>
</sharedListeners>
<trace autoflush="true"/>
</system.diagnostics>

I'm not aware of a built in way to do that automatically. However, if you have a look at the TraceLogger in Castle's git repository, you can see that they have essentially wrapped and extended TraceSource to support "hierarichal" naming.
https://github.com/castleproject/Core/blob/master/src/Castle.Core/Core/Logging/TraceLogger.cs
I would copy the code here, but it might not be proper to just cut and paste there code into SO.
I can explain how the ideas presented in the class could work for your (without you having to use Castle)
In essence, in your client code (that wants to log stuff), you would create an instance of your "logger" (rather than TraceSource). As input to the logger, you would give, for example, the fully qualified class name. Inside the constructor, use the input name to try to resolve a TraceSource. If there is a TraceSource configured with that name, use that TraceSource to do the work. If not, trim off the rightmost part of the fully qualified name. Try to resolve a TraceSource with that name. If there is a TraceSource configured with that name, use it. And so on. If you don't find any TraceSources, then don't log anything from your "logger". You could add on the ability to recognize a TraceSource that has been configured with a wildcard name (""). If you never find a TraceSource using the name trimming technique and if there is a "" TraceSource, use the "*" TraceSource as the fallback.
So, you might have something like this:
class MyTraceSource
{
private TraceSource ts;
public MyTraceSource(string name)
{
ResolveTraceSource(name);
}
private void ResolveTraceSource(string name)
{
//Check for a configured TraceSource from most qualified name (as input) to least qualified ("").
//Assume name like this: Namespace1:Namespace2:Class
//Try to resolve:
// TraceSource("Namespace1.Namespace2.Class");
// TraceSource("Namespace1.Namespace2");
// TraceSource("Namespace1");
//If you still haven't found one, try to resolve
// TraceSource("*");
}
//Implement either TraceSource API, or whatever API you prefer for logging.
}
I have actually done something like this myself as part of a prototype (that we ended up not using) and it worked pretty well for mimicking the way that you can specify loggers in log4net and NLog.
Good luck!

Related

What is the correct usage of TraceSource across a solution?

I've not worked with the System.Diagnostics.TraceSource package before so I want to make sure I implement it correctly. In the past I've used NLog (before that log4net) where you configure the settings in a config file (or programmatically) and in each class you declare this:
private static Logger Logger = LogManager.GetCurrentClassLogger();
then statements like this as required:
Logger.Error("some message", exc);
But with TraceSource you create a new TraceSource, set its properties and assign your listener(s) and then write your messages to it.
Do I create a TraceSource instance for every class like NLog? Or do I have one per application that all classes use? And if the former, it seems like at least I should centralize the creation of the TraceSource into a factory or something so the code is simplified and I'm not having to setup the settings, listeners etc every time I need use it. What's the proper usage here?
Note: due to the nature of how this code will be deployed, I cannot have a config file, I have to do it all programmatically.
just use
class DracBlahBlah
{
private static readonly TraceSource = new TraceSource("deathtoexecutives", SourceLevel.Error);
}
and in the other classes
class werewolf
{
private static readonly TraceSource = new TraceSource("deathtoexecutives", SourceLevel.Error);
}
then assign the listeners using config
<system.diagnostics>
<sharedListeners>
<add name="p0rn" type="p0rn.Tracelistener, super.bad.crazytown" />
</sharedListeners>
<sources>
<source name="deathtoexecutives" switchValue="Verbose">
<listeners>
<add name="p0rn" />
</listeners>
</source>

Cannot get WPF binding error trace information to write to log file configured in code

I'm trying to debug what I believe is a WPF binding issue that is only happening on one machine in production -- I cannot repro on a developer machine. In order to do this, I've been trying to get the binding trace information to output to a log file. Following answers like this one, I've been able to get it to output to a hard-coded location by configuring it in App.config:
<system.diagnostics>
<sources>
<source name="System.Windows.Data" switchName="SourceSwitch" >
<listeners>
<add name="textListener" />
</listeners>
</source>
</sources>
<switches>
<add name="SourceSwitch" value="All" />
</switches>
<sharedListeners>
<add name="textListener"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="c:\BindingErrors.log" />
</sharedListeners>
<trace autoflush="true" indentsize="4"/>
</system.diagnostics>
This works fine on my machine where I have administrative rights to the c:\ drive. The problem is I want to write the log somewhere the user has rights to, e.g. their TEMP folder. So I want to do something like this, using the %TEMP% environmental variable:
initializeData="%TEMP%\BindingErrors.log"
This isn't working, though, and I guess it won't work -- see this answer; so, following the advice in that answer, I've attempted to configure the output via code instead of App.config. Here's what I've tried so far:
var listener = new
TextWriterTraceListener(Environment.ExpandEnvironmentVariables(
#"%TEMP%\BindingErrors.log"), "myListener");
Trace.Listeners.Add(listener);
Trace.WriteLine("foo"); // just to see if it works at all.
Trace.Flush();
But this only writes foo to the log file in the %TEMP% folder. It doesn't write the binding errors. I've tried to replicate what the App.config had, but there's no Sources collection, so when I instantiate a TraceSource, like this:
var source = new TraceSource("mySource", SourceLevels.Information);
I don't know what to do with it, and there's no Listeners collection to which I can add my listener instance.
MSDN doesn't seem to bring it all together for me, or I'm missing some critical details. Can someone please help me figure out what I'm doing wrong?
…there's no Listeners collection to which I can add my listener instance.
Actually, there is: PresentationTraceSources.DataBindingSource.Listeners
The property returns the TraceSource object that is used when data binding messages are output. You can add any listener to that source, such as a TextWriterTraceListener you've created in code-behind by expanding the %TEMP% environment variable and using that as the directory for your output file.
Note that the programmatic approach requires recompiling to change the output location, or the addition of some other configuration value that can be read at run-time. A different technique allows you to specify the entire configuration in the app.config file, by implementing a custom TraceListener that knows how to expand environment variables.
For example:
namespace TestSO39836570TraceListenerBindingErrors
{
class EnvironmentAwareTextWriterTraceListener : TextWriterTraceListener
{
public EnvironmentAwareTextWriterTraceListener(string path)
: base(Environment.ExpandEnvironmentVariables(path))
{ }
public EnvironmentAwareTextWriterTraceListener(string path, string name)
: base(Environment.ExpandEnvironmentVariables(path), name)
{ }
}
}
Then in the app.config file, you can specify the listener:
<system.diagnostics>
<sources>
<source name="System.Windows.Data" switchName="SourceSwitch">
<listeners>
<add name="textListener"/>
</listeners>
</source>
</sources>
<switches>
<add name="SourceSwitch" value="All"/>
</switches>
<sharedListeners>
<add name="textListener"
type="TestSO39836570TraceListenerBindingErrors.EnvironmentAwareTextWriterTraceListener, TestSO39836570TraceListenerBindingErrors"
initializeData="%temp%\BindingErrors.log"/>
</sharedListeners>
<trace autoflush="true" indentsize="4"/>
</system.diagnostics>
Note that when specifying a custom TraceListener type that is found in your own program assembly, you need to specify the assembly name in the type attribute, by following the fully-qualified type name with a comma and then the assembly name (in the example above, the type's namespace is identical to the assembly name, per the defaults for a project created in Visual Studio).
You may of course opt for shorter namespace and type names than the ones I've used here. :)

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.
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.

How to filter trace listened by event id?

I'm using next method to add a trace record:
TraceSource.TraceEvent(TraceEventType, Int32, String)
where Int32 represents event id.
So how to filter in TraceSwitch to listen only by specified event id? Ir this is impossible?
<system.diagnostics>
<sources>
<source name="MyTraceSource" switchName="sourceSwitch" switchType="System.Diagnostics.SourceSwitch>"
<listeners>
<add name="console" type="System.Diagnostics.ConsoleTraceListener" />
</listeners>
</source>
</sources>
<switches>
<add name="sourceSwitch" value="?" />
</switches>
</system.diagnostics>
It's possible but you need to write a custom TraceFilter and override the ShouldTrace method. The id is passed to it, but no out-of-the-box filter supports it.
Then, you can declare it like this in a .config file:
<source name="MyTraceSource" switchName="sourceSwitch" switchType="System.Diagnostics.SourceSwitch">
<listeners>
<add name="console" type="System.Diagnostics.ConsoleTraceListener">
<filter type="YourNamespace.YourFilter, YourAssembly, ..." />
</add>
</listeners>
</source>
You can try Ukadc.Diagnostics from codeplex. This project provides some useful extensions for System.Diagnostics. The coolest thing, in my opinion, that they provide is a token based system that can be used to define log/trace output format similar to what you can achieve with log4net and NLog. It is a configuration-only dependency. That is, if you code is already using TraceSources, you only have to put Ukadc.Diagnostics on your machine and have your app.config point to their TraceListeners, PropertyTokens, etc.
You still instrument your code using System.Diagnostics.TraceSource objects.
To your point, using Ukadc.Diagnostics you can filter based on most property tokens (including EventId).
Note that the token system can only be used (as far as I know) with the corresponding TraceListeners provided in Ukadc.Diagnostics (or any TraceListener that you write based on their base TraceListener class).
I have not used this project in production, but I have fooled around with it quite a bit and have been quite impressed. It works well and is easy to extend.

How do I configure base class libraries in my app.config file?

I've found a couple of snippets of information pertaining to app.config/web.config that hints at almost codeless configuration of BCL components directly through the app.config. However, given the amount of tags suggested by the intellisense within the app.config, it suggests that there is a huge amount of possibilities for this that I can't find any useful information for.
Is there any documentation that supports this particular area of configuration files? I can find plenty of information on storing/retrieving configuration information and a small amount regarding writing custom configuration sections which I'm familiar with, but I cannot find any information regarding configuring BCL components this way. Does anyone have any reference material for this?
One example I've come across is as follows:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<trace autoflush="true" indentsize="2">
<listeners>
<add name="Console"
type="System.Diagnostics.ConsoleTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
traceOutputOptions="Timestamp" />
</listeners>
</trace>
<switches>
<add name="Logging.Program.Listener" value="Error" />
</switches>
</system.diagnostics>
</configuration>
Which may be consumed using code in a similar fashion to this:
class Program
{
private static TextWriterTraceListener tw = new TextWriterTraceListener();
private static TraceSwitch ts = new TraceSwitch("Logging.Program.Listener", "Default Logging Level", "Off");
static void Main(string[] args)
{
Trace.Listeners.Add(tw);
try
{
throw (new EntryPointNotFoundException());
}
catch (EntryPointNotFoundException ex)
{
string TraceMessage = "Trace {0}: {1}";
Trace.WriteLineIf(ts.TraceError, String.Format(TraceMessage, TraceLevel.Error, "Error Level Message"));
Trace.WriteLineIf(ts.TraceWarning, String.Format(TraceMessage, TraceLevel.Warning, "Warning Level Message"));
Trace.WriteLineIf(ts.TraceInfo, String.Format(TraceMessage, TraceLevel.Info, "Info Level Message"));
Trace.WriteLineIf(ts.TraceVerbose, String.Format(TraceMessage, TraceLevel.Verbose, "Verbose Level Message"));
}
}
}
One useful resource is the machine-level configuration files. The actual files are bare-bones, but there are ".comments" files alongside them that give fairly detailed examples of what can be achieved. For example, take a look in
C:\Windows\Microsoft.NET\Framework\v2.0.50727\CONFIG\machine.config.comments
That will give you some idea of what's achievable. Anywhere where you see collection elements, as in the case of the <traceSwitches> and <traceListeners> elements, the individual <add> elements contained within may vary depending on what you are adding (i.e. the specific attributes on those <add> elements will vary depending on exactly what you're adding to the collection). For this, you'll need to consult specific areas of documentation, but searching for the <traceSwitches> element in MSDN ought to serve as a decent starting point there.
They're all configurable this way. That's why you're not finding anything.
Ok, maybe not all, but certainly most. If you want to know, Use Reflector to find all the derived classes of System.configuration.ConfigurationSection, etc.

Categories