มิเรอร์คอนโซลเอาต์พุตไปยังไฟล์


89

ในแอปพลิเคชันคอนโซล C # มีวิธีที่ชาญฉลาดในการมิเรอร์เอาต์พุตคอนโซลเป็นไฟล์ข้อความหรือไม่?

ขณะนี้ฉันเพิ่งส่งสตริงเดียวกันไปยังทั้งสองConsole.WriteLineและInstanceOfStreamWriter.WriteLineในวิธีการบันทึก

คำตอบ:


119

นี่อาจจะเป็นงานมากกว่า แต่ฉันจะไปอีกทางหนึ่ง

สร้างอินสแตนซ์ a 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.");
}

เท่าที่ฉันจำได้คุณสามารถกำหนด Listeners ในการกำหนดค่าแอปพลิเคชันทำให้สามารถเปิดใช้งานหรือปิดใช้งานการบันทึกได้โดยไม่ต้องสัมผัสโครงสร้าง


4
สมบูรณ์แบบ - ขอบคุณ ฉันรู้จัก Log4Net แต่ดูเหมือนว่าจะผิดที่ต้องดึงไลบรารีมาเพื่ออะไรแบบนี้
xyz

3
ฉันไม่รู้ว่าทำไมพวกเขาถึงไม่สร้าง Trace ให้ใหญ่ขึ้น - สำหรับฉันแล้วดูเหมือนว่ามันควรจะทำงานได้ดีสำหรับการบันทึกในระดับการผลิต แต่ทุกคนต้องการที่จะจัดการกับไลบรารีเพิ่มเติม (เช่น log4net) เพื่อทำ
Coderer

นี่คือการสะท้อนทางเดียว ฉันหมายถึงถ้าคุณมีคอนโซลแบบโต้ตอบและรับข้อมูลบางอย่างจากผู้ใช้และต้องการบันทึกทุกอย่างในไฟล์วิธีนี้ใช้ไม่ได้ แม้ว่าคำถามของฉันจะถูกปิด ที่นี่: 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

3
ขอบคุณ! มันเจ๋งมาก! ฉันเพิ่งเปลี่ยนFile.CreateโดยFile.Open (path, FileMode.Append, FileAccess.Write, FileShare.Read); เนื่องจากฉันไม่ต้องการลบบันทึกเก่าเมื่อเริ่มต้นระบบและฉันต้องการเปิดไฟล์บันทึกในขณะที่โปรแกรมยังทำงานอยู่
John

1
สิ่งนี้ไม่ได้ต้องการให้ฉันแทนที่การโทรที่มีอยู่ทั้งหมดConsole.WriteLine()ซึ่งเป็นสิ่งที่ฉันต้องการ
x6herbius

สำหรับใครที่กำลังสับสนว่ามันทำแบบนี้ได้อย่างไร Console.SetOut(doubleWriter);มองหา ซึ่งกำลังปรับเปลี่ยน global สำหรับ Console ฉันต้องใช้เวลาสักหน่อยเนื่องจากฉันคุ้นเคยกับการทำงานในแอปพลิเคชันที่แทบไม่มีอะไรเป็นสากล สิ่งที่ดี!
Douglas Gaskell

13

ตรวจสอบlog4net ด้วย log4net คุณสามารถตั้งค่าคอนโซลและตัวผนวกไฟล์ที่จะส่งออกข้อความบันทึกไปยังทั้งสองที่ด้วยคำสั่งบันทึกเดียว


6
ฉันคิดว่าควรหลีกเลี่ยง libs พิเศษหากสามารถทำได้กับสิ่งที่มีอยู่แล้ว
Oliver Friedrich

ฉันแนะนำ log4net ด้วย แต่ดูเหมือนว่า NLog จะเข้ามาแทนที่ในชุมชน
Mark Richman

10

คุณไม่สามารถเปลี่ยนเส้นทางผลลัพธ์ไปยังไฟล์โดยใช้>คำสั่งได้หรือไม่?

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

หากคุณต้องการมิเรอร์คุณสามารถลองค้นหาเวอร์ชัน win32 teeที่แยกเอาต์พุตเป็นไฟล์

ดู/superuser/74127/tee-for-windowsเพื่อเรียกใช้ทีจาก PowerShell


