Mirroring dell'output della console in un file


88

In un'applicazione console C #, esiste un modo intelligente per eseguire il mirroring dell'output della console in un file di testo?

Attualmente sto solo passando la stessa stringa a entrambi Console.WriteLinee InstanceOfStreamWriter.WriteLinein un metodo di log.

Risposte:


117

Potrebbe trattarsi di qualche tipo di lavoro in più, ma io andrei al contrario.

Istanziare un TraceListenerper la console e uno per il file di registro; da allora in poi usa le Trace.Writeistruzioni nel tuo codice invece di Console.Write. In seguito diventa più facile rimuovere il registro, o l'output della console, o collegare un altro meccanismo di registrazione.

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

Per quanto posso ricordare, è possibile definire i listener nella configurazione dell'applicazione rendendo possibile attivare o disattivare il logging senza toccare la build.


4
Perfetto, grazie. Ero a conoscenza di Log4Net ma sembra sbagliato dover inserire una libreria per qualcosa di simile.
xyz

3
Non so perché non fanno una quantità maggiore di Trace - mi sembra che dovrebbe funzionare bene per la registrazione su scala di produzione, ma tutti vogliono aggiungere una libreria extra (come log4net) per farlo.
Coderer

Questo è un mirroring unidirezionale. Intendevo dire che se si dispone di una console interattiva e si ottengono alcuni dati dall'utente e si desidera registrare tutto in un file, questa soluzione non funziona. Nonostante questo semplice fatto la mia domanda è chiusa. Qui: stackoverflow.com/questions/3886895/...
Xaqron

6
Mi è piaciuta molto questa soluzione, quindi ho creato un rapido blog su di essa con qualche piccola ripulitura e una soluzione di alcuni ostacoli lungo la strada. mcrook.com/2014/11/quick-and-easy-console-logging-trace.html Grazie per l'ottima soluzione :)
Michael Crook

51

Questa è una semplice classe che sottoclasse TextWriter per consentire il reindirizzamento dell'input sia a un file che alla console.

Usalo in questo modo

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

Ecco la classe:

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
Penso che questa sia la soluzione più completa. Non è necessario sovrascrivere tutti gli overload dei metodi Write / WriteLine ed è trasparente per l'altro codice. Quindi tutta l'attività della console verrà duplicata nel file, senza apportare modifiche ad altro codice.
papadi

3
Grazie uomo! È meraviglioso! Ho appena sostituito File.Create con File.Open (path, FileMode.Append, FileAccess.Write, FileShare.Read); perché non voglio cancellare i vecchi registri all'avvio e voglio essere in grado di aprire il file di registro mentre il programma è ancora in esecuzione.
John

1
Ciò non richiedeva di sostituire tutte le mie chiamate esistenti a Console.WriteLine(), che era esattamente quello che volevo.
x6herbius

Per chiunque passi da questo è confuso come questo fa questo. Cerca Console.SetOut(doubleWriter);. Che sta modificando un globale per Console, ci ho messo un po ', dal momento che sono così abituato a lavorare in applicazioni in cui praticamente nulla è globale. Roba buona!
Douglas Gaskell

13

Dai un'occhiata a log4net . Con log4net è possibile configurare console e file appender che possono inviare messaggi di log in entrambe le posizioni con una singola istruzione di log.


6
Bene, penso che le librerie extra dovrebbero essere evitate se può essere fatto con ciò che è già presente.
Oliver Friedrich

Raccomando anche log4net, ma sembra che NLog stia prendendo il suo posto nella comunità.
Mark Richman

10

Non puoi semplicemente reindirizzare l'output su un file, usando il >comando?

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

Se hai bisogno di eseguire il mirroring, puoi provare a trovare una versione win32 teeche divide l'output in un file.

Vedi /superuser/74127/tee-for-windows per eseguire tee da PowerShell


8
Ho bisogno di rispecchiare. Ecco perché è menzionato nell'oggetto e nel corpo. Grazie per la punta però :)
xyz

8

È possibile creare una sottoclasse della classe TextWriter e quindi assegnare la sua istanza a Console.Out utilizzando il metodo Console.SetOut , che in particolare fa la stessa cosa del passaggio della stessa stringa a entrambi i metodi nel metodo log.

Un altro modo potrebbe dichiarare la propria classe Console e utilizzare l'istruzione using per distinguere tra le classi:

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

