<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Xavier Fischer</title>
    <description>The latest articles on DEV Community by Xavier Fischer (@xfischer).</description>
    <link>https://dev.to/xfischer</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F152719%2F3060b826-87e8-47f2-87f0-667a803f93c1.jpeg</url>
      <title>DEV Community: Xavier Fischer</title>
      <link>https://dev.to/xfischer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xfischer"/>
    <language>en</language>
    <item>
      <title>Logging in a .Net Core Library</title>
      <dc:creator>Xavier Fischer</dc:creator>
      <pubDate>Sat, 27 Apr 2019 22:09:42 +0000</pubDate>
      <link>https://dev.to/xfischer/logging-in-a-net-core-library-292m</link>
      <guid>https://dev.to/xfischer/logging-in-a-net-core-library-292m</guid>
      <description>&lt;h2&gt;
  
  
  Foreword
&lt;/h2&gt;

&lt;p&gt;In this post, I'll try to explain how to setup logging (and dependency injection in the process) in a .Net Core library, and setup an application configuring listeners and filters as an example.&lt;/p&gt;

&lt;p&gt;You can Go directly to the Solution if you want.&lt;/p&gt;

&lt;h2&gt;
  
  
  The context, the mind process
&lt;/h2&gt;

&lt;p&gt;My &lt;a href="https://github.com/dem-net" rel="noopener noreferrer"&gt;DEM.Net project&lt;/a&gt; is a class library, intended to be used as a NuGet Package for any kind of .Net application (WebAPI, Console app, WinForms, other class library).&lt;/p&gt;

&lt;p&gt;At the early development stage of this project, I was using good old &lt;code&gt;Console.WriteLine()&lt;/code&gt; or &lt;code&gt;Debug.WriteLine()&lt;/code&gt; statements to give output to me, as a developer inside Visual Studio.&lt;/p&gt;

&lt;p&gt;Then when the classes were OK, I needed to write &lt;em&gt;only&lt;/em&gt; warnings in specific cases. I still could use&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;Console.WriteLine("WARNING: bla bla")&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
, but this is ugly I know you can feel it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why should I log
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;I usually log&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For troubleshooting : progress of a task, cases encountered, dumps of in-memory structures : everything that will be useful if I need to know what was running and why.&lt;/li&gt;
&lt;li&gt;To give the consuming code a basic knowledge of the progress of 
their demand. Anything that could prevent the " black box " effect : 
waiting for something to complete with no feedback.&lt;/li&gt;
&lt;li&gt;At different levels (Trace, Debug, Info, Warn, Error, Critical) : 
it's very important to prioritize what's important and what's internal 
or critical.&lt;/li&gt;
&lt;li&gt;With &lt;em&gt;categories&lt;/em&gt;, allowing logical grouping of messages.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The rule-of-thumb is : read the logs, and you should know exactly what is going on inside the code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So I have built a custom wrapper for different types of logs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Diagnostics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TraceInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// ... plus other methods for each log lvel&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I could emit logs from my code with a single line, and I knew I could migrate with no pain when I will find a solution for clean logging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Processing file &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Using

&lt;code&gt;System.Diagnostics.Trace&lt;/code&gt;

, library consumers are constrained to listen to Traces, and coded this way, there is no categories to enable them to filter out those trace logs.
- Depending on the consumer logging system, they may need to redirect my Trace output. That's a lot to ask, they may never do that.
- I need to enable logging &lt;strong&gt;without actually logging anywhere&lt;/strong&gt;. The consumer can be able to choose where the traces go, either Console, Trace, a Database, Event Logs, File, ...
- I need to find a solid solution, where there is no custom hand-coded class, I want to be pro.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;ILogger&amp;lt;T&amp;gt;&lt;/code&gt; interface
&lt;/h3&gt;

&lt;p&gt;This interface resides in the NuGet package &lt;code&gt;Microsoft.Extensions.Logging.Abstractions&lt;/code&gt;, which is part of &lt;a href="https://github.com/aspnet/Extensions" rel="noopener noreferrer"&gt;ASP.Net Extensions catalog&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;.NET APIs for commonly used programming patterns and utilities, such as dependency injection, logging, and configuration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is not tied to ASP.Net alone in any way. We can use it in our library!&lt;/p&gt;

&lt;p&gt;This interface is simple and allows log calls to be made without having a concrete implementation. Don't dive into the internals, it's made for ease of use. Just remember that the generic type T is the type of the service for which the logging is made. Remember T as T*YourService*.&lt;/p&gt;

