94

In a C# console application, is there a smart way to have console output mirrored to a text file?

Currently I am just passing the same string to both Console.WriteLine and InstanceOfStreamWriter.WriteLine in a log method.

xyz
  • 27,223
  • 29
  • 105
  • 125

14 Answers14

121

This may be some kind of more work, but I would go the other way round.

Instantiate a TraceListener for the console and one for the log file; thereafter use Trace.Write statements in your code instead of Console.Write. It becomes easier afterwards to remove the log, or the console output, or to attach another logging mechanism.

static void Main(string[] args)
{
    Trace.Listeners.Clear();

    TextWriterTraceListener twtl = new TextWriterTraceListener(Path.Combine(Path.GetTempPath(), AppDomain.CurrentDomain.FriendlyName));
    twtl.Name = "TextLogger";
    twtl.TraceOutputOptions = TraceOptions.ThreadId | TraceOptions.DateTime;

    ConsoleTraceListener ctl = new ConsoleTraceListener(false);
    ctl.TraceOutputOptions = TraceOptions.DateTime;

    Trace.Listeners.Add(twtl);
    Trace.Listeners.Add(ctl);
    Trace.AutoFlush = true;

    Trace.WriteLine("The first line to be in the logfile and on the console.");
}

As far as I can recall, you can define the listeners in the application configuration making it possible to activate or deactivate the logging without touching the build.

Pooven
  • 1,744
  • 1
  • 25
  • 44
Oliver Friedrich
  • 9,018
  • 9
  • 42
  • 48
  • 4
    That's perfect - thanks. I was aware of Log4Net but it seems wrong to have to pull in a library for something like this. – xyz Jan 07 '09 at 15:17
  • 3
    I don't know why they don't make a bigger deal of Trace -- it seems to me like it should work well for production-scale logging, but everybody wants to tack on an extra library (like log4net) to do it. – Coderer Jan 07 '09 at 15:18
  • This is a one-way mirroring. I meant if you you have an interactive console and get some data from user and want to log everything in a file this solution doesn't work. Despite of this simple fact my question is closed. Here : http://stackoverflow.com/questions/3886895/how-to-log-console-screen-into-a-text-file-closed – Xaqron Oct 09 '10 at 21:00
  • 6
    I really liked this solution, so I made a quick blog about it with some minor cleanup and walkthrough of a few snags along the way. http://www.mcrook.com/2014/11/quick-and-easy-console-logging-trace.html Thanks for the great solution :) – Michael Crook Nov 04 '14 at 03:42
57

This is a simple class which subclasses TextWriter to allow redirection of the input to both a file and the console.

Use it like this

  using (var cc = new ConsoleCopy("mylogfile.txt"))
  {
    Console.WriteLine("testing 1-2-3");
    Console.WriteLine("testing 4-5-6");
    Console.ReadKey();
  }

Here is the class:

class ConsoleCopy : IDisposable
{

  FileStream fileStream;
  StreamWriter fileWriter;
  TextWriter doubleWriter;
  TextWriter oldOut;

  class DoubleWriter : TextWriter
  {

    TextWriter one;
    TextWriter two;

    public DoubleWriter(TextWriter one, TextWriter two)
    {
      this.one = one;
      this.two = two;
    }

    public override Encoding Encoding
    {
      get { return one.Encoding; }
    }

    public override void Flush()
    {
      one.Flush();
      two.Flush();
    }

    public override void Write(char value)
    {
      one.Write(value);
      two.Write(value);
    }

  }

  public ConsoleCopy(string path)
  {
    oldOut = Console.Out;

    try
    {
      fileStream = File.Create(path);

      fileWriter = new StreamWriter(fileStream);
      fileWriter.AutoFlush = true;

      doubleWriter = new DoubleWriter(fileWriter, oldOut);
    }
    catch (Exception e)
    {
      Console.WriteLine("Cannot open file for writing");
      Console.WriteLine(e.Message);
      return;
    }
    Console.SetOut(doubleWriter);
  }

