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.WriteLine
dan InstanceOfStreamWriter.WriteLine
dalam metode log.
Jawaban:
Ini mungkin semacam pekerjaan lain, tapi saya akan sebaliknya.
Membuat instance TraceListener
untuk konsol dan satu untuk file log; setelah itu gunakan Trace.Write
pernyataan 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.
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;
}
}
}
Console.WriteLine()
, yang persis seperti yang saya inginkan.
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!
Lihat log4net . Dengan log4net Anda dapat menyiapkan konsol dan penambah file yang akan mengeluarkan pesan log ke kedua tempat dengan satu pernyataan log.
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 tee
yang membagi output ke file.
Lihat /superuser/74127/tee-for-windows untuk menjalankan tee dari PowerShell
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
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);
}
}
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.
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.
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!
Tentu Anda bisa membuat metode Console.Write();
dengan cara yang sama jika Anda tidak hanya menggunakan WriteLines
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);
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();
}
}
}
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);
}
}
}
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!
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 MemoryStream
misalnya), 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.
}