Espelhar a saída do console em um arquivo

87

Em um aplicativo de console C #, há uma maneira inteligente de espelhar a saída do console em um arquivo de texto?

Atualmente estou apenas passando a mesma string para ambos Console.WriteLinee InstanceOfStreamWriter.WriteLineem um método de registro.

xyz
fonte

Respostas:

114

Isso pode dar mais algum trabalho, mas eu faria o contrário.

Instancie um TraceListenerpara o console e outro para o arquivo de log; depois disso, use Trace.Writeinstruções em seu código em vez de Console.Write. Depois disso, fica mais fácil remover o log, ou a saída do console, ou anexar outro mecanismo de log.

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

Pelo que me lembro, você pode definir os ouvintes na configuração do aplicativo, tornando possível ativar ou desativar o registro sem tocar na construção.

Oliver Friedrich
fonte
4
Isso é perfeito - obrigado. Eu conhecia o Log4Net, mas parece errado ter que puxar uma biblioteca para algo assim.
xyz
3
Não sei por que eles não dão muita importância ao Trace - parece que ele deve funcionar bem para registro em escala de produção, mas todo mundo quer adicionar uma biblioteca extra (como log4net) para fazer isso.
Coderer
Este é um espelhamento unilateral. Eu quis dizer se você tem um console interativo e obtém alguns dados do usuário e deseja registrar tudo em um arquivo, esta solução não funciona. Apesar deste simples fato, minha pergunta está encerrada. Aqui: stackoverflow.com/questions/3886895/…
Xaqron
5
Eu realmente gostei dessa solução, então fiz um blog rápido sobre ela com algumas pequenas limpezas e instruções de alguns empecilhos ao longo do caminho. mcrook.com/2014/11/quick-and-easy-console-logging-trace.html Obrigado pela ótima solução :)
Michael Crook
51

Esta é uma classe simples com subclasses de TextWriter para permitir o redirecionamento da entrada para um arquivo e para o console.

Use assim

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

Aqui está a aula:

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

}
cristão
fonte
5
Acho que essa é a solução mais completa. Não há necessidade de substituir todas as sobrecargas dos métodos Write / WriteLine e é transparente para o outro código. Portanto, todas as atividades do console serão duplicadas no arquivo, sem fazer nenhuma alteração em outro código.
papadi
3
Obrigado cara! É incrivel! Acabei de substituir File.Create por File.Open (path, FileMode.Append, FileAccess.Write, FileShare.Read); porque não quero apagar logs antigos na inicialização e quero poder abrir o arquivo de log enquanto o programa ainda está em execução.
João
1
Isso não exigiu que eu substituísse todas as minhas chamadas existentes para Console.WriteLine(), que era exatamente o que eu queria.
x6herbius
Para quem está passando, está confuso como isso faz isso. Procure Console.SetOut(doubleWriter);. Que é modificar um global para o Console, demorei um pouco, já que estou tão acostumado a trabalhar em aplicativos onde praticamente nada é global. Coisa boa!
Douglas Gaskell
13

Verifique log4net . Com log4net, você pode configurar o console e anexadores de arquivo que podem enviar mensagens de log para ambos os lugares com uma única instrução de log.

Tvanfosson
fonte
6
Bem, eu acho que libs extras devem ser evitados se isso puder ser feito com o que já está lá.
Oliver Friedrich
Eu também recomendo o log4net, mas parece que o NLog está tomando seu lugar na comunidade.
Mark Richman de
10

Você não pode simplesmente redirecionar a saída para um arquivo, usando o >comando?

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

Se precisar espelhar, você pode tentar encontrar uma versão win32 teeque divida a saída em um arquivo.

Consulte /superuser/74127/tee-for-windows para executar tee do PowerShell

xtofl
fonte
8
Eu preciso me espelhar. É por isso que é mencionado no sujeito e no corpo. Obrigado pela dica :)
xyz
8

Você pode criar uma subclasse da classe TextWriter e, em seguida, atribuir sua instância ao Console.Out usando o Console.SetOut método - que em particular faz a mesma coisa que passar a mesma string para ambos os métodos no método de log.

Outra maneira pode declarar sua própria classe Console e usar a instrução using para distinguir entre as classes:

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

