Czy w aplikacji konsolowej C # istnieje sprytny sposób dublowania danych wyjściowych konsoli do pliku tekstowego?
Obecnie po prostu przekazuję ten sam ciąg do obu Console.WriteLine
i InstanceOfStreamWriter.WriteLine
w metodzie dziennika.
To może być trochę więcej pracy, ale zrobiłbym odwrotnie.
Utwórz wystąpienie a TraceListener
dla konsoli i jednego dla pliku dziennika; następnie użyj Trace.Write
instrukcji w swoim kodzie zamiast Console.Write
. Później łatwiej będzie usunąć dziennik, dane wyjściowe konsoli lub dołączyć inny mechanizm rejestrowania.
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.");
}
O ile dobrze pamiętam, słuchaczy można zdefiniować w konfiguracji aplikacji, dzięki czemu możliwe będzie włączenie lub wyłączenie logowania bez dotykania builda.
Jest to prosta klasa, która podklasa TextWriter umożliwia przekierowanie danych wejściowych zarówno do pliku, jak i do konsoli.
Użyj tego w ten sposób
using (var cc = new ConsoleCopy("mylogfile.txt")) { Console.WriteLine("testing 1-2-3"); Console.WriteLine("testing 4-5-6"); Console.ReadKey(); }
Oto klasa:
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; } } }
źródło
Console.WriteLine()
, co było dokładnie tym, czego chciałem.Console.SetOut(doubleWriter);
. Co modyfikuje globalną konsolę, zajęło mi to trochę, ponieważ jestem przyzwyczajony do pracy w aplikacjach, w których praktycznie nic nie jest globalne. Dobry towar!Sprawdź log4net . Dzięki log4net możesz skonfigurować konsolę i programy dołączające pliki, które będą wysyłały komunikaty dziennika do obu miejsc za pomocą jednej instrukcji dziennika.
źródło
Czy nie możesz po prostu przekierować wyjścia do pliku za pomocą
>
polecenia?Jeśli potrzebujesz
tee
kopii lustrzanej, możesz spróbować znaleźć wersję win32, która dzieli dane wyjściowe na plik.Zobacz /superuser/74127/tee-for-windows, aby uruchomić tee z PowerShell
źródło
Możesz utworzyć podklasę klasy TextWriter, a następnie przypisać jej instancję do klasy Console.Out przy użyciu metody Console.SetOut - która w szczególności robi to samo, co przekazanie tego samego ciągu do obu metod w metodzie dziennika.
Innym sposobem może być zadeklarowanie własnej klasy Console i użycie instrukcji using do rozróżnienia klas:
using Console = My.Very.Own.Little.Console;
Aby uzyskać dostęp do standardowej konsoli, której potrzebujesz:
global::Console.Whatever
źródło
EDYCJA: Ta metoda daje możliwość przekierowania informacji konsoli pochodzących z pakietu innej firmy. zastąpienie metody WriteLine jest dobre w mojej sytuacji, ale może być konieczne zastąpienie innych metod Write, które zależą od pakietu innej firmy.
Najpierw musimy utworzyć nową klasę nieodłączną od StreamWriter, powiedzmy CombinedWriter;
Następnie zainicjuj nową chwilę CombinedWriter z Console.Out;
Wreszcie możemy przekierować dane wyjściowe konsoli do momentu nowej klasy przez Console.SetOut;
Poniższy kod to nowa klasa działa dla mnie.
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); } }
źródło
public override void Write(char value);
,public override void Write(char[] buffer);
,public override void Write(string value);
ipublic override void Write(char[] buffer, int index, int count);
. W przeciwnym razie nie drukuje do konsoli, jeśli używaszWriteLine(format, ...)
metody.Log4net może to zrobić za Ciebie. Napisałbyś tylko coś takiego:
logger.info("Message");
Konfiguracja określi, czy wydruk trafi do konsoli, do pliku czy do obu.
źródło
Myślę, że to, czego już używasz, jest najlepszym podejściem. Prosta metoda odzwierciedlania wyników.
Najpierw zadeklaruj globalny TextWriter na początku:
private TextWriter txtMirror = new StreamWriter("mirror.txt");
Następnie utwórz metodę pisania:
// Write empty line private void Log() { Console.WriteLine(); txtMirror.WriteLine(); } // Write text private void Log(string strText) { Console.WriteLine(strText); txtMirror.WriteLine(strText); }
Teraz zamiast
Console.WriteLine("...");
używaćLog("...");
. Proste. Jest jeszcze krótszy!Przesunięcie pozycji kursora (
Console.SetCursorPosition(x, y);
) może spowodować pewne problemy , ale poza tym działa dobrze, sam też z niej korzystam!EDYTOWAĆ
Oczywiście możesz znaleźć metodę
Console.Write();
ten sam sposób, jeśli nie używasz tylko WriteLinesźródło
Jak sugeruje Arul, użycie
Console.SetOut
może być użyte do przekierowania wyjścia do pliku tekstowego:Console.SetOut(new StreamWriter("Output.txt"));
źródło
Decyzja o użyciu klasy, odziedziczona po StreamWriter, sugestie użytkownika Keep Thinking, działa. Ale musiałem dodać do bazy konstruktora.AutoFlush = true:
{ this.console = console; base.AutoFlush = true; }
i wyraźne wezwanie do destruktora:
public new void Dispose () { base.Dispose (); }
W przeciwnym razie plik zostanie zamknięty wcześniej niż zarejestrował wszystkie dane.
Używam go jako:
CombinedWriter cw = new CombinedWriter ( "out.txt", true, Encoding.Unicode, 512, Console.Out ); Console.SetOut (cw);
źródło
Dziękuję za doskonałe rozwiązanie! Dodałem kilka dalszych nadpisań, aby uniknąć rejestrowania niektórych zdarzeń zapisu konsoli, które (dla moich celów) są oczekiwane tylko do wyświetlania konsoli.
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(); } } }
źródło
Jeśli zduplikujesz dane wyjściowe konsoli z kodu, nad którym nie kontrolujesz, na przykład biblioteki innej firmy, wszyscy członkowie TextWriter powinni zostać nadpisani. Kod wykorzystuje pomysły z tego wątku.
Stosowanie:
using (StreamWriter writer = new StreamWriter(filePath)) { using (new ConsoleMirroring(writer)) { // code using console output } }
ConsoleMirroring, klasa
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); } } }
źródło
W rzeczywistości możesz stworzyć przezroczyste dublowanie Console.Out to Trace, implementując własną klasę odziedziczoną po TextWriter i nadpisując metodę WriteLine.
W WriteLine możesz zapisać go do Trace, który można następnie skonfigurować do zapisu do pliku.
Ta odpowiedź okazała się bardzo pomocna: https://stackoverflow.com/a/10918320/379132
To faktycznie zadziałało dla mnie!
źródło
Moja odpowiedź opiera się na największej liczbie głosów odrzuconych, które nie zostały zaakceptowane , a także na najmniejszej liczbie głosów, co moim zdaniem jest jak dotąd najbardziej eleganckim rozwiązaniem. Jest trochę bardziej ogólny pod względem typu strumienia, którego możesz użyć (możesz użyć
MemoryStream
na przykład), ale dla zwięzłości pominąłem wszystkie rozszerzone funkcje zawarte w drugiej odpowiedzi .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); } }
Stosowanie:
using (var stream = File.Create(Path.Combine(Path.GetTempPath(), AppDomain.CurrentDomain.FriendlyName))) using (var writer = new ConsoleMirrorWriter(stream)) { // Code using console output. }
źródło