Mencerminkan output konsol ke file

89

Dalam aplikasi konsol C #, apakah ada cara cerdas agar keluaran konsol dicerminkan ke file teks?

Saat ini saya hanya meneruskan string yang sama ke keduanya Console.WriteLinedan InstanceOfStreamWriter.WriteLinedalam metode log.

xyz
sumber

Jawaban:

119

Ini mungkin semacam pekerjaan lain, tapi saya akan sebaliknya.

Membuat instance TraceListeneruntuk konsol dan satu untuk file log; setelah itu gunakan Trace.Writepernyataan dalam kode Anda, bukan Console.Write. Setelah itu menjadi lebih mudah untuk menghapus log, atau keluaran konsol, atau memasang mekanisme logging lain.

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

Sejauh yang saya ingat, Anda dapat menentukan pendengar dalam konfigurasi aplikasi sehingga memungkinkan untuk mengaktifkan atau menonaktifkan logging tanpa menyentuh build.

Oliver Friedrich
sumber
4
Sempurna - terima kasih. Saya mengetahui Log4Net tetapi tampaknya salah harus menarik perpustakaan untuk sesuatu seperti ini.
xyz
3
Saya tidak tahu mengapa mereka tidak membuat Trace menjadi lebih besar - bagi saya sepertinya itu berfungsi dengan baik untuk logging skala produksi, tetapi semua orang ingin menggunakan perpustakaan tambahan (seperti log4net) untuk melakukannya.
Coderer
Ini adalah pencerminan satu arah. Maksud saya jika Anda memiliki konsol interaktif dan mendapatkan beberapa data dari pengguna dan ingin memasukkan semuanya ke dalam file, solusi ini tidak berfungsi. Terlepas dari fakta sederhana ini, pertanyaan saya sudah ditutup. Di sini: stackoverflow.com/questions/3886895/…
Xaqron
6
Saya sangat menyukai solusi ini, jadi saya membuat blog singkat tentangnya dengan beberapa pembersihan kecil dan panduan dari beberapa halangan di sepanjang jalan. mcrook.com/2014/11/quick-and-easy-console-logging-trace.html Terima kasih atas solusi hebatnya :)
Michael Crook
51

Ini adalah kelas sederhana yang mensubkelas TextWriter untuk memungkinkan pengalihan input ke file dan konsol.

Gunakan seperti ini

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

Ini kelasnya:

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

}
Kristen
sumber
5
Saya rasa ini adalah solusi paling lengkap. Tidak perlu menimpa semua kelebihan metode Write / WriteLine dan transparan ke kode lain. Jadi semua aktivitas Konsol akan diduplikasi ke dalam file, tanpa membuat perubahan apa pun ke kode lain.
papadi
3
Terima kasih sobat! Itu mengagumkan! Saya baru saja mengganti File.Create dengan File.Open (path, FileMode.Append, FileAccess.Write, FileShare.Read); karena saya tidak ingin menghapus log lama saat startup dan saya ingin dapat membuka file log saat program masih berjalan.
Yohanes
1
Ini tidak mengharuskan saya untuk mengganti semua panggilan yang ada ke Console.WriteLine(), yang persis seperti yang saya inginkan.
x6herbius
Bagi siapa pun yang mampir, itu bingung bagaimana ini melakukan ini. Cari Console.SetOut(doubleWriter);. Yang memodifikasi global untuk Konsol, Saya butuh waktu sedikit, karena saya sudah terbiasa bekerja di aplikasi di mana praktis tidak ada yang global. Barang bagus!
Douglas Gaskell
13

Lihat log4net . Dengan log4net Anda dapat menyiapkan konsol dan penambah file yang akan mengeluarkan pesan log ke kedua tempat dengan satu pernyataan log.

tvanfosson.dll
sumber
6
Menurut saya, lib ekstra harus dihindari jika dapat dilakukan dengan apa yang sudah ada.
Oliver Friedrich
Saya juga merekomendasikan log4net, tetapi sepertinya NLog menggantikan tempatnya di komunitas.
Mark Richman
10

Tidak bisakah Anda mengarahkan output ke file, menggunakan >perintah?

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

Jika Anda perlu melakukan mirror, Anda dapat mencoba menemukan versi win32 teeyang membagi output ke file.

Lihat /superuser/74127/tee-for-windows untuk menjalankan tee dari PowerShell

xtofl
sumber
8
Saya perlu bercermin. Itulah mengapa itu disebutkan dalam subjek dan isi. Terima kasih atas tipnya :)
xyz
8

Anda dapat membuat subkelas kelas TextWriter, lalu menetapkan instance-nya ke Console.Out menggunakan metode Console.SetOut - yang secara khusus melakukan hal yang sama seperti meneruskan string yang sama ke kedua metode dalam metode log.

Cara lain mungkin mendeklarasikan kelas Console Anda sendiri dan menggunakan pernyataan use untuk membedakan antara kelas:

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

Untuk mengakses konsol standar, Anda memerlukan:

global::Console.Whatever
arul
sumber
8

