Arlen Beiler
Arlen Beiler

Reputation: 15906

How do I output console to multiple streams at once in C#?

I have a program that takes the Console output and writes it to a logfile, however it no longer shows up in the console window. Is there a way to keep it in the window but write it to the log file as well?

Update:

appLogStream = new FileStream(logFile, FileMode.Append, FileAccess.Write, FileShare.Read);
TextWriter logtxtWriter = Console.Out;
logstrmWriter = new StreamWriter(appLogStream);
if(!console) Console.SetOut(logstrmWriter);
logstrmWriter.AutoFlush = true;
Console.WriteLine("Started at " + DateTime.Now);

console is a constant set in the class. It basically tells it whether it is using the console window or not (readline is not called, etc, if not in console).

So is there a way to write to both the console and the file?

Upvotes: 3

Views: 4871

Answers (4)

Andres Garcia
Andres Garcia

Reputation: 81

Finally i leave the class like this:

public class ConsoleDecorator : TextWriter
{
    private TextWriter m_OriginalConsoleStream;

    public override Encoding Encoding => m_OriginalConsoleStream.Encoding;

    public ConsoleDecorator(TextWriter consoleTextWriter)
    {
        m_OriginalConsoleStream = consoleTextWriter;
    }

    public override void WriteLine(string value)
    {
        string cHora = DateTime.Now.ToString("yyyyMMddTHHmm:ss") + "." + DateTime.Now.Millisecond.ToString().PadLeft(3, '0');
        string msg = $"{cHora}: {value}";
        m_OriginalConsoleStream.WriteLine(msg);

        // Fire event here with value
        if (Properties.Settings.Default.GenerateLog)
        {
            // Ruta y nombre del archivo de texto
            string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "APP_" + DateTime.Now.ToString("yyyyMMdd").Substring(2, 6) + ".Log");
            try
            {
                using (StreamWriter writer = new StreamWriter(filePath, true))
                {
                    writer.Write('\n' + msg);
                }
            }
            catch { }
        }
    }

    public static void SetToConsole()
    {
        Console.SetOut(new ConsoleDecorator(Console.Out));
    }
}

Upvotes: 0

When you call Console.SetOut, you specify where Console should write to. If you don't (i.e. the way Console is typically used), and you call Console.Write, it checks if it has an output writer, and if not, sets it with

   stream = OpenStandardOutput(256);

and then

        Encoding encoding = Encoding.GetEncoding((int) Win32Native.GetConsoleOutputCP());
        writer = TextWriter.Synchronized(new StreamWriter(stream, encoding, 256, false) { HaveWrittenPreamble = true, AutoFlush = true });

So you should be able to do what you're doing now, and if you also want to echo everything to standard out as if you hadn't redirected Console, you can create your own writer using a stream you open yourself using Console.OpenStandardOutput method. The Win32Native used in that code is internal, so you don't have access to it, but you can use the Console.OutputEncoding to retrieve the encoding that it's using.

Something you can also try is using the Console.Out property to get and hang onto the standard output TextWriter just before you call SetOut. Then you can just use that to echo to standard output.

Upvotes: 0

rekire
rekire

Reputation: 47975

You could simply read that stream log it and print it out.

It depends a little on your code if you assign the output stream to the inputstream of the outfile this could be a little harder if you read the content to a buffer that should be a little easier.


About your update I would suggest that you exchange all Console with a custom logging function e.g. a instance of MyLogger (code below) . Which writes your output to the console and to your log file.

class MyLogger {
    private FileStream appLogStream;

    public MyLogger() {
        appLogStream = new FileStream(logFile, FileMode.Append, FileAccess.Write,
                                      FileShare.Read);
        appLogStream.WriteLine("Started at " + DateTime.Now);
    }

    public Write(string msg) {
        Console.Write(msg);
        appLogStream.Write(msg);
    }

    public WriteLine(string msg) {
        Console.WriteLine(msg);
        appLogStream.WriteLine(msg);
    }
}

Upvotes: 3

eyossi
eyossi

Reputation: 4340

I think you can do something like this:

 public class ConsoleDecorator : TextWriter
{
    private TextWriter m_OriginalConsoleStream;

    public ConsoleDecorator(TextWriter consoleTextWriter)
    {
        m_OriginalConsoleStream = consoleTextWriter;
    }

    public override void WriteLine(string value)
    {
        m_OriginalConsoleStream.WriteLine(value);

        // Fire event here with value
    }


    public static void SetToConsole()
    {
        Console.SetOut(new ConsoleDecorator(Console.Out));
    }
}

You will have to "register" the wrapper with calling ConsoleDecorator.SetToConsole(); After that, every Console.WriteLine call will get to the custom method and there you can fire an event and get the text written in other places (logging for example).

if you will want to use that way, you will have to make the class a singleton and then you can have access to the even registration from other classes (which supposed to write to log file when the even is fired)

Upvotes: 2

Related Questions