0

I have a console application that will execute once every day. (Using task scheduler in Windows.) It generates output to the console, which is useful during debugging, but the automated task will not save it. And while I could set the task to redirect the output to a file, I would rather prefer this logging to be written to the console and a text file. (Which would have a timestamp as name.)

Now, I could just use a separate text stream for this file, and write information to it, but that means duplicating every Write/WriteLine statement twice. That would be a bad design.

So, how could I reduce this to just one Write/WriteLine statement that would write the text to both the console and to a file?

I am considering using the ILogger interface, but this console application is about 70 lines of code, all of it straight to the point. (About four things that it processes.) Besides, the ILogger interface adds filtering based on the log level. I just need everything to be logged so ILogger is overkill for my project.

I do not want to redirect the console output! I want the console stream to be copied to a second stream.

Does .NET 7.0 has some new features that I could use for this purpose?


Just to be clear: I Do NOT need a logger! There are no log levels or anything. Normally, I would use the application and it writes what it's doing to the console. But as a scheduled task, this console becomes invisible so I need an exact carbon copy. This would be similar to using the tee command on the commandline, but I'm looking for a method inside the code that does not rely on any commandline parameters.


I found an answer at Mirroring console output to a file which is quite old, yet should still work. Closing this now as it's a duplicate question.

Wim ten Brink
  • 25,901
  • 20
  • 83
  • 149
  • 2
    Make a function that write to both places? If your application is 70 lines you might be overcomplicating things. – gunr2171 Jan 05 '23 at 23:07
  • @gunr2171 A single function to write things would work. It's just not very elegant, as I might want to use this solution for other projects. – Wim ten Brink Jan 05 '23 at 23:21
  • 1
    How do you define "elegant"? This seems like a subjective question. – gunr2171 Jan 06 '23 at 00:13
  • 1
    @WimtenBrink I'd argue that a single function is very elegant. If it requires a couple hundred lines of codes and a dll that is imported, then yes, it might not be 'elegant'. You have the option to define Code Snippets in Visual Studio and just insert that code into any project making a single function very elegant, at least in my opinion –  Jan 06 '23 at 00:16
  • 3
    BTW, what you are trying to do is traditionally known as a `tee` (i.e., you pipe the output to the _tee_ in the pipe, one branch goes to a file, the other through to `stdout`). There are `tee` utilities around if you just want to do this quickly on the command line (yes, I know you said you didn't) – Flydog57 Jan 06 '23 at 00:23

1 Answers1

3

Serilog. It does everything you are asking for:

  1. Write to your console anytime you have a visible console (adds colors per type of message: info, warn, error, etc.)
  2. Writes to a text file specified at startup.cs/program.cs and it's easily customizable.
  3. You can still use the ILogger interface and it will bind to that so you don't have to deal with Serilog dependencies all over the place.

Here's a quick sample:

Setup on Program.cs

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .WriteTo.File(
        path: directory + "\\logs\\info.log", 
        restrictedToMinimumLevel: LogEventLevel.Information, 
        rollingInterval: RollingInterval.Day,
        fileSizeLimitBytes: logSizeLimitBytes)
    .WriteTo.File(
        path: directory + "\\logs\\error.log", 
        restrictedToMinimumLevel: LogEventLevel.Error, 
        rollingInterval: RollingInterval.Day,
        fileSizeLimitBytes: logSizeLimitBytes)
    .CreateLogger();

Regular usage on other classes/controllers:

Logger.LogInformation("Started order data movement");
var startTime = DateTime.Now;
await CreateAsync();
await UpdateAsync();
Logger.LogInformation("Finished order data movement. Elapsed time: {duration}", (DateTime.Now - startTime).ToReadableUnit());

Logger is ILogger

Also, there's nothing wrong with having 70 lines of working code and maybe 30-20 on logging lines. You can never log enough. On mayday, it's better to have too much logging than not having or having too little.

mrbitzilla
  • 368
  • 3
  • 11