EDIT: Metode ini memberikan kemungkinan untuk mengarahkan informasi konsol yang berasal dari paket pihak ketiga. override metode WriteLine bagus untuk situasi saya, tetapi Anda mungkin perlu menimpa metode Write lain tergantung pada paket pihak ketiga.

Pertama kita perlu membuat kelas baru yang melekat dari StreamWriter, katakanlah CombinedWriter;

Kemudian init instan baru CombinedWriter dengan Console.Out;

Akhirnya kita bisa mengarahkan output konsol ke kelas baru secara instan dengan Console.SetOut;

Kode berikut adalah kelas baru yang berfungsi untuk saya.

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);
    }
}
Terus berpikir
sumber
Dengan cara ini kami tidak akan melewatkan apa pun yang ditampilkan di konsol.
Terus Berpikir
1
Anda harus mengesampingkan metode berikut public override void Write(char value);, public override void Write(char[] buffer);, public override void Write(string value);dan public override void Write(char[] buffer, int index, int count);. Jika tidak, itu tidak mencetak ke konsol jika Anda menggunakan WriteLine(format, ...)metode.
Dmytro Ovdiienko
6

Log4net dapat melakukan ini untuk Anda. Anda hanya akan menulis sesuatu seperti ini:

logger.info("Message");

Konfigurasi akan menentukan apakah hasil cetak akan masuk ke konsol, file atau keduanya.

kgiannakakis
sumber
5

Saya pikir apa yang sudah Anda gunakan adalah jenis pendekatan terbaik. Metode sederhana untuk mencerminkan output Anda pada dasarnya.

Pertama-tama deklarasikan TextWriter global di awal:

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

Kemudian buat metode untuk menulis:

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

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

Sekarang, alih-alih menggunakan Console.WriteLine("...");, gunakan Log("...");. Sederhana seperti itu. Bahkan lebih pendek!


Mungkin ada beberapa masalah jika Anda menggeser posisi kursor ( Console.SetCursorPosition(x, y);), tetapi jika tidak berfungsi dengan baik, saya juga menggunakannya sendiri!

EDIT

Tentu Anda bisa membuat metode Console.Write();dengan cara yang sama jika Anda tidak hanya menggunakan WriteLines

Richard de Wit
sumber
1
Ini adalah solusi paling sederhana. Jangan lupa untuk menambahkan ini di akhir program Anda: <br/> txtMirror.Flush (); txtMirror.Close ();
Dominic Isaia
3

Seperti yang disarankan oleh Arul, menggunakan Console.SetOutdapat digunakan untuk mengarahkan output ke file teks:

Console.SetOut(new StreamWriter("Output.txt"));
hgirish
sumber
2

Keputusan untuk menggunakan kelas, yang diwarisi dari StreamWriter, saran oleh pengguna Keep Thinking, berhasil. Tetapi saya harus menambahkan ke dalam basis konstruktor. AutoFlush = true:

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

dan panggilan eksplisit ke destruktor:

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

Jika tidak, file tersebut ditutup sebelum dia mencatat semua data.

Saya menggunakannya sebagai:

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

Terima kasih untuk Terus Berpikir untuk solusi luar biasa! Saya menambahkan beberapa penggantian lebih lanjut untuk menghindari logging peristiwa penulisan konsol tertentu yang (untuk tujuan saya) hanya diharapkan untuk tampilan konsol.

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();
        }
    }
}
pengguna2789183
sumber
Adakah alasan khusus Anda mengganti metode Dispose dan kemudian memanggil base.Dispose ()?
Adam Plocher
1

Jika Anda menduplikasi keluaran konsol dari kode yang tidak Anda kontrol, misalnya pustaka pihak ketiga, semua anggota TextWriter harus ditimpa. Kode menggunakan ide dari utas ini.

Pemakaian:

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

Kelas 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);
        }
    }
}
Crabba
sumber
0

Anda sebenarnya dapat membuat mirroring transparan dari Console.Out to Trace dengan mengimplementasikan kelas Anda sendiri yang diwarisi dari TextWriter dan mengganti metode WriteLine.

Di WriteLine Anda dapat menuliskannya ke Trace yang kemudian dapat dikonfigurasi untuk menulis ke file.

Saya menemukan jawaban ini sangat membantu: https://stackoverflow.com/a/10918320/379132

Ini benar-benar berhasil untuk saya!

Sushil
sumber
0

Jawaban saya didasarkan pada jawaban yang paling banyak dipilih dan tidak diterima , dan juga jawaban yang paling sedikit dipilih yang menurut saya merupakan solusi paling elegan sejauh ini. Ini sedikit lebih umum dalam hal jenis aliran yang dapat Anda gunakan (Anda dapat menggunakan MemoryStreammisalnya), tetapi saya telah menghilangkan semua fungsi tambahan yang termasuk dalam jawaban terakhir agar singkatnya.

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

Pemakaian:

using (var stream = File.Create(Path.Combine(Path.GetTempPath(), AppDomain.CurrentDomain.FriendlyName)))
using (var writer = new ConsoleMirrorWriter(stream))
{
    // Code using console output.
}
Neo
sumber