如何在C#中将控制台输出同时写入控制台和文件?

7

我有一个简单的控制台应用程序,并且我已经在许多地方使用了Console.WriteLine来向用户显示正在执行的活动。但是,最后我还想将所有控制台输出保存到日志文件中。目前,我的代码类似于:

if (!Directory.Exists(LOG_DIRECTORY)) {
    Directory.CreateDirectory(LOG_DIRECTORY);
}

long ticks = DateTime.Now.Ticks;
string logFilename = ticks.ToString() + ".txt";
string filePath = Directory.GetCurrentDirectory() + "\\" + LOG_DIRECTORY + "\\" + logFilename;
FileStream ostream = null;
StreamWriter writer = null;
TextWriter oldOut = Console.Out;

try
{
    ostream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
    writer = new StreamWriter(ostream);
}
catch (Exception ex)
{
    Console.WriteLine("Cannot open {0} for writing.", logFilename);
    Console.WriteLine(ex.Message);
    return;
}

Console.SetOut(writer);

Console.WriteLine("{0}", Directory.GetCurrentDirectory());

Console.SetOut(oldOut);
writer.Close();
ostream.Close();

Console.WriteLine("\n\nDone!");

问题在于这将直接将内容打印到文件中,而在控制台上不会有任何输出。是否有任何解决方法?请注意,我需要Console.WriteLine的实时输出直接写入控制台,而对于写入日志文件,则可以在程序结束时完成,此时几乎所有其他操作都已完成。


1
你可以编写自己的组件,也可以使用像NLog这样的日志记录工具,它可以同时写入多个输出。我们使用NLog来写入调试器、控制台、CSV和缓冲电子邮件(用于自动错误报告)。 - DiskJunky
4个回答

11

您可以自己创建组件来写入多个输出,或者使用像NLog这样的日志记录工具。 可以进行配置,使得在<targets>部分中有类似以下内容的设置;

<target name="debugger" xsi:type="Debugger" layout="${level}>${message} (${exception:format=ToString})"/>
<target name="console" xsi:type="ColoredConsole" layout="${date:format=dd-MM-yyyy HH\:mm\:ss} - ${message}" />
<target name="FullCSVFile" xsi:type="File"  fileName="${specialfolder:folder=LocalApplicationData}\YourApp\YourApp-${date:format=yyyy-MM-dd}.csv">
  <layout xsi:type="CsvLayout">
    <column name="Index" layout="${counter}" />
    <column name="ThreadID" layout="${threadid}" />
    <column name="Time" layout="${longdate}" />
    <column name="Severity" layout="${level:uppercase=true}" />
    <column name="Location" layout="${callsite:className=False:fileName=True:includeSourcePath=False:methodName=False}" />
    <column name="Detail" layout="${message}" />
    <column name="Exception" layout="${exception:format=ToString}" />
  </layout>
</target>

然后,在 rules 部分,您将会有以下内容;

<logger name="*" minlevel="Debug" writeTo="console" />
<logger name="*" minlevel="Debug" writeTo="debugger" />
<logger name="*" minlevel="Debug" writeTo="FullCSVFile" />

实际进行写操作时,您的C#代码可能如下所示:

// at a class level;
private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();

// later in code that writes to both targets...
_logger.Info("Something happened!");

谢谢,但这会写入调试器和控制台,而不是文件和控制台,对吗? - typos
1
@typos,抱歉,我已经更新了我的答案,可以将其写入CSV文件中。这来自我们在测试和生产系统中使用的配置,一旦启动,它就非常有效。NLog可以配置为写入多个终端,如SQL Server、电子邮件等。 - DiskJunky

1

如何将控制台输出保存到变量中,例如 string arrayList<string>?然后使用 File.WriteAllText(filepath, consoleOutputArray); 方法写入文件。

我的意思是这样的:

 class Program
    {
        static void Main(string[] args)
        {
            List<string> textStorage = new List<string>();

            string exampleData = "Ford Mustang";

            Console.WriteLine(exampleData);

            SaveOutput(ref textStorage, exampleData);

            System.IO.File.WriteAllLines(@"C://Desktop//MyFolder", textStorage);

        }
        public static void SaveOutput(ref List<string> textStorage, string output)
        {
            textStorage.Add(output);
        }
    }

如何将控制台输出保存到变量中? - typos
只需将字符串分配给它即可。 - john.kernel

0
在每个控制台输出后,将输出存储在列表中,并在程序结束时使用详细信息更新日志。
var list = new List<string>;
string xyz = "message";
Console.WriteLine(xyz);
list.add(xyz);
foreach (object o in list)
{
     StreamWriter sw = null;
            String Logfile = "C:\ExceptionLog.txt";
            if (!System.IO.File.Exists(LogFile))
            {
                sw = File.CreateText(LogFile);


            }
            else
            {
                sw = File.AppendText(@"C:\ExceptionLog.txt");
            }

            sw.WriteLine(o);
            sw.Close();
}

0

我在简单的场景下使用类似这样的东西 - 当我正在原型设计或研究某些内容时,不想担心意外丢失控制台输出。

// run this at application start
using var multiWriter = new MultiTextWriter(File.CreateText("my_log_file"), Console.Out);
Console.SetOut(multiWriter);

public class MultiTextWriter : TextWriter
{
    private readonly TextWriter[] _writers;

    public MultiTextWriter(params TextWriter[] writers)
    {
        _writers = writers;
    }

    // abstract in parent; most of the time you don't need it, though
    // xml stuff needs it, so you may have to implement it
    public override Encoding Encoding => throw new NotImplementedException();

    // technically I think this is the only thing you need to override
    public override void Write(char value)
    {
        for (var index = 0; index < _writers.Length; index++)
        {
            _writers[index].Write(value);
        }
    }

    public override void Write(string? value)
    {
        for (var index = 0; index < _writers.Length; index++)
        {
            _writers[index].Write(value);
        }
    }

    protected override void Dispose(bool disposing)
    {
        for (var index = 0; index < _writers.Length; index++)
        {
            _writers[index].Dispose();
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接