Mise en miroir de la sortie de la console dans un fichier

88

Dans une application console C #, existe-t-il un moyen intelligent de mettre en miroir la sortie de la console dans un fichier texte?

Actuellement, je passe simplement la même chaîne à la fois Console.WriteLineet InstanceOfStreamWriter.WriteLinedans une méthode de journalisation.

xyz
la source

Réponses:

117

C'est peut-être une sorte de travail supplémentaire, mais j'irais dans l'autre sens.

Instanciez un TraceListenerpour la console et un pour le fichier journal; utilisez ensuite des Trace.Writeinstructions dans votre code au lieu de Console.Write. Il devient plus facile par la suite de supprimer le journal, ou la sortie de la console, ou d'attacher un autre mécanisme de journalisation.

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

Pour autant que je me souvienne, vous pouvez définir les écouteurs dans la configuration de l'application permettant d'activer ou de désactiver la journalisation sans toucher à la construction.

Oliver Friedrich
la source
4
C'est parfait - merci. J'étais au courant de Log4Net mais il semble mal de devoir extraire une bibliothèque pour quelque chose comme ça.
xyz
3
Je ne sais pas pourquoi ils ne font pas beaucoup de Trace - il me semble que cela devrait bien fonctionner pour la journalisation à l'échelle de la production, mais tout le monde veut utiliser une bibliothèque supplémentaire (comme log4net) pour le faire.
Coderer
Il s'agit d'une mise en miroir unidirectionnelle. Je voulais dire que si vous avez une console interactive et que vous obtenez des données de l'utilisateur et que vous souhaitez tout consigner dans un fichier, cette solution ne fonctionne pas. Malgré ce simple fait, ma question est close. Ici: stackoverflow.com/questions/3886895/…
Xaqron
6
J'ai vraiment aimé cette solution, alors j'ai fait un blog rapide à ce sujet avec un nettoyage mineur et une présentation de quelques problèmes en cours de route. mcrook.com/2014/11/quick-and-easy-console-logging-trace.html Merci pour la bonne solution :)
Michael Crook
51

Il s'agit d'une classe simple qui sous-classe TextWriter pour permettre la redirection de l'entrée vers un fichier et la console.

Utilisez-le comme ça

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

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

}
Christian
la source
5
Je pense que c'est la solution la plus complète. Il n'est pas nécessaire de remplacer toutes les surcharges des méthodes Write / WriteLine et est transparent pour l'autre code. Ainsi, toute l'activité de la console sera dupliquée dans le fichier, sans apporter de modifications à un autre code.
papadi
3
Merci mec! C'est génial! Je viens de remplacer File.Create par File.Open (chemin, FileMode.Append, FileAccess.Write, FileShare.Read); car je ne veux pas effacer les anciens journaux au démarrage et je veux pouvoir ouvrir le fichier journal pendant que le programme est toujours en cours d'exécution.
John
1
Cela ne m'a pas obligé à remplacer tous mes appels existants vers Console.WriteLine(), ce qui était exactement ce que je voulais.
x6herbius
Pour tous ceux qui se balancent qui ne savent pas comment cela fonctionne. Cherchez Console.SetOut(doubleWriter);. Ce qui modifie un global pour Console, cela m'a pris un peu, car je suis tellement habitué à travailler dans des applications où pratiquement rien n'est global. Bon produit!
Douglas Gaskell
13

Découvrez log4net . Avec log4net, vous pouvez configurer des appenders de console et de fichier qui peuvent générer des messages de journal aux deux endroits avec une seule instruction de journal.

Tvanfosson
la source
6
Eh bien, je pense que les bibliothèques supplémentaires devraient être évitées si cela peut être fait avec ce qui existe déjà.
Oliver Friedrich
Je recommande également log4net, mais il semble que NLog prend sa place dans la communauté.
Mark Richman
10

Ne pouvez-vous pas simplement rediriger la sortie vers un fichier à l'aide de la >commande?

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

Si vous avez besoin de mettre en miroir, vous pouvez essayer de trouver une version win32 de teequi divise la sortie en un fichier.

Voir /superuser/74127/tee-for-windows pour exécuter le tee à partir de PowerShell

xtofl
la source
8
J'ai besoin de refléter. C'est pourquoi il est mentionné dans le sujet et le corps. Merci pour le tuyau :)
xyz
8

Vous pouvez sous-classer la classe TextWriter, puis affecter son instance à Console.Out à l'aide de la méthode Console.SetOut - qui fait en particulier la même chose que de transmettre la même chaîne aux deux méthodes dans la méthode de journal.

Une autre façon pourrait déclarer votre propre classe de console et utiliser l'instruction using pour distinguer les classes:

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