8
ฉันจำเป็นต้องส่องกระจก นั่นเป็นเหตุผลที่กล่าวถึงในเรื่องและเนื้อหา ขอบคุณสำหรับเคล็ดลับ :)
xyz

8

คุณสามารถซับคลาสคลาส TextWriter จากนั้นกำหนดอินสแตนซ์ให้กับ Console.Out โดยใช้เมธอดConsole.SetOutซึ่งโดยเฉพาะจะทำสิ่งเดียวกันกับการส่งสตริงเดียวกันไปยังทั้งสองวิธีในเมธอดบันทึก

อีกวิธีหนึ่งอาจประกาศคลาสคอนโซลของคุณเองและใช้คำสั่งใช้เพื่อแยกความแตกต่างระหว่างคลาส:

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

ในการเข้าถึงคอนโซลมาตรฐานคุณจะต้อง:

global::Console.Whatever

8

แก้ไข: วิธีนี้ให้ความเป็นไปได้ในการเปลี่ยนเส้นทางข้อมูลคอนโซลที่มาจากแพ็คเกจของบุคคลที่สาม การแทนที่วิธี WriteLine นั้นดีสำหรับสถานการณ์ของฉัน แต่คุณอาจต้องแทนที่วิธีการเขียนอื่น ๆ ขึ้นอยู่กับแพ็คเกจของบุคคลที่สาม

ก่อนอื่นเราต้องสร้างคลาสใหม่จาก StreamWriter กล่าว CombinedWriter;

จากนั้นเริ่มต้น CombinedWriter ทันทีใหม่ด้วย Console.Out;

ในที่สุดเราสามารถเปลี่ยนทิศทางเอาต์พุตคอนโซลไปยังทันทีของคลาสใหม่โดย 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);
    }
}

ด้วยวิธีนี้เราจะไม่พลาดทุกสิ่งที่แสดงในคอนโซล
Keep Thinking

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

6

Log4netสามารถทำสิ่งนี้ให้คุณได้ คุณจะเขียนเฉพาะสิ่งนี้:

logger.info("Message");

การกำหนดค่าจะกำหนดว่างานพิมพ์จะไปที่คอนโซลไฟล์หรือทั้งสองอย่าง


5

ฉันคิดว่าสิ่งที่คุณใช้อยู่เป็นแนวทางที่ดีที่สุด วิธีง่ายๆในการสะท้อนผลลัพธ์ของคุณเป็นหลัก

ก่อนอื่นให้ประกาศ 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 แนะนำการใช้Console.SetOutสามารถใช้เพื่อเปลี่ยนทิศทางเอาต์พุตไปยังไฟล์ข้อความ:

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

2

การตัดสินใจใช้คลาสที่สืบทอดมาจาก StreamWriter คำแนะนำโดยผู้ใช้ Keep Thinking ใช้ได้ผล แต่ฉันต้องเพิ่มลงในฐานตัวสร้าง 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

ขอขอบคุณ Keep Thinking สำหรับวิธีแก้ปัญหาที่ยอดเยี่ยม! ฉันได้เพิ่มการแทนที่เพิ่มเติมเพื่อหลีกเลี่ยงการบันทึกเหตุการณ์การเขียนคอนโซลบางอย่างที่คาดว่า (สำหรับวัตถุประสงค์ของฉัน) สำหรับการแสดงผลคอนโซลเท่านั้น

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 ()?
Adam Plocher

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

ใน WriteLine คุณสามารถเขียนลงใน Trace ซึ่งสามารถกำหนดค่าให้เขียนลงไฟล์ได้

ฉันพบว่าคำตอบนี้มีประโยชน์มาก: https://stackoverflow.com/a/10918320/379132

มันใช้งานได้จริงสำหรับฉัน!


0

คำตอบของฉันมาจากคำตอบที่ไม่ได้รับการโหวตมากที่สุดและยังเป็นคำตอบที่ได้รับการโหวตน้อยที่สุดซึ่งฉันคิดว่าเป็นคำตอบที่ดีที่สุด เป็นเพียงเล็กน้อยทั่วไปในแง่ของประเภทสตรีมที่คุณสามารถใช้ได้ (คุณอาจใช้MemoryStreamตัวอย่างเช่น) แต่ฉันได้ละเว้นฟังก์ชันเพิ่มเติมทั้งหมดที่รวมอยู่ในคำตอบหลังเพื่อความกะทัดรัด

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.
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.