Para acessar o console padrão, você precisará:

global::Console.Whatever
arul
fonte
8

EDIT: Este método fornece a possibilidade de redirecionar as informações do console provenientes de pacotes de terceiros. substituir o método WriteLine é bom para a minha situação, mas você pode precisar substituir outros métodos Write depende do pacote de terceiros.

Primeiro, precisamos criar uma nova classe inerente de StreamWriter, digamos CombinedWriter;

Então inicie um novo instante de CombinedWriter com Console.Out;

Finalmente, podemos redirecionar a saída do console para o instante da nova classe por Console.SetOut;

O código a seguir é a nova classe funciona para mim.

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);
    }
}
Continue pensando
fonte
Desta forma, não perderemos nada exibido no console.
Continue pensando
1
Você deve substituir seguintes métodos 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);. Caso contrário, ele não será impresso no console se você usar o WriteLine(format, ...)método.
Dmytro Ovdiienko
6

Log4net pode fazer isso por você. Você só escreveria algo assim:

logger.info("Message");

Uma configuração determinará se a impressão irá para o console, arquivo ou ambos.

kgiannakakis
fonte
4

Acho que o que você já está usando é a melhor abordagem. Um método simples para espelhar essencialmente sua saída.

Primeiro, declare um TextWriter global no início:

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

Em seguida, crie um método para escrever:

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

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

Agora, em vez de usar Console.WriteLine("...");, use Log("...");. Simples assim. É ainda mais curto!


Pode haver algum problema se você mudar a posição do cursor ( Console.SetCursorPosition(x, y);), mas de outra forma funciona bem, eu também uso!

EDITAR

Claro, você pode fazer um método Console.Write();da mesma maneira se não estiver usando apenas WriteLines

Richard de Wit
fonte
1
Esta é a solução mais simples. Não se esqueça de adicionar ao final do programa: <br/> txtMirror.Flush (); txtMirror.Close ();
Dominic Isaia
3

Conforme sugerido por Arul, o uso Console.SetOutpode ser usado para redirecionar a saída para um arquivo de texto:

Console.SetOut(new StreamWriter("Output.txt"));
hgirish
fonte
2

A decisão de usar uma classe, herdada do StreamWriter, sugestões do usuário Keep Thinking, funciona. Mas eu tive que adicionar no construtor base.AutoFlush = true:

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

e uma chamada explícita para o destruidor:

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

Caso contrário, o arquivo é fechado antes que ele registrou todos os dados.

Estou usando como:

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

Obrigado, Continue Pensando pela excelente solução! Eu adicionei algumas substituições adicionais para evitar o registro de certos eventos de gravação do console que (para meus propósitos) são esperados apenas para exibição no 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();
        }
    }
}
user2789183
fonte
Algum motivo específico para substituir o método Dispose e, em seguida, chamar base.Dispose ()?
Adam Plocher
1

Se você duplicar a saída do console de um código que não controla, por exemplo, uma biblioteca de terceiros, todos os membros do TextWriter devem ser substituídos. O código usa ideias deste tópico.

Uso:

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

Classe 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);
        }
    }
}
Crabba
fonte
0

Na verdade, você pode criar um espelhamento transparente de Console.Out to Trace implementando sua própria classe herdada de TextWriter e substituindo o método WriteLine.

No WriteLine, você pode gravá-lo no Trace, que pode então ser configurado para gravar no arquivo.

Achei esta resposta muito útil: https://stackoverflow.com/a/10918320/379132

Na verdade funcionou para mim!

Sushil
fonte
0

Minha resposta é baseada na resposta mais votada não aceita , e também na resposta menos votada que eu acho que é a solução mais elegante até agora. É um pouco mais genérico em termos do tipo de fluxo que você pode usar (você pode usar um, MemoryStreampor exemplo), mas omiti todas as funcionalidades estendidas incluídas na última resposta para abreviar.

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

Uso:

using (var stream = File.Create(Path.Combine(Path.GetTempPath(), AppDomain.CurrentDomain.FriendlyName)))
using (var writer = new ConsoleMirrorWriter(stream))
{
    // Code using console output.
}
Neo
fonte