When I start a new project for a small tool I sprinkle throughout my code some Console.WriteLine calls to see what is going on and to report errors. I am quite sure that 99% of all developers work this way except for the TDD elite which do start with a MEF container and injectable tracer and logger interfaces right from the beginning. When the tool proves to be useful it is extended a little bit here and there and other people start using it. Perhaps it is used in a script or it is put in an Run key to be started every time the user logs on or it does get a nice WPF UI…
Errors do still happen but the Console output goes nowhere now. If you e.g. switch from a Console Application project type to a Windows Application in the Visual Studio project settings there is no hidden magic going on. The project type is a simple tag in the PE file which does nothing for a console app and for a Windows application it detaches upon start from the console (if any) it was started from to free the command prompt for the next application you want to start. When your users report problems with your tool you have just lost your console output you did rely on. At this point you tell the users to switch on tracing and to log the data to a file with the hidden –debug switch your application surely has. But for this to happen you need to take an extra library dependency or to copy quite a bit of code into your still small project and you need to exchange all Console.WriteLine calls with calls to your tracing library. That is overkill for many small tools which are useful because they to spare enough time to justify the time spent on coding them.
Before you throw out your Console.WriteLine calls to get your diagnostics abilities back you can sidestep the problem by redirecting the Console.Out TextWriter instance to your own intercepting TextWriter instance. You can still print to the console but you can now easily write the output to a secondary (or even tertiary) location. It can be a log file or an ETW TraceListener or the diagnostics window in your WPF application. The following example shows how to write your console output to ETW with jus a few additional lines of code.
using Microsoft.Diagnostics.Tracing; using System; using System.IO; using System.Text; using System.Threading;
namespace ConsoleRedirector { class Program { static void Main(string[] args) { // Simply wrap your Main method with this using statement and all output goes to // ETW, DebugOutput or a log file without the need to touch or change existing Console.WriteLine calls using (new ForwardWriter(WriteToETW, WriteToETW)) { for (int i = 0; i < 1000; i++) { Console.WriteLine("Writing event {0}", i); Thread.Sleep(1000); } } }
static void WriteToETW(string line)
{
ConsoleEventSource.Log.Write(line);
}
/// <summary>
/// Forward Console.Write and Console.WriteLine calls to your handler to redirect them to alternate locations
/// </summary>
class ForwardWriter : TextWriter
{
/// <summary>
/// Original TextWriter from Console.Out
/// </summary>
TextWriter Orig;
/// <summary>
/// Redirect Console.Out with this instance and forward Console output to passed handlers.
/// </summary>
/// <param name="onWrite">Receives strings from Console.Write</param>
/// <param name="onWriteLine">Receives strings from Console.WriteLine</param>
public ForwardWriter(Action<string\> onWrite, Action<string\> onWriteLine)
{
Orig = Console.Out;
Console.SetOut(this);
if (onWrite != null)
{
OnWrite += onWrite;
}
if (onWriteLine != null)
{
OnWriteLine += onWriteLine;
}
}
/// <summary>
/// Create a new forwarder. To receive the output you need to subscribe to the events OnWrite and OnWriteLine
/// </summary>
public ForwardWriter()
: this(null, null)
{
}
/// <summary>
/// Is fired when Console.Write call happened
/// </summary>
public event Action<string\> OnWrite = (str) => { };
/// <summary>
/// Is fired when Console.WriteLine call happened
/// </summary>
public event Action<string\> OnWriteLine = (str) => { };
public override Encoding Encoding
{
get { throw new NotImplementedException(); }
}
/// <summary>
/// All overloads of Console.WriteLine call finally this overload
/// </summary>
/// <param name="value"></param>
public override void WriteLine(string value)
{
Orig.WriteLine(value);
OnWriteLine(value);
}
/// <summary>
/// All overloads of Console.Wrie call finally this overload
/// </summary>
/// <param name="value"></param>
public override void Write(string value)
{
Orig.Write(value);
OnWrite(value);
}
}
/// <summary>
/// ETW event provider for redirected Console messages
/// </summary>
\[EventSource(Name = "ConsoleLog", Guid="{**7B25937F-14D7-4BFF-BD7D-2A0C00ABA1EE**}")\]
sealed class ConsoleEventSource : EventSource
{
public static ConsoleEventSource Log = new ConsoleEventSource();
/// <summary>
/// Write a console message
/// </summary>
/// <param name="Message"></param>
public void Write(string Message)
{
WriteEvent(1, Message);
}
private ConsoleEventSource():base(true)
{ }
}
}
}
Now you can leave your precious Console.WriteLine calls in your code and you still can get full diagnostic by forwarding your output to ETW or a simple log file by adding a simple using statement to your Main method. This is of course not perfect but for small tools it is often enough to get the job done. The ETW writer works not only with .NET 4.5 but you can also Target .NET 4.0 apps if you get the Microsoft.Diagnostics.Tracing.EventSource package via Nuget (be sure to check beta packages) which does target .NET 4.5 but it works with .NET 4.0 as well. The nice thing about ETW is that you can enable it for an already running application and you get the full output without any registration steps. The ETW Provider introduced with .NET 4.5 creates from the declaring class via reflection. With the Windows Performance Toolkit you can capture with WPR by creating your own profile to capture your ETW events with a wprp file like
[](/images/akraus1/153804/c4b61845-image_2.png) As you can see the provider name is derived from the EventSource Name attribute, the Task Name is the called method and the Message field is the parameter name passed to to the Write method. This magic is only possible because this ETW provider creates an ETW manifest file via reflection from your class, method and method parameters which is written an an extra ETW event to the etw event stream which is later parsed by WPA to make this nice visualization possible. As added bonus you do not need to put the process and thread id to your messages anymore because the ETW infrastructure will capture it for you. You can create from your class also a “classic” ETW manifest file which you can register with wevtutil –im xxx.man as usual. You can get the manifest string via EventSource.GenerateManifest(typeof(ConsoleEventSource), Assembly.GetExecutingAssembly().Location) from where you can save the returned string to a file and register it. The beta NuGet package seems to have problems because it did not work and did throw a NullReferenceException inside it. But the .NET 4.5 EventSource class in mscorlib works as expected. For the simple ETW provider I get this manifest: **_ConsoleTraces.wprp_**