&lt;p&gt;Let's setup :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A library with a simple service. We want this service to log its activity.&lt;/li&gt;
&lt;li&gt;A Console app referencing the library and calling the service.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1. Starting point : a class library with your &lt;code&gt;MyService&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Create a new Class Library project &lt;code&gt;MyNetStandardLib&lt;/code&gt; targeting .Net Standard 2.0.&lt;/p&gt;

&lt;p&gt;Your service takes a param and simulates a long processing task for 1 second.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Threading&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MyNetStandardLib&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyService&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DoSomething&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;theParam&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Simulate something we do for 1 second&lt;/span&gt;
            &lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// done&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Add package Microsoft.Extensions.Logging.Abstractions
&lt;/h3&gt;

&lt;p&gt;You can do it by right clicking your project and choosing &lt;em&gt;Add NuGet Packages...&lt;/em&gt; or via dotNet CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nuget add Microsoft.Extensions.Logging.Abstractions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Add support for &lt;code&gt;ÌLogger&amp;lt;TYourService&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Change the service class as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a constructor taking an &lt;code&gt;ILogger&amp;lt;MyService&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add a backing field for the logger
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// backing field&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// constructor&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DoSomething&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;theParam&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"DoSometing with &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;theParam&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Simultate something me do for 1 second&lt;/span&gt;
        &lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Test logs at each level&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;LogTrace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DoSometing TRACE message"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;LogDebug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DoSometing DEBUG message"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DoSometing INFO message"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;LogWarning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DoSometing WARN message"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DoSometing ERROR message"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;LogCritical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DoSometing CRITICAL message"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, apart from the new &lt;em&gt;Microsoft.Extensions.Logging.Abstractions&lt;/em&gt; dependency, nothing has changed from a caller perspective.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a program was calling &lt;code&gt;MyService&lt;/code&gt; it still can do it after recompilation because the &lt;code&gt;ILogger&lt;/code&gt; is optional.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;null&lt;/code&gt; is passed for the &lt;code&gt;ÌLogger&lt;/code&gt;, the service still works thanks to the null check operator &lt;code&gt;_logger&lt;/code&gt;&lt;strong&gt;?&lt;/strong&gt;&lt;code&gt;.LogInformation()&lt;/code&gt; which is equivalent to:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that if you are migrating an existing service from your code that  already has a constructor with parameters, your could pass an optional ILogger as the last parameter.&lt;/p&gt;

&lt;p&gt;The ILogger passed as a parameter is perfectly suited for dependency injection. With dependency injection, we can register a concrete implementation of the Logger on the caller side. Let's do it the .Net way!&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The caller application, using dependency injection
&lt;/h3&gt;

&lt;p&gt;So, now we have a class library that is logging. How can connect the wires ?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;ASP.Net Core has already a DI container and could call our library directly. But this is very well documented yet and falls outside the scope of this article.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We are going to create a simple Console App. Follow those steps, in order to setup dependency injection and logging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new Console App,&lt;/li&gt;
&lt;li&gt;Add a project reference to &lt;code&gt;MyNetStandardLib&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;Add a NuGet package &lt;code&gt;Microsoft.Extensions.DependencyInjection&lt;/code&gt;. This is the Microsoft DI container. There are plenty of others (AutoFac, StructureMap, Ninject among others), but I'll stick to the .Net Foundation stack.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So let's change the &lt;code&gt;Program.cs&lt;/code&gt; file from this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;TestConsole&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.DependencyInjection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;TestConsole&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Program with Dependency Injection&lt;/span&gt;
    &lt;span class="c1"&gt;// Still no logging support!&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Setting up dependency injection&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;serviceCollection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ServiceCollection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serviceCollection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serviceCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuildServiceProvider&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// Get an instance of the service&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;myService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyNetStandardLib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// Call the service (logs are made here)&lt;/span&gt;
            &lt;span class="n"&gt;myService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DoSomething&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Register service from the library&lt;/span&gt;
            &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTransient&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyNetStandardLib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's explain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We create the DI container &lt;code&gt;ServicesCollection&lt;/code&gt; and configure it in a separate method for clarity.&lt;/li&gt;
