Background

Reference: c# - Why should Console.WriteLine be avoided in library code? - Stack Overflow

Do not use the Console class to display output in unattended applications, such as server applications. Calls to methods such as Console.Write and Console.WriteLine have no effect in GUI applications.

Using Console.WriteLine in a library tightly couples your library to stdout, and assumes that the calling code (the application consuming your library) is paying attention to stdout. What if your library code is invoked by someone's unit test suite? Or on a web server that is using a different logging paradigm?

Reference: https://en.wikipedia.org/wiki/Dependency_inversion_principle

  1. High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces).
  2. Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.

Console is a service for your app which depends on. It is a low-level module.

While logging is a requirement that helps developer to debug. It is has relationship with your business and is a high level module.

So logging should not depend on Console. Instead, there is some abstractions like: ILogger. Which describes something that can provide a logging service.

Reasons

  • You can't assume that Console is always consumed and read.
  • You need to rebuild your project to support more logging service like File\ApplicationInsights\Database logging.
  • You should follow the dependency inversion principle to not depend on a low-level module like Console.WriteLine.
  • GUI app has no effect. But may provide other logging window like output window.
  • Console logging is hard to track\diagnose for server-side apps when scaling out.
  • It's hard for you to format the log with level, timestamp and origin.

Solutions

  • Use ILogger instead of Console

  • Use

    ILogger.LogInformation
    ILogger.LogCritical
    ILogger.LogDebug
    ILogger.LogInformation
    ILogger.LogTrace
    ILogger.LogWarning

However, ILogger is an interface that can NOT be used like Console directly. How can you get a logger that redirects all your log to console?

Get a console logger which implements ILogger

First, reference those three packages in your project:

    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
    <PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="5.0.0" />

Create a new entry program:

using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace MyNameSpace
{
    public class Entry
    {
        private readonly ILogger<Entry> logger;

        public Entry(ILogger<Entry> logger)
        {
            this.logger = logger;
        }

        public async Task StartEntry()
        {
            this.logger.LogInformation($"Program started. Hello world!");
        }
    }
}

From your main program, create a service collection like this:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyNameSpace
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var services = new ServiceCollection();
            services.AddLogging(logging => {
                logging.AddConsole();
                logging.SetMinimumLevel(LogLevel.Information);
            });

            // services.AddSingleton<CannonQueue>(); // You can add your other services like this.
            services.AddSingleton<Entry>();

            var serviceProvider = services.BuildServiceProvider();
            var entry = serviceProvider.GetRequiredService<Entry>();

            await entry.StartEntry();
        }
    }
}

And since you have a dependency framework, you can also register other services like EntityFramework\MemoryCache\HTTPService\RetryService, etc.

And your entry can also get services from other dependencies.

The logger will automatically redirect the log to console.

And your other services like Entity Framework will also use that logging service to log.

Enjoy!

And you can also log with levels:

Or auto submit the log to Application Insights:

<PackageReference Include="Microsoft.Extensions.Logging.ApplicationInsights" Version="2.18.0" />

            services.AddLogging(builder =>
                {
                    builder.AddConsole();
                    builder.AddApplicationInsights(instrumentationKey);
                })

Minimum code to get a logger

If you insist not to use the dependency injection, you can do it this way:

        static void Main(string[] args)
        {
            var logger = LoggerFactory.Create(logging => logging.AddConsole()).CreateLogger<Program>();

            logger.LogInformation($"Hello world!");
        }

Is there any case that I can use Console class?

Actually yes. If you believe that you are not doing something like logging. But instead really need to interact with console, you can use it.

For example:

  • When you are building a terminal UI application that never want the STDOUT to go to other places besides the console.
  • When you are implementing the ILogger to support the console logging.