  public void Dispose()
  {
    Console.SetOut(oldOut);
    if (fileWriter != null)
    {
      fileWriter.Flush();
      fileWriter.Close();
      fileWriter = null;
    }
    if (fileStream != null)
    {
      fileStream.Close();
      fileStream = null;
    }
  }

}
Christian
  • 841
  • 7
  • 8
  • 6
    I think this is the most complete solution. There is no need to override all the overloads of the Write/WriteLine methods and is transparent to the other code. So all Console activity will be duplicated into the file, without making any changes into other code. – papadi Nov 06 '14 at 10:36
  • 4
    Thanks man! It's awesome! I just replaced *File.Create* by *File.Open(path, FileMode.Append, FileAccess.Write, FileShare.Read);* because I don't wanna erase old logs on startup and I want to be able to open log file while program is still running. – John Jan 29 '16 at 11:29
  • 1
    This didn't require me to replace all my existing calls to `Console.WriteLine()`, which was exactly what I wanted. – NoodleCollie Feb 07 '18 at 16:16
  • For anyone swinging by that's confused how this does this. Look for `Console.SetOut(doubleWriter);`. Which is modifying a global for Console, It took me a bit, since I'm so used to working in applications where practically nothing is global. Good stuff! – Douglas Gaskell Jul 23 '19 at 23:29
  • I like this but for some reason it considerably slows down my application as it logs progress to the console. What's strange is it still slows it down even if I comment out `one.Flush()` and `one.Write(value)` to take the file IO out of the equation. – xr280xr Apr 28 '21 at 17:51
13

Check out log4net. With log4net you can set up console and file appenders that will can output log messages to both places with a single log statement.

tvanfosson
  • 524,688
  • 99
  • 697
  • 795
10

Can't you just redirect the output to a file, using the > command?

c:\>Console.exe > c:/temp/output.txt

If you need to mirror, you can try find a win32 version of tee that splits the output to a file.

See https://superuser.com/questions/74127/tee-for-windows to run tee from PowerShell

Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170
xtofl
  • 40,723
  • 12
  • 105
  • 192
  • 8
    I do need to mirror. That's why it's mentioned in the subject and the body. Thanks for the tip though :) – xyz Jan 07 '09 at 15:21
8

You could subclass the TextWriter class, and then assign its instance to the Console.Out using the Console.SetOut method - which in particular does the same thing as passing the same string to both methods in the log method.

Another way might declaring your own Console class and use the using statement to distinguish between the classes:

using Console = My.Very.Own.Little.Console;

To access the standard console you'd then need:

global::Console.Whatever
arul
  • 13,998
  • 1
  • 57
  • 77
7

EDIT: This method provide the possibility to redirect the console information come from third party package. override the WriteLine method is good for my situation, but you may need to override other Write methods depends on the third party package.

First we need to create new class inherent from StreamWriter, say CombinedWriter;

Then init a new instant of CombinedWriter with Console.Out;

Finally we can redirect console output to the instant of the new class by Console.SetOut;

Following code is the new class works for me.

public class CombinedWriter : StreamWriter
{
    TextWriter console;
    public CombinedWriter(string path, bool append, Encoding encoding, int bufferSize, TextWriter console)
        :base(path, append, encoding, bufferSize)
    {
        this.console = console;
        base.AutoFlush = true; // thanks for @konoplinovich reminding
    }
    public override void WriteLine(string value)
    {
        console.Write(value);
        base.WriteLine(value);
    }
}
Keep Thinking
  • 141
  • 1
  • 7
  • In this way we will not miss anything displayed in console. – Keep Thinking Sep 21 '12 at 01:03
  • 1
    You should override following methods `public override void Write(char value);`, `public override void Write(char[] buffer);`, `public override void Write(string value);` and `public override void Write(char[] buffer, int index, int count);`. Otherwise it does not print to console if you use `WriteLine(format, ...)` method. – Dmytro Ovdiienko Apr 28 '15 at 10:51
6

Log4net can do this for you. You would only write something like this:

logger.info("Message");

A configuration will determine whether the print out will go to console, file or both.

kgiannakakis
  • 103,016
  • 27
  • 158
  • 194
5

I think what you already are using is kind of the best approach. A simple method to essentially mirror your output.

First declare a global TextWriter at the beginning:

private TextWriter txtMirror = new StreamWriter("mirror.txt");

Then make a method for writing:

// Write empty line
private void Log()
{
    Console.WriteLine();
    txtMirror.WriteLine();
}

// Write text
private void Log(string strText)
{
    Console.WriteLine(strText);
    txtMirror.WriteLine(strText);
}

