Зеркальное отображение вывода консоли в файл

88

Есть ли в консольном приложении C # разумный способ зеркального отображения вывода консоли в текстовый файл?

В настоящее время я просто передаю одну и ту же строку обоим Console.WriteLineи InstanceOfStreamWriter.WriteLineв методе журнала.

xyz
источник

Ответы:

117

Это может быть еще какая-то работа, но я бы пошел наоборот.

Создайте экземпляр TraceListenerдля консоли и один для файла журнала; после этого используйте Trace.Writeоператоры в своем коде вместо Console.Write. После этого становится проще удалить журнал, вывод консоли или подключить другой механизм ведения журнала.

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.

Oliver Friedrich
источник
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
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
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 : stackoverflow.com/questions/3886895/…
Xaqron
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. mcrook.com/2014/11/quick-and-easy-console-logging-trace.html Thanks for the great solution :)
Michael Crook
51

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
источник
5
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
3
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
1
This didn't require me to replace all my existing calls to Console.WriteLine(), which was exactly what I wanted.
x6herbius
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
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
источник
6
Well, I think that extra libs should be avoided if it can be done with what is already there.
Oliver Friedrich
I also recommend log4net, but it looks like NLog is taking its place in the community.
Mark Richman
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 /superuser/74127/tee-for-windows to run tee from PowerShell

xtofl
источник
8
I do need to mirror. That's why it's mentioned in the subject and the body. Thanks for the tip though :)
xyz
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
источник
8

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
источник
In this way we will not miss anything displayed in console.
Keep Thinking
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
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
источник
4

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

Richard de Wit
источник
1
This is the simplest solution. Don't forget to add this to the end of your program:<br/> txtMirror.Flush(); txtMirror.Close();
Dominic Isaia
3

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

Console.SetOut(new StreamWriter("Output.txt"));
hgirish
источник
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);
konoplinovich
источник
2

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();
        }
    }
}
user2789183
источник
Any particular reason you're replacing the Dispose method and then calling base.Dispose() ?
Adam Plocher
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
источник
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!

Sushil
источник
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
источник