Дзеркальне відображення виводу консолі у файл


88

Чи існує розумний спосіб у консольному додатку C # віддзеркалити вихід консолі у текстовий файл?

На даний момент я просто передаю один і той же рядок обом Console.WriteLineі InstanceOfStreamWriter.WriteLineв методі журналу.

Відповіді:


117

Це може бути ще якась робота, але я пішов би навпаки.

Примірник a TraceListenerдля консолі та один для файлу журналу; після цього використовуйте Trace.Writeоператори у своєму коді замість Console.Write. Після цього стає легше видалити журнал, вихід консолі або підключити інший механізм реєстрації.

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

Наскільки я пам’ятаю, ви можете визначити слухачів у конфігурації програми, що дозволяє активувати або деактивувати ведення журналу, не торкаючись збірки.


4
Це ідеально - дякую. Мені було відомо про Log4Net, але здається неправильним, коли доводиться втягувати бібліотеку для чогось подібного.
xyz

3
Я не знаю, чому вони не укладають більшої угоди з Trace - мені здається, це має добре працювати для ведення журналів у виробничому масштабі, але всі хочуть взяти додаткову бібліотеку (як log4net), щоб зробити це.
Кодерер

Це одностороннє дзеркальне відображення. Я мав на увазі, якщо у вас є інтерактивна консоль і ви отримуєте деякі дані від користувача і хочете записати все у файл, це рішення не працює. Незважаючи на цей простий факт, моє питання закрите. Тут: stackoverflow.com/questions/3886895 / ...
Xaqron

6
Мені дуже сподобалось це рішення, тому я зробив короткий блог про нього, виконавши невеликі зачистки та покрокові інструкції з кількох зачеплень по дорозі. mcrook.com/2014/11/quick-and-easy-console-logging-trace.html Дякую за чудове рішення :)
Michael Crook

51

Це простий клас, який підкласами TextWriter дозволяє переспрямовувати вхідні дані як до файлу, так і до консолі.

Використовуйте його так

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

Ось клас:

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
Я думаю, це найповніше рішення. Немає необхідності перевизначати всі перевантаження методів Write / WriteLine і є прозорим для іншого коду. Тож усі дії консолі будуть продубльовані у файл, не вносячи жодних змін в інший код.
пападі

3
Дякую людино! Це приголомшливо! Я просто замінив File.Create на File.Open (шлях, FileMode.Append, FileAccess.Write, FileShare.Read); тому що я не хочу стирати старі журнали під час запуску, і я хочу мати можливість відкрити файл журналу, поки програма все ще працює.
Джон

1
Це не вимагало від мене заміни всіх існуючих дзвінків Console.WriteLine(), що було саме тим, що я хотів.
x6herbius

Для тих, хто розмахує цим, це заплутано. Шукайте Console.SetOut(doubleWriter);. Що модифікує глобальний для консолі, це зайняло у мене трохи, оскільки я так звик працювати в додатках, де практично нічого глобального немає. Хороший матеріал!
Дуглас Гаскелл,

13

Перевірте log4net . За допомогою log4net ви можете налаштувати додатки консолі та файлів, які будуть виводити повідомлення журналу в обидва місця за допомогою одного оператора журналу.


6
Ну, я думаю, що слід уникати зайвих кіл, якщо це можна зробити за допомогою того, що вже є.
Олівер Фрідріх

Я також рекомендую log4net, але схоже на те, що NLog займає своє місце у спільноті.
Марк Річман,

10

Ви не можете просто перенаправити висновок у файл, використовуючи >команду?

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

Якщо вам потрібно віддзеркалити, ви можете спробувати знайти версію win32, teeяка розділяє вихідні дані у файл.

Див. Https://superuser.com/questions/74127/tee-for-windows для запуску трійника з PowerShell


8
Мені потрібно дзеркально. Ось чому це згадується в предметі та тілі. Дякую за підказку :)
xyz

8

Ви можете підкласувати клас TextWriter, а потім призначити його екземпляр Console.Out, використовуючи метод Console.SetOut - який, зокрема, робить те саме, що передає один і той же рядок обом методам у методі журналу.

Інший спосіб може оголосити власний клас консолі та використовувати оператор using для розрізнення класів:

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