Pour accéder à la console standard, vous aurez alors besoin de:

global::Console.Whatever
arul
la source
8

EDIT: Cette méthode offre la possibilité de rediriger les informations de la console provenant d'un package tiers. remplacer la méthode WriteLine est bon pour ma situation, mais vous devrez peut-être remplacer d'autres méthodes d'écriture en fonction du package tiers.

Nous devons d'abord créer une nouvelle classe inhérente à StreamWriter, disons CombinedWriter;

Puis lancez un nouvel instant de CombinedWriter avec Console.Out;

Enfin, nous pouvons rediriger la sortie de la console vers l'instant de la nouvelle classe par Console.SetOut;

Le code suivant est la nouvelle classe qui fonctionne pour moi.

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 à penser
la source
De cette façon, nous ne manquerons rien affiché dans la console.
Continuez à réfléchir
1
Vous devez remplacer les méthodes suivantes public override void Write(char value);, public override void Write(char[] buffer);, public override void Write(string value);et public override void Write(char[] buffer, int index, int count);. Sinon, il ne s'imprime pas sur la console si vous utilisez WriteLine(format, ...)method.
Dmytro Ovdiienko
6

Log4net peut le faire pour vous. Vous n'écririez que quelque chose comme ceci:

logger.info("Message");

Une configuration déterminera si l'impression ira à la console, au fichier ou aux deux.

kgiannakakis
la source
4

Je pense que ce que vous utilisez déjà est en quelque sorte la meilleure approche. Une méthode simple pour refléter essentiellement votre sortie.

Déclarez d'abord un TextWriter global au début:

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

Ensuite, créez une méthode pour écrire:

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

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

Maintenant, au lieu d'utiliser Console.WriteLine("...");, utilisez Log("...");. Aussi simple que cela. C'est encore plus court!


Il pourrait y avoir des problèmes si vous déplacez la position du curseur ( Console.SetCursorPosition(x, y);), mais sinon fonctionne bien, je l'utilise moi-même aussi!

ÉDITER

Bien sûr, vous pouvez créer une méthode de Console.Write();la même manière si vous n'utilisez pas uniquement WriteLines

Richard de Wit
la source
1
C'est la solution la plus simple. N'oubliez pas d'ajouter ceci à la fin de votre programme: <br/> txtMirror.Flush (); txtMirror.Close ();
Dominic Isaia
3

Comme suggéré par Arul, l'utilisation Console.SetOutpeut être utilisée pour rediriger la sortie vers un fichier texte:

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

La décision d'utiliser une classe, héritée du StreamWriter, des suggestions de l'utilisateur Keep Thinking, fonctionne. Mais j'ai dû ajouter dans la base du constructeur.AutoFlush = true:

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

et un appel explicite au destructeur:

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

Sinon, le fichier est fermé avant qu'il n'enregistre toutes les données.

Je l'utilise comme:

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

Merci à Keep Thinking pour l'excellente solution! J'ai ajouté d'autres remplacements pour éviter de consigner certains événements d'écriture de console qui (pour mes besoins) ne sont attendus que pour l'affichage de la 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
la source
Une raison particulière pour laquelle vous remplacez la méthode Dispose puis appelez base.Dispose ()?
Adam Plocher
1

Si vous dupliquez la sortie de la console à partir d'un code que vous ne contrôlez pas, par exemple une bibliothèque tierce, tous les membres de TextWriter doivent être écrasés. Le code utilise des idées de ce fil.

Usage:

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

ConsoleMirroring class

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
la source
0

Vous pouvez en fait créer une mise en miroir transparente de Console.Out to Trace en implémentant votre propre classe héritée de TextWriter et en remplaçant la méthode WriteLine.

Dans WriteLine, vous pouvez l'écrire dans Trace qui peut ensuite être configuré pour écrire dans un fichier.

J'ai trouvé cette réponse très utile: https://stackoverflow.com/a/10918320/379132

Cela a fonctionné pour moi!

Sushil
la source
0

Ma réponse est basée sur la réponse non acceptée la plus votée , ainsi que sur la réponse la moins votée qui, à mon avis, est la solution la plus élégante à ce jour. C'est un peu plus générique en termes de type de flux que vous pouvez utiliser (vous pouvez utiliser un MemoryStreampar exemple), mais j'ai omis toutes les fonctionnalités étendues incluses dans cette dernière réponse par souci de concision.

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

Usage:

using (var stream = File.Create(Path.Combine(Path.GetTempPath(), AppDomain.CurrentDomain.FriendlyName)))
using (var writer = new ConsoleMirrorWriter(stream))
{
    // Code using console output.
}
Néo
la source