Now, instead of using Console.WriteLine("...");, use Log("...");. Simple as that. It's even shorter!


There could be some trouble if you shift the cursorposition (Console.SetCursorPosition(x, y);), but otherwise works well, I use it myself too!

EDIT

Ofcourse you can make a method for Console.Write(); the same way if you're not using only WriteLines

Community
  • 1
  • 1
Richard de Wit
  • 7,102
  • 7
  • 44
  • 54
  • 1
    This is the simplest solution. Don't forget to add this to the end of your program:
    txtMirror.Flush(); txtMirror.Close();
    – Dominic Isaia Feb 20 '19 at 19:03
3

As suggested by Arul, using Console.SetOut can be used to redirect output to a text file:

Console.SetOut(new StreamWriter("Output.txt"));
Pooven
  • 1,744
  • 1
  • 25
  • 44
hgirish
  • 39
  • 2
2

The decision to use a class, inherited from the StreamWriter, suggestions by user Keep Thinking, works. But I had to to add into constructor base.AutoFlush = true:

{
    this.console = console;
    base.AutoFlush = true;
}

аnd an explicit call to the destructor:

public new void Dispose ()
{
    base.Dispose ();
}

Otherwise, the file is closed earlier than he recorded all the data.

I am using it as:

CombinedWriter cw = new CombinedWriter ( "out.txt", true, Encoding.Unicode, 512, Console.Out );
Console.SetOut (cw);
1

Thank you to Keep Thinking for the excellent solution! I added some further overrides to avoid logging certain console write events that (for my purposes) are only expected for console display.

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RedirectOutput
{
    public class CombinedWriter  : StreamWriter
    {
        TextWriter console;
        public CombinedWriter(string path, bool append, TextWriter consoleout)
            : base(path, append)
        {
            this.console = consoleout;
            base.AutoFlush = true;
        }
        public override void Write(string value)
        {
            console.Write(value);
            //base.Write(value);//do not log writes without line ends as these are only for console display
        }
        public override void WriteLine()
        {
            console.WriteLine();
            //base.WriteLine();//do not log empty writes as these are only for advancing console display
        }
        public override void WriteLine(string value)
        {
            console.WriteLine(value);
            if (value != "")
            {
                base.WriteLine(value);
            }
        }
        public new void Dispose()
        {
            base.Dispose();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            CombinedWriter cw = new CombinedWriter("combined.log", false, Console.Out);
            Console.SetOut(cw);
            Console.WriteLine("Line 1");
            Console.WriteLine();
            Console.WriteLine("Line 2");
            Console.WriteLine("");
            for (int i = 0; i < 10; i++)
            {
                Console.Write("Waiting " + i.ToString());
                Console.CursorLeft = 0;
            }
            Console.WriteLine();
            for (int i = 0; i < 10; i++)
            {
                Console.Write("Waiting " + i.ToString());
            }
            Console.WriteLine();
            Console.WriteLine("Line 3");
            cw.Dispose();
        }
    }
}
1

If you duplicate console output from a code you do not control, for example 3rd party library, all members of TextWriter should be overwritten. The code uses ideas from this thread.

Usage:

using (StreamWriter writer = new StreamWriter(filePath))
{
   using (new ConsoleMirroring(writer))
   {
       // code using console output
   }
}

ConsoleMirroring class

public class ConsoleMirroring : TextWriter
{
    private TextWriter _consoleOutput;
    private TextWriter _consoleError;

    private StreamWriter _streamWriter;

    public ConsoleMirroring(StreamWriter streamWriter)
    {
        this._streamWriter = streamWriter;
        _consoleOutput = Console.Out;
        _consoleError = Console.Error;

        Console.SetOut(this);
        Console.SetError(this);
    }

    public override Encoding Encoding { get { return _consoleOutput.Encoding; } }
    public override IFormatProvider FormatProvider { get { return _consoleOutput.FormatProvider; } }
    public override string NewLine { get { return _consoleOutput.NewLine; } set { _consoleOutput.NewLine = value; } }

    public override void Close()
    {
        _consoleOutput.Close();
        _streamWriter.Close();
    }

    public override void Flush()
    {
        _consoleOutput.Flush();
        _streamWriter.Flush();
    }

