views:

493

answers:

2

OK, here's a good one (I think) - I'm working on an application with lots (far too many) dependency dlls, created by a team of developers. I'm trying to debug just one assembly, but the console output is 'polluted' by the Console.WriteLines and Debug.WriteLines left scattered around by my colleagues.

Is there anyway I can work out exactly which assembly a given line is coming from, so I can get the author to clean up their source?

UPDATE If you're also experiencing this kind of issue, note that there is another potential source of output messages which is any breakpoints with 'When hit' set to print a message. Having said which, this is a VERY cool feature, which can prevent the kind of problems I was having above.

+7  A: 

Yes - replace Console.Out. Use Console.SetOut after creating a TextWriter which not only dumps the requested data to the original console, but also dumps a stack trace (and timestamp, and the requested data) to a file.

Here's some code, adapted from Benjol's answer:

(Note: you will want to adapt this code depending on whether you want a stack trace after each write, or after each writeline. In the code below, each char is followed by a stack trace!)

using System.Diagnostics;
using System.IO;
using System.Text;

public sealed class StackTracingWriter : TextWriter
{
    private readonly TextWriter writer;

    public StackTracingWriter (string path)
    {
        writer = new StreamWriter(path) { AutoFlush = true };
    }

    public override System.Text.Encoding Encoding
    {
        get { return Encoding.UTF8; }
    }

    public override void Write(string value)
    {
        string trace = (new StackTrace(true)).ToString();
        writer.Write(value + " - " + trace);
    }

    public override void Write(char[] buffer, int index, int count)
    {
        Write(new string(buffer, index, count));
    }

    public override void Write(char value)
    {
        // Note that this will create a stack trace for each character!
        Write(value.ToString());
    }

    public override void WriteLine()
    {
        // This is almost always going to be called in conjunction with
        // real text, so don't bother writing a stack trace
        writer.WriteLine();
    }

    protected override void Dispose(bool disposing)
    {
        writer.Dispose();
    }
}

To use this for logging both Console.WriteLine and Debug.WriteLine to a file, make calls like this as early as possible in your code:

var writer = new StackTracingWriter(@"C:\Temp\ConsoleOut.txt");
Console.SetOut(writer);
Debug.Listeners.Add(new TextWriterTraceListener(writer));

Note that this currently doesn't also write to the original console. To do so, you'd need to have a second TextWriter (for the original console) in StackTracingWriter, and write to both places each time. Debug will however continue to be written to the original console.

Jon Skeet
Damn you, Jon Skeet... don't you sleep??
Josh Einstein
Just to be clear - this presupposes that I have access to the application code (which is the case?) or I could also do this from within the code of my assembly?
Benjol
You can do this from your own code - so long as your code is running in the same AppDomain or process (not sure which in this case). Just replace Console.Out as close to startup as you can, and it should intercept all the calls to write to the console. You might want to replace Console.Error as well.
Jon Skeet
@Josh: Not on my way to work, no :)
Jon Skeet
Sorry, further question - will this trap the Debug.WriteLines too?
Benjol
You should also encourage them to switch to the more robust TraceSource class. It's a little more complicated than Console.WriteLine but it gives you total control over the output.
Josh Einstein
@Jon: Ah, after I posted I realized you were from the land of UTC. I also realized that I myself am actually awake too. :)
Josh Einstein
@Josh, I heartily agree, for some reason they've opted for log4net, but only recently, and more for production logging than debugging. I've implemented my own 'SmartDebug' which at least allows for categories and filtering, but it hasn't caught on yet...
Benjol
@Jon, sorry, hope you're not at work yet - I'm stuck on 'creating a TextWriter which also dumps a stack trace' : which Writes should I override?
Benjol
I think Jon means you would have to subclass TextWriter and add code to it to intercept the writes and prepend the additional info.
Josh Einstein
Exactly as Josh says. I can't remember which methods you have to override, I'm afraid - but hopefully they'll be abstract anyway, which should make life easier. Can't test right now, I'm afraid. And I've no idea whether or not this will also grab Debug.WriteLine, but it's worth trying :)
Jon Skeet
Will incorporate, thanks.
Jon Skeet
Righto - will edit, and make my answer community wiki.
Jon Skeet
+6  A: 

Download Reflector and you can open up the mscorlib assembly, add your application's assemblies, then right click on the Console class and click Analyze and you can show all methods that reference the Console class.

Josh Einstein
Good idea, for the moment I'm going with Jon's idea - simply because loading 100+ assemblies into Reflector sounds too much like work.
Benjol
It does support drag and drop. :)
Josh Einstein
Yes, I guess I'll give it a shot too, I owe you that, at least. Must resist the temptation to just upvote Jon because He's Always Right (TM) :)
Benjol