Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write Console ouput to both console and file in C#?

Tags:

c#

console

I have a simple console application, and I have used Console.WriteLine in many places to display to the user the activities being performed. But, at the end I want to save all the Console output to a log file also. Currently, I have something like this:

if (!Directory.Exists(LOG_DIRECTORY)) {
    Directory.CreateDirectory(LOG_DIRECTORY);
}

long ticks = DateTime.Now.Ticks;
string logFilename = ticks.ToString() + ".txt";
string filePath = Directory.GetCurrentDirectory() + "\\" + LOG_DIRECTORY + "\\" + logFilename;
FileStream ostream = null;
StreamWriter writer = null;
TextWriter oldOut = Console.Out;

try
{
    ostream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
    writer = new StreamWriter(ostream);
}
catch (Exception ex)
{
    Console.WriteLine("Cannot open {0} for writing.", logFilename);
    Console.WriteLine(ex.Message);
    return;
}

Console.SetOut(writer);

Console.WriteLine("{0}", Directory.GetCurrentDirectory());

Console.SetOut(oldOut);
writer.Close();
ostream.Close();

Console.WriteLine("\n\nDone!");

The point is that this will print the stuff directly into the file and nothing is printed in the Console. Is there any way to workaround this? Please note that I need the Console.WriteLine output to be directly in real time to be written to the console, whereas, for writing to a log file, it can be done at the end of the program, where nearly everything else is finished.

like image 687
typos Avatar asked Dec 21 '17 11:12

typos


2 Answers

You can either create your own component to write to multiple outputs or you can use a logging tool such as NLog. This can be configured such that in the <targets> section you have something like;

<target name="debugger" xsi:type="Debugger" layout="${level}>${message} (${exception:format=ToString})"/>
<target name="console" xsi:type="ColoredConsole" layout="${date:format=dd-MM-yyyy HH\:mm\:ss} - ${message}" />
<target name="FullCSVFile" xsi:type="File"  fileName="${specialfolder:folder=LocalApplicationData}\YourApp\YourApp-${date:format=yyyy-MM-dd}.csv">
  <layout xsi:type="CsvLayout">
    <column name="Index" layout="${counter}" />
    <column name="ThreadID" layout="${threadid}" />
    <column name="Time" layout="${longdate}" />
    <column name="Severity" layout="${level:uppercase=true}" />
    <column name="Location" layout="${callsite:className=False:fileName=True:includeSourcePath=False:methodName=False}" />
    <column name="Detail" layout="${message}" />
    <column name="Exception" layout="${exception:format=ToString}" />
  </layout>
</target>

Then in the rules section, you'd have;

<logger name="*" minlevel="Debug" writeTo="console" />
<logger name="*" minlevel="Debug" writeTo="debugger" />
<logger name="*" minlevel="Debug" writeTo="FullCSVFile" />

To actually do the write operation, you'd have something like the following in your C# code;

// at a class level;
private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();

// later in code that writes to both targets...
_logger.Info("Something happened!");
like image 107
DiskJunky Avatar answered Sep 20 '22 22:09

DiskJunky


What about saving console output in variable like string array or List<string> ? Then do the File.WriteAllText(filepath, consoleOutputArray);

I mean this way:

 class Program
    {
        static void Main(string[] args)
        {
            List<string> textStorage = new List<string>();

            string exampleData = "Ford Mustang";

            Console.WriteLine(exampleData);

            SaveOutput(ref textStorage, exampleData);

            System.IO.File.WriteAllLines(@"C://Desktop//MyFolder", textStorage);

        }
        public static void SaveOutput(ref List<string> textStorage, string output)
        {
            textStorage.Add(output);
        }
    }
like image 25
john.kernel Avatar answered Sep 18 '22 22:09

john.kernel