Duplicar la salida de la consola a un archivo

En una aplicación de consola C #, ¿hay alguna manera inteligente de tener la salida de la consola reflejada en un archivo de texto?

Actualmente estoy pasando la misma cadena tanto a Console.WriteLine como a InstanceOfStreamWriter.WriteLine en un método de registro.

Esto puede ser algún tipo de trabajo más, pero yo iría al revés.

TraceListener una instancia de TraceListener para la consola y otra para el archivo de registro; a partir de entonces, use Trace.Write instrucciones Trace.Write en su código en lugar de Console.Write . Después se vuelve más fácil eliminar el registro, la salida de la consola o conectar otro mecanismo de registro.

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

Por lo que puedo recordar, puede definir los oyentes en la configuración de la aplicación, lo que permite activar o desactivar el registro sin tocar la construcción.

Esta es una clase simple que subclasifica TextWriter para permitir la redirección de la entrada tanto a un archivo como a la consola.

Úselo así

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

Aquí está la clase:

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

Echa un vistazo a log4net . Con log4net puede configurar los appendidores de consola y archivo que pueden generar mensajes de registro a ambos lugares con una sola statement de registro.

Podría crear una subclase de la clase TextWriter y luego asignar su instancia a Console.Out utilizando el método Console.SetOut , que en particular hace lo mismo que pasar la misma cadena a ambos métodos en el método de registro.

Otra forma podría ser declarar su propia clase de consola y usar la instrucción using para distinguir entre las clases:

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

Para acceder a la consola estándar necesitarás:

 global::Console.Whatever 

¿No puedes simplemente redirigir la salida a un archivo, usando el comando > ?

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

Si necesita duplicar, puede intentar encontrar una versión de tee win32 que divida el resultado en un archivo.

Como he aprendido mucho de aquí, realmente necesito compartir mi solución aquí.

Primero necesitamos crear una nueva clase inherente de StreamWriter, digamos CombinedWriter;

A continuación, inicie un nuevo instante de CombinedWriter con Console.Out;

Finalmente, podemos redireccionar la salida de la consola al instante de la nueva clase mediante Console.SetOut;

El siguiente código es que la nueva clase funciona para mí.

 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 Write(string value) { console.Write(value); base.Write(value); } } 

Log4net puede hacer esto por ti. Solo escribirías algo como esto:

 logger.info("Message"); 

Una configuración determinará si la impresión irá a la consola, archivo o ambos.

Según lo sugerido por Arul, el uso de Console.SetOut se puede usar para redirigir la salida a un archivo de texto:

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

Creo que lo que ya usas es el mejor enfoque. Un método simple para esencialmente reflejar tu salida.

Primero declare un TextWriter global al principio:

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

Luego crea un método para escribir:

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

Ahora, en lugar de usar Console.WriteLine("..."); , usa Log("..."); . Simple como eso. ¡Es aún más corto!


Podría haber algún problema si cambia la posición del cursor ( Console.SetCursorPosition(x, y); ), pero de lo contrario funciona bien, ¡yo también lo uso!

EDITAR

Por supuesto, puede crear un método para Console.Write(); de la misma manera si no está utilizando solo WriteLines

La decisión de usar una clase, heredada de StreamWriter, sugerencias del usuario Keep Thinking, funciona. Pero tuve que agregar en la base del constructor.AutoFlush = true:

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

y una llamada explícita al destructor:

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

De lo contrario, el archivo se cierra antes de que haya registrado todos los datos.

Lo estoy usando como:

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

¡Gracias a Keep Thinking por la excelente solución! Agregué algunas modificaciones adicionales para evitar el registro de ciertos eventos de escritura de la consola que (para mi propósito) solo se esperan para la visualización de la consola.

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

En realidad, puede crear un reflection transparente de Console.Out to Trace implementando su propia clase heredada de TextWriter y reemplazando el método WriteLine.

En WriteLine puede escribirlo en Trace, que luego se puede configurar para escribir en un archivo.

Encontré esta respuesta muy útil: https://stackoverflow.com/a/10918320/379132

¡Realmente funcionó para mí!

Si duplica la salida de la consola desde un código que no controla, por ejemplo, una biblioteca de terceros, todos los miembros de TextWriter deben sobrescribirse. El código usa ideas de este hilo.

Uso:

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

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