    public override void Write(double value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }
    public override void Write(string value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(object value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(decimal value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(float value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(bool value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(int value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(uint value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(ulong value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(long value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(char[] buffer)
    {
        _consoleOutput.Write(buffer);
        _streamWriter.Write(buffer);

    }

    public override void Write(char value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(string format, params object[] arg)
    {
        _consoleOutput.Write(format, arg);
        _streamWriter.Write(format, arg);

    }

    public override void Write(string format, object arg0)
    {
        _consoleOutput.Write(format, arg0);
        _streamWriter.Write(format, arg0);

    }

    public override void Write(string format, object arg0, object arg1)
    {
        _consoleOutput.Write(format, arg0, arg1);
        _streamWriter.Write(format, arg0, arg1);

    }

    public override void Write(char[] buffer, int index, int count)
    {
        _consoleOutput.Write(buffer, index, count);
        _streamWriter.Write(buffer, index, count);

    }

    public override void Write(string format, object arg0, object arg1, object arg2)
    {
        _consoleOutput.Write(format, arg0, arg1, arg2);
        _streamWriter.Write(format, arg0, arg1, arg2);

    }

    public override void WriteLine()
    {
        _consoleOutput.WriteLine();
        _streamWriter.WriteLine();

    }

    public override void WriteLine(double value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(decimal value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(string value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(object value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(float value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(bool value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(uint value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(long value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(ulong value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(int value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(char[] buffer)
    {
        _consoleOutput.WriteLine(buffer);
        _streamWriter.WriteLine(buffer);

    }
    public override void WriteLine(char value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(string format, params object[] arg)
    {
        _consoleOutput.WriteLine(format, arg);
        _streamWriter.WriteLine(format, arg);

    }
    public override void WriteLine(string format, object arg0)
    {
        _consoleOutput.WriteLine(format, arg0);
        _streamWriter.WriteLine(format, arg0);

    }
    public override void WriteLine(string format, object arg0, object arg1)
    {
        _consoleOutput.WriteLine(format, arg0, arg1);
        _streamWriter.WriteLine(format, arg0, arg1);

    }
    public override void WriteLine(char[] buffer, int index, int count)
    {
        _consoleOutput.WriteLine(buffer, index, count);
        _streamWriter.WriteLine(buffer, index, count);

    }
    public override void WriteLine(string format, object arg0, object arg1, object arg2)
    {
        _consoleOutput.WriteLine(format, arg0, arg1, arg2);
        _streamWriter.WriteLine(format, arg0, arg1, arg2);

    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            Console.SetOut(_consoleOutput);
            Console.SetError(_consoleError);
        }
    }
}
Crabba
  • 21
  • 2
0

You can actually create a transparent mirroring of Console.Out to Trace by implementing your own class inherited from TextWriter and overriding WriteLine method.

In WriteLine you can write it to Trace which can then be configured to write to file.

I found this answer very helpful: https://stackoverflow.com/a/10918320/379132

It actually worked for me!

Community
  • 1
  • 1
Sushil
  • 11
  • 1
0

My answer is based on the most-voted non-accepted answer, and also the least-voted answer which I think is the most elegant solution so far. It's a little more generic in terms of the stream type you can use (you may use a MemoryStream for instance), but I've omitted all the extended functionality included in the latter answer for brevity.

class ConsoleMirrorWriter : TextWriter
{
    private readonly StreamWriter _writer;
    private readonly TextWriter _consoleOut;

    public ConsoleMirrorWriter(Stream stream)
    {
        _writer = new StreamWriter(stream);
        _consoleOut = Console.Out;
        Console.SetOut(this);
    }

    public override Encoding Encoding => _writer.Encoding;

    public override void Flush()
    {
        _writer.Flush();
        _consoleOut.Flush();
    }

    public override void Write(char value)
    {
        _writer.Write(value);
        _consoleOut.Write(value);
    }

    protected override void Dispose(bool disposing)
    {
        if (!disposing) return;
        _writer.Dispose();
        Console.SetOut(_consoleOut);
    }
}

Usage:

using (var stream = File.Create(Path.Combine(Path.GetTempPath(), AppDomain.CurrentDomain.FriendlyName)))
using (var writer = new ConsoleMirrorWriter(stream))
{
    // Code using console output.
}
Neo
  • 4,145
  • 6
  • 53
  • 76