Spiegeln der Konsolenausgabe in eine Datei


87

Gibt es in einer C # -Konsolenanwendung eine intelligente Möglichkeit, die Konsolenausgabe in eine Textdatei zu spiegeln?

Derzeit übergebe ich nur die gleiche Zeichenfolge an beide Console.WriteLineund InstanceOfStreamWriter.WriteLinein einer Protokollmethode.

Antworten:


114

Dies mag eine Art mehr Arbeit sein, aber ich würde den umgekehrten Weg gehen.

Instanziieren Sie a TraceListenerfür die Konsole und eins für die Protokolldatei. Verwenden Trace.WriteSie danach Anweisungen in Ihrem Code anstelle von Console.Write. Danach wird es einfacher, das Protokoll oder die Konsolenausgabe zu entfernen oder einen anderen Protokollierungsmechanismus anzuhängen.

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

Soweit ich mich erinnern kann, können Sie die Listener in der Anwendungskonfiguration definieren, um die Protokollierung zu aktivieren oder zu deaktivieren, ohne den Build zu berühren.


4
Das ist perfekt - danke. Mir war Log4Net bekannt, aber es scheint falsch, für so etwas eine Bibliothek abrufen zu müssen.
XYZ

3
Ich weiß nicht, warum sie Trace nicht in größerem Umfang nutzen - es scheint mir, dass es für die Protokollierung im Produktionsmaßstab gut funktionieren sollte, aber jeder möchte eine zusätzliche Bibliothek (wie log4net) in Angriff nehmen, um dies zu tun.
Coderer

Dies ist eine Einwegspiegelung. Ich meinte, wenn Sie eine interaktive Konsole haben und einige Daten vom Benutzer erhalten und alles in einer Datei protokollieren möchten, funktioniert diese Lösung nicht. Trotz dieser einfachen Tatsache ist meine Frage geschlossen. Hier: stackoverflow.com/questions/3886895/…
Xaqron

5
Diese Lösung hat mir sehr gut gefallen, deshalb habe ich einen kurzen Blog darüber erstellt, in dem einige kleinere Fehler behoben und einige Fehler behoben wurden. mcrook.com/2014/11/quick-and-easy-console-logging-trace.html Danke für die großartige Lösung :)
Michael Crook

50

Dies ist eine einfache Klasse, die TextWriter in Unterklassen unterteilt, um die Umleitung der Eingabe sowohl in eine Datei als auch in die Konsole zu ermöglichen.

Verwenden Sie es so

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

Hier ist die Klasse:

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
Ich denke, das ist die vollständigste Lösung. Es ist nicht erforderlich, alle Überladungen der Write / WriteLine-Methoden zu überschreiben, und sie sind für den anderen Code transparent. Daher werden alle Konsolenaktivitäten in die Datei dupliziert, ohne dass Änderungen an anderem Code vorgenommen werden.
Papadi

3
Danke, Mann! Es ist toll! Ich habe gerade File.Create durch File.Open ersetzt (Pfad, FileMode.Append, FileAccess.Write, FileShare.Read); weil ich beim Start keine alten Protokolle löschen möchte und die Protokolldatei öffnen möchte, während das Programm noch läuft.
John

1
Ich musste nicht alle meine bestehenden Anrufe ersetzen Console.WriteLine(), was genau das war, was ich wollte.
x6herbius

Für jeden, der vorbeischwingt, ist das verwirrt, wie das geht. Suchen Sie nach Console.SetOut(doubleWriter);. Was eine globale Konsole modifiziert, hat mich ein bisschen gekostet, da ich es so gewohnt bin, in Anwendungen zu arbeiten, in denen praktisch nichts global ist. Gutes Zeug!
Douglas Gaskell

13

Schauen Sie sich log4net an . Mit log4net können Sie Konsolen- und Dateianhänge einrichten, die Protokollnachrichten mit einer einzigen Protokollanweisung an beide Stellen ausgeben können.


6
Nun, ich denke, dass zusätzliche Bibliotheken vermieden werden sollten, wenn dies mit dem getan werden kann, was bereits vorhanden ist.
Oliver Friedrich

Ich empfehle auch log4net, aber es sieht so aus, als würde NLog seinen Platz in der Community einnehmen.
Mark Richman

10

Können Sie die Ausgabe nicht einfach mit dem >Befehl in eine Datei umleiten ?

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

Wenn Sie spiegeln müssen, können Sie versuchen, eine Win32-Version zu finden tee, die die Ausgabe in eine Datei aufteilt.

Unter /superuser/74127/tee-for-windows können Sie Tee von PowerShell ausführen


8
Ich muss spiegeln. Deshalb wird es im Thema und im Körper erwähnt. Danke für den Tipp :)
xyz

8

Sie können die TextWriter-Klasse in Unterklassen unterteilen und dann ihre Instanz der Console.Out mithilfe der Console.SetOut- Methode zuweisen. Dies entspricht insbesondere der Übergabe derselben Zeichenfolge an beide Methoden in der Protokollmethode.

Eine andere Möglichkeit besteht darin, Ihre eigene Konsolenklasse zu deklarieren und die using-Anweisung zu verwenden, um zwischen den Klassen zu unterscheiden:

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