&lt;li&gt;We register our &lt;code&gt;MyService&lt;/code&gt; as a Transient (ie: short lived instance within the calling code variable's scope)&lt;/li&gt;
&lt;li&gt;When we call &lt;code&gt;var myService = serviceProvider.GetService&amp;lt;MyNetStandardLib.MyService&amp;gt;();&lt;/code&gt; the DI container will instanciate the &lt;code&gt;MyService&lt;/code&gt; and will check the dependency tree for the object and pass required instances that are registered within the container. As we have still not registered any &lt;code&gt;ILogger&lt;/code&gt;, a &lt;code&gt;null&lt;/code&gt; will be passed.&lt;/li&gt;
&lt;li&gt;We can use the &lt;code&gt;myService&lt;/code&gt; variable as usual, calling the &lt;code&gt;DoSomething&lt;/code&gt; service&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Add Logging support
&lt;/h3&gt;

&lt;p&gt;We will add &lt;strong&gt;Console&lt;/strong&gt; and &lt;strong&gt;Debug&lt;/strong&gt; loggers. For a Console App, this will be useful because we will see logs in the console and in the &lt;em&gt;Output&amp;gt;Debug&lt;/em&gt; window.&lt;/p&gt;

&lt;p&gt;Add the following NuGet packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Extensions.Logging&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Extensions.Logging.Console&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.Extensions.Logging.Debug&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add the following &lt;code&gt;using&lt;/code&gt; statements at the top of &lt;code&gt;Program.cs&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Logging&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Logging.Console&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Logging.Debug&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the &lt;code&gt;ConfigureServices&lt;/code&gt; method as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddLogging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDebug&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Log to debug (debug window in Visual Studio or any debugger attached)&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddConsole&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Log to console (colored !)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoggerFilterOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddFilter&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DebugLoggerProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="cm"&gt;/* category*/&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Information&lt;/span&gt; &lt;span class="cm"&gt;/* min level */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddFilter&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConsoleLoggerProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;  &lt;span class="cm"&gt;/* category*/&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Warning&lt;/span&gt; &lt;span class="cm"&gt;/* min level */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTransient&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyNetStandardLib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Register service from the library&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Very, &lt;strong&gt;very&lt;/strong&gt; neat and powerful. Let's explain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;services.AddLogging&lt;/code&gt; allows us to add &lt;em&gt;various&lt;/em&gt; loggers to the DI container and configure their options. Here we register the &lt;em&gt;Console&lt;/em&gt; and &lt;em&gt;Debug&lt;/em&gt; loggers with &lt;code&gt;AddConsole()&lt;/code&gt; and &lt;code&gt;AddDebug()&lt;/code&gt;: two extensions methods provided by their respective NuGet packages.&lt;/li&gt;
&lt;li&gt;Then we configure (optionally) their filter level.

&lt;ul&gt;
&lt;li&gt;The first parameter is the category: not very well documented, it acts as a &lt;em&gt;StartsWith&lt;/em&gt; or &lt;em&gt;Contains&lt;/em&gt; filter on the type name &lt;code&gt;T&lt;/code&gt; of &lt;code&gt;ILogger&amp;lt;T&amp;gt;&lt;/code&gt;, allowing to filter which logs we want to listen or mute.&lt;/li&gt;
&lt;li&gt;The second parameter takes a &lt;code&gt;LogLevel&lt;/code&gt;, allowing to listen only when log have a lower greater or equal than the specified parameter.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This configuration can also be made via &lt;a href="https://docs.microsoft.com/fr-fr/aspnet/core/fundamentals/configuration/options?view=aspnetcore-2.2" rel="noopener noreferrer"&gt;configuration files&lt;/a&gt;.&lt;br&gt;
Now, every time a &lt;code&gt;ILogger&amp;lt;T&amp;gt;&lt;/code&gt; is required, as in our &lt;code&gt;MyService&lt;/code&gt; constructor, an instance will be injected, and this instance will log to the destinations registered.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Test run
&lt;/h3&gt;

&lt;p&gt;After the Console App launch, this is what we see in the Console window. Note that only Warn, Error and Critical logs are written, as configured:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fheliirdv9yg7nrttd45r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fheliirdv9yg7nrttd45r.png" alt="Console output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this is what we see in the Debug window, note that Info level is also written, as configured:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fysfs05i359jaji0gngc6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fysfs05i359jaji0gngc6.png" alt="Debug output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We have seen how powerful Dependency Injection is, and how we can build a library that supports logging without constraining the calling application.&lt;/p&gt;

&lt;p&gt;You can find all the source files for this project here (Tested on VS2019, both Windows and Mac) : &lt;a href="https://github.com/xfischer/CoreLoggingTests" rel="noopener noreferrer"&gt;https://github.com/xfischer/CoreLoggingTests&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;Those articles helped me a lot, and it's good to cite them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Official MS doc : &lt;a href="https://docs.microsoft.com/aspnet/core/fundamentals/logging" rel="noopener noreferrer"&gt;Logging in ASP.NET Core&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://microsoft.github.io/code-with-engineering-playbook/Engineering/DevOpsLoggingDetailsCSharp.html" rel="noopener noreferrer"&gt;Logging and Monitoring with .NET/C#&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
