将控制台输出镜像到文件


88

在C#控制台应用程序中,是否存在将控制台输出镜像到文本文件的明智方法?

目前,我只是将相同的字符串传递给两者Console.WriteLineInstanceOfStreamWriter.WriteLine以log方法传递。

Answers:


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.");
}

据我所知,您可以在应用程序配置中定义侦听器,从而可以在不触摸构建的情况下激活或停用日志记录。


4
太好了-谢谢。我知道Log4Net,但是必须为这样的内容插入库似乎是错误的。
xyz

3
我不知道为什么他们不做大量的Trace工作-在我看来,它应该可以很好地用于生产规模的日志记录,但是每个人都想添加一个额外的库(例如log4net)来完成它。
编码员

这是单向镜像。我的意思是,如果您有一个交互式控制台,并且从用户那里获取了一些数据,并且想要将所有内容记录到文件中,则此解决方案将无法正常工作。尽管有这个简单的事实,但我的问题仍未解决。在这里:stackoverflow.com/questions/3886895/...
Xaqron

6
我真的很喜欢这个解决方案,因此我做了一个简短的博客,其中进行了一些小的清理和逐步解决的一些问题。 mcrook.com/2014/11/quick-and-easy-console-logging-trace.html 感谢您的出色解决方案:)
Michael Crook

51

这是一个简单的类,该类将TextWriter子类化,以允许将输入重定向到文件和控制台。

这样使用

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

这是课程:

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;
    }
  }

}

5
我认为这是最完整的解决方案。无需重写Write / WriteLine方法的所有重载,并且对其他代码是透明的。因此,所有控制台活动都将复制到文件中,而无需对其他代码进行任何更改。
papadi 2014年

3
谢啦!这很棒!我只是将File.Create替换为File.Open(path,FileMode.Append,FileAccess.Write,FileShare.Read); 因为我不想在启动时删除旧日志,并且希望能够在程序仍在运行时打开日志文件。
约翰

1
这并不需要我替换所有对的现有调用Console.WriteLine(),而这正是我想要的。
x6herbius

对于任何摇摆的人来说,这是怎么做到的。寻找Console.SetOut(doubleWriter);。这正在修改Console的全局变量,花了我一点时间,因为我已经习惯于在几乎没有全局变量的应用程序中工作。好东西!
道格拉斯·加斯凯尔

13

log4net。使用log4net,您可以设置控制台和文件追加器,它们可以通过单个log语句将日志消息输出到两个位置。


6
好吧,我认为如果可以使用现有的库来完成,则应该避免额外的库。
奥利弗·弗里德里希

我还建议使用log4net,但看起来NLog在社区中占有一席之地。
Mark Richman 2014年

10

您不能使用>命令将输出重定向到文件吗?

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

如果需要镜像,则可以尝试找到Win32版本,tee该版本会将输出拆分为一个文件。

请参阅/superuser/74127/tee-for-windows从PowerShell运行tee


8
我确实需要镜像。这就是在主题和正文中提到它的原因。虽然感谢小费:)
xyz

8

您可以对TextWriter类进行子类化,然后使用Console.SetOut方法将其实例分配给Console.Out-特别是与将相同的字符串传递给log方法中的两个方法具有相同的作用。

另一种方法可能是声明您自己的Console类,并使用using语句来区分这些类:

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

要访问标准控制台,您需要:

global::Console.Whatever

8

编辑:此方法提供了重定向控制台信息来自第三方程序包的可能性。重写WriteLine方法对我的情况很有用,但是您可能需要重写其他Write方法,具体取决于第三方程序包。

首先,我们需要创建StreamWriter固有的新类,比如CombinedWriter。

然后使用Console.Out初始化一个CombinedWriter的新实例;

最后,我们可以通过Console.SetOut;将控制台输出重定向到新类的实例。

以下代码是对我有用的新类。

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);
    }
}

这样,我们将不会错过控制台中显示的任何内容。
继续思考

1
你应该重写下面的方法public override void Write(char value);public override void Write(char[] buffer);public override void Write(string value);public override void Write(char[] buffer, int index, int count);。否则,如果使用WriteLine(format, ...)方法,它将不会打印到控制台。
Dmytro Ovdiienko 2015年

6

Log4net可以为您做到这一点。您只会写这样的东西:

logger.info("Message");

配置将确定打印输出是进入控制台,文件还是同时进入两者。


4

我认为您已经在使用的是最好的方法。一种基本反映您的输出的简单方法。

首先在开头声明一个全局TextWriter:

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

然后编写一种写方法:

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

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

现在,使用而不是Console.WriteLine("...");使用Log("...");。就那么简单。更短!


如果您移动光标位置(Console.SetCursorPosition(x, y);)可能会有些麻烦,但否则效果很好,我自己也使用它!

编辑

当然,Console.Write();如果您不只使用WriteLines,也可以使用相同的方法


1
这是最简单的解决方案。不要忘记将其添加到程序的末尾:<br/> txtMirror.Flush();。txtMirror.Close();
Dominic Isaia

3

如Arul所建议,usingConsole.SetOut可用于将输出重定向到文本文件:

Console.SetOut(new StreamWriter("Output.txt"));

2

由用户Keep Thought提出的使用从StreamWriter继承的类的决定的决定起作用。但是我必须添加到构造函数base.AutoFlush = true中:

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

并明确调用析构函数:

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

否则,文件将在他记录所有数据之前关闭。

我将其用作:

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

2

感谢您继续考虑出色的解决方案!我添加了一些其他替代,以避免记录某些控制台写入事件(出于我的目的)(仅出于控制台目的)。

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();
        }
    }
}

您要替换Dispose方法然后调用base.Dispose()的任何特殊原因吗?
亚当·普洛彻

1

如果从不受控制的代码(例如,第三方库)复制控制台输出,则应覆盖TextWriter的所有成员。该代码使用了该线程的想法。

用法:

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

ConsoleMirroring类

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);
        }
    }
}

0

实际上,您可以通过实现继承自TextWriter的类并重写WriteLine方法来创建Console.Out到Trace的透明镜像。

在WriteLine中,您可以将其写入Trace,然后可以对其进行配置以写入文件。

我发现此答案非常有帮助:https : //stackoverflow.com/a/10918320/379132

它实际上为我工作!


0

我的答案基于投票最多的不被接受的答案,也是投票最少的答案,我认为这是迄今为止最优雅的解决方案。就可以使用的流类型而言,它更为通用(MemoryStream例如,您可以使用a ),但是为了简洁起见,我省略了后者的所有扩展功能。

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);
    }
}

用法:

using (var stream = File.Create(Path.Combine(Path.GetTempPath(), AppDomain.CurrentDomain.FriendlyName)))
using (var writer = new ConsoleMirrorWriter(stream))
{
    // Code using console output.
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.