Um auf die Standardkonsole zuzugreifen, benötigen Sie dann:

global::Console.Whatever

8

BEARBEITEN: Diese Methode bietet die Möglichkeit, die Konsoleninformationen aus dem Paket eines Drittanbieters umzuleiten. Das Überschreiben der WriteLine-Methode ist gut für meine Situation, aber Sie müssen möglicherweise andere Write-Methoden überschreiben, abhängig vom Paket eines Drittanbieters.

Zuerst müssen wir eine neue Klasse erstellen, die StreamWriter innewohnt, z. B. CombinedWriter.

Starten Sie dann einen neuen Moment von CombinedWriter mit Console.Out.

Schließlich können wir die Konsolenausgabe von Console.SetOut auf den Zeitpunkt der neuen Klasse umleiten.

Der folgende Code ist die neue Klasse funktioniert für mich.

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

Auf diese Weise verpassen wir nichts, was in der Konsole angezeigt wird.
Denken Sie weiter

1
Sie sollten folgende Methoden außer Kraft setzen public override void Write(char value);, public override void Write(char[] buffer);, public override void Write(string value);und public override void Write(char[] buffer, int index, int count);. Andernfalls wird nicht auf der Konsole gedruckt, wenn Sie die WriteLine(format, ...)Methode verwenden.
Dmytro Ovdiienko

6

Log4net kann dies für Sie tun. Sie würden nur so etwas schreiben:

logger.info("Message");

Eine Konfiguration bestimmt, ob der Ausdruck an die Konsole, die Datei oder an beide gesendet wird.


4

Ich denke, was Sie bereits verwenden, ist der beste Ansatz. Eine einfache Methode, um Ihre Ausgabe im Wesentlichen zu spiegeln.

Deklarieren Sie zunächst einen globalen TextWriter am Anfang:

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

Dann machen Sie eine Methode zum Schreiben:

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

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

Verwenden Sie jetzt anstatt zu Console.WriteLine("...");verwenden Log("...");. So einfach ist das. Es ist noch kürzer!


Es könnte Probleme geben, wenn Sie die Cursorposition ( Console.SetCursorPosition(x, y);) verschieben, aber ansonsten funktioniert es gut, ich benutze es auch selbst!

BEARBEITEN

Natürlich können Sie eine Methode auf Console.Write();die gleiche Weise erstellen, wenn Sie nicht nur WriteLines verwenden


1
Dies ist die einfachste Lösung. Vergessen Sie nicht, dies am Ende Ihres Programms hinzuzufügen: <br/> txtMirror.Flush (); txtMirror.Close ();
Dominic Isaia

3

Wie von Arul vorgeschlagen, Console.SetOutkann using verwendet werden, um die Ausgabe in eine Textdatei umzuleiten:

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

2

Die Entscheidung, eine vom StreamWriter geerbte Klasse zu verwenden, Vorschläge des Benutzers Keep Thinking, funktioniert. Aber ich musste zur Konstruktorbasis hinzufügen. AutoFlush = true:

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

Und ein expliziter Aufruf an den Destruktor:

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

Andernfalls wird die Datei früher geschlossen, als er alle Daten aufgezeichnet hat.

Ich benutze es als:

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

2

Vielen Dank an Keep Thinking für die hervorragende Lösung! Ich habe einige weitere Überschreibungen hinzugefügt, um zu vermeiden, dass bestimmte Konsolenschreibereignisse protokolliert werden, die (für meine Zwecke) nur für die Konsolenanzeige erwartet werden.

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

Gibt es einen bestimmten Grund, warum Sie die Dispose-Methode ersetzen und dann base.Dispose () aufrufen?
Adam Plocher

1

Wenn Sie die Konsolenausgabe von einem Code duplizieren, den Sie nicht steuern, z. B. einer Bibliothek eines Drittanbieters, sollten alle Mitglieder von TextWriter überschrieben werden. Der Code verwendet Ideen aus diesem Thread.

Verwendung:

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

ConsoleMirroring-Klasse

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

Sie können tatsächlich eine transparente Spiegelung von Console.Out to Trace erstellen, indem Sie Ihre eigene Klasse implementieren, die von TextWriter geerbt wurde, und die WriteLine-Methode überschreiben.

In WriteLine können Sie es in Trace schreiben, das dann so konfiguriert werden kann, dass es in eine Datei schreibt.

Ich fand diese Antwort sehr hilfreich: https://stackoverflow.com/a/10918320/379132

Es hat tatsächlich bei mir funktioniert!


0

Meine Antwort basiert auf der am meisten gewählten nicht akzeptierten Antwort und auch auf der am wenigsten gewählten Antwort, die ich für die eleganteste Lösung halte. Es ist etwas allgemeiner in Bezug auf den Stream-Typ, den Sie verwenden können (Sie können MemoryStreambeispielsweise einen verwenden), aber ich habe der Kürze halber alle erweiterten Funktionen, die in der letztgenannten Antwort enthalten sind, weggelassen .

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

Verwendung:

using (var stream = File.Create(Path.Combine(Path.GetTempPath(), AppDomain.CurrentDomain.FriendlyName)))
using (var writer = new ConsoleMirrorWriter(stream))
{
    // Code using console output.
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.