Per accedere alla console standard avresti quindi bisogno di:

global::Console.Whatever

8

EDIT: questo metodo offre la possibilità di reindirizzare le informazioni sulla console provenienti da pacchetti di terze parti. sovrascrivere il metodo WriteLine va bene per la mia situazione, ma potrebbe essere necessario sovrascrivere altri metodi di scrittura a seconda del pacchetto di terze parti.

Per prima cosa dobbiamo creare una nuova classe inerente a StreamWriter, diciamo CombinedWriter;

Quindi inizia un nuovo istante di CombinedWriter con Console.Out;

Infine possiamo reindirizzare l'output della console all'istante della nuova classe tramite Console.SetOut;

Il codice seguente è la nuova classe che funziona per me.

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

In questo modo non ci mancherà nulla visualizzato in console.
Continua a pensare

1
Si dovrebbe eseguire l'override seguenti metodi public override void Write(char value);, public override void Write(char[] buffer);, public override void Write(string value);e public override void Write(char[] buffer, int index, int count);. In caso contrario, non viene stampato sulla console se si utilizza il WriteLine(format, ...)metodo.
Dmytro Ovdiienko

6

Log4net può farlo per te. Scriveresti solo qualcosa del genere:

logger.info("Message");

Una configurazione determinerà se la stampa andrà su console, file o entrambi.


4

Penso che quello che stai già usando sia l'approccio migliore. Un metodo semplice per rispecchiare essenzialmente il tuo output.

Per prima cosa dichiara un TextWriter globale all'inizio:

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

Quindi crea un metodo per scrivere:

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

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

Ora, invece di usare Console.WriteLine("...");, usa Log("...");. Semplice come quella. È ancora più breve!


Potrebbero esserci dei problemi se sposti il ​​cursoreposition ( Console.SetCursorPosition(x, y);), ma per il resto funziona bene, lo uso anche io!

MODIFICARE

Ovviamente puoi creare un metodo Console.Write();allo stesso modo se non stai usando solo WriteLines


1
Questa è la soluzione più semplice. Non dimenticare di aggiungere questo alla fine del tuo programma: <br/> txtMirror.Flush (); txtMirror.Close ();
Dominic Isaia

3

Come suggerito da Arul, using Console.SetOutpuò essere utilizzato per reindirizzare l'output a un file di testo:

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

2

La decisione di utilizzare una classe, ereditata dallo StreamWriter, suggerita dall'utente Keep Thinking, funziona. Ma ho dovuto aggiungere nella base del costruttore. AutoFlush = true:

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

e una chiamata esplicita al distruttore:

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

In caso contrario, il file viene chiuso prima di aver registrato tutti i dati.

Lo sto usando come:

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

2

Grazie a Keep Thinking per l'eccellente soluzione! Ho aggiunto ulteriori sostituzioni per evitare di registrare determinati eventi di scrittura della console che (per i miei scopi) sono previsti solo per la visualizzazione della console.

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

Qualche motivo particolare per sostituire il metodo Dispose e quindi chiamare base.Dispose ()?
Adam Plocher

1

Se duplichi l'output della console da un codice che non controlli, ad esempio una libreria di terze parti, tutti i membri di TextWriter dovrebbero essere sovrascritti. Il codice usa idee da questo thread.

Utilizzo:

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

ConsoleMirroring classe

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

Puoi effettivamente creare un mirroring trasparente di Console.Out to Trace implementando la tua classe ereditata da TextWriter e sovrascrivendo il metodo WriteLine.

In WriteLine puoi scriverlo su Trace che può quindi essere configurato per scrivere su file.

Ho trovato questa risposta molto utile: https://stackoverflow.com/a/10918320/379132

In realtà ha funzionato per me!


0

La mia risposta si basa sulla risposta non accettata più votata e anche sulla risposta meno votata che ritengo sia la soluzione più elegante finora. È un po 'più generico in termini di tipo di flusso che puoi usare (puoi usare un MemoryStreamper esempio), ma ho omesso tutte le funzionalità estese incluse in quest'ultima risposta per brevità.

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

Utilizzo:

using (var stream = File.Create(Path.Combine(Path.GetTempPath(), AppDomain.CurrentDomain.FriendlyName)))
using (var writer = new ConsoleMirrorWriter(stream))
{
    // Code using console output.
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.