Для доступу до стандартної консолі вам знадобиться:

global::Console.Whatever

8

РЕДАГУВАТИ: Цей метод надає можливість перенаправляти інформацію консолі, що надходить із стороннього пакету. перевизначення методу WriteLine - це добре для моєї ситуації, але, можливо, вам доведеться замінити інші методи Write, залежно від стороннього пакета.

Спочатку нам потрібно створити новий клас, властивий StreamWriter, скажімо CombinedWriter;

Потім ініціюйте нову мить CombinedWriter з Console.Out;

Нарешті, ми можемо перенаправити вихід консолі на мить нового класу за допомогою Console.SetOut;

Наступний код - новий клас працює для мене.

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

Таким чином ми не пропустимо нічого, що відображається в консолі.
Продовжуйте думати

1
Ви повинні перевизначити наступні методи public override void Write(char value);, public override void Write(char[] buffer);, public override void Write(string value);і public override void Write(char[] buffer, int index, int count);. В іншому випадку він не друкується на консолі, якщо ви використовуєте WriteLine(format, ...)метод.
Дмитро Овдієнко

6

Log4net може зробити це за вас. Ви б написали лише щось подібне:

logger.info("Message");

Конфігурація визначатиме, чи надрукується надрукований файл у консолі, файлі чи в обох випадках.


4

Я вважаю, що те, що ви вже використовуєте, є найкращим підходом. Простий спосіб по суті відобразити результат.

Спочатку оголосіть глобальний TextWriter на початку:

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

Потім складіть метод для написання:

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

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

Тепер замість використання Console.WriteLine("...");використовуйте Log("...");. Просто як це. Це ще коротше!


Можуть виникнути проблеми, якщо ви змістите позицію курсору ( Console.SetCursorPosition(x, y);), але в іншому випадку це працює добре, я використовую це сам!

РЕДАГУВАТИ

Звичайно, ви можете створити метод Console.Write();таким же чином, якщо ви не використовуєте тільки WriteLines


1
Це найпростіше рішення. Не забудьте додати це в кінці вашої програми: <br/> txtMirror.Flush (); txtMirror.Close ();
Домінік Ісая

3

Як запропонував Arul, за допомогою Console.SetOutперенаправлення вихідних даних у текстовий файл можна використовувати:

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

2

Рішення використовувати клас, успадкований від StreamWriter, пропозиції користувача Keep Thinking, працює. Але мені довелося додати в конструктор base.AutoFlush = true:

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

і явний виклик деструктору:

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

В іншому випадку файл закривається раніше, ніж він записав усі дані.

Я використовую його як:

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

2

Дякую, що продовжуєте думати за відмінне рішення! Я додав деякі подальші заміни, щоб уникнути реєстрації певних подій запису консолі, які (для моїх цілей) очікуються лише для відображення консолі.

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

Будь-яка конкретна причина, через яку ви замінюєте метод Dispose, а потім викликаєте base.Dispose ()?
Адам Плочер,

1

Якщо ви дублюєте вихід консолі з коду, яким ви не керуєте, наприклад, стороння бібліотека, усі члени TextWriter повинні бути перезаписані. У коді використовуються ідеї з цієї теми.

Використання:

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

Клас 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);
        }
    }
}

0

Ви насправді можете створити прозоре дзеркальне відображення Console.Out to Trace, реалізувавши власний клас, успадкований від TextWriter, і замінивши метод WriteLine.

У WriteLine ви можете записати його в Trace, який потім можна налаштувати на запис у файл.

Я знайшов цю відповідь дуже корисною: https://stackoverflow.com/a/10918320/379132

Це насправді спрацювало для мене!


0

Моя відповідь базується на найбільш неприйнятій , а також найменш голосованій відповіді, яка, на мою думку, є найелегантнішим рішенням на сьогодні. Це трохи загальніше з точки зору типу потоку, який ви можете використовувати (ви можете використовувати MemoryStreamнаприклад), але я пропустив всю розширену функціональність, включену в останню відповідь, для стислості.

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

Використання:

using (var stream = File.Create(Path.Combine(Path.GetTempPath(), AppDomain.CurrentDomain.FriendlyName)))
using (var writer = new ConsoleMirrorWriter(stream))
{
    // Code using console output.
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.