Using Dotnet Core Logging without using the DI container

Just before Christmas, one of our regular contributors to Lidnug posed the question on how to use the ILogger interface in dotnet core 3+ without injecting logger instances into classes used within a dotnet core application.

In his specific case he was working on a project where by the classes being consumed had parameter-less constructors, and as we all know if we can’t use parametered constructors, then we can’t inject anything into our class at runtime.

Now I happen to have an application skeleton that I use for most of my dotnet core projects, and in that skeleton I routinely use the built in logging service (Based around ILogger) with the very excellent serilog, and I have things set up in a way that allows me to use a static logging instance should I need to, but to also be able to inject loggers into classes where I am able to use the DI container built into dotnet core.

Here’s how I do it.

First off, we need to add into our project the required NuGet packages to support serilog and what we want to do with the ILogger interface, in my case it’s these 3 packages

untitled

With those 3 packages in place, we then turn our attention to the “Program.cs” code file and make the following changes.

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;

namespace theapp

{
   public class Program
   {
     public static void Main(string[] args)
     {
       Log.Logger = new LoggerConfiguration()
         .MinimumLevel.Verbose()
         .Enrich.FromLogContext()
         .WriteTo.Console(LogEventLevel.Information)
         .WriteTo.RollingFile("Logs/MainLog-{Date}.log", LogEventLevel.Verbose)
         .CreateLogger();
         
       Log.Information("[MAIN] Starting Application.");
       CreateHostBuilder(args).Build().Run();
     }
     
     public static IHostBuilder CreateHostBuilder(string[] args)
     {
       return Host.CreateDefaultBuilder(args)
         .UseSerilog()
         .ConfigureWebHostDefaults(webBuilder =>         
         {           
           webBuilder.UseStartup();         
         });     
     }   
     
  }
}





In the project I’m using to document this, I’m creating a stand alone kestrel based web application, but since this is dotnet core 3, then this will work for any project that uses the new program/startup class way of doing things.

The code should be fairly self explanatory, but essentially what were doing is attaching Serilog to a static instance of the “Microsoft.Extensions.Hosting.Log.Logger” which is a global singleton attached to the app at it’s lowest level (IE: Executing program assembly)

You can see that we also configure it here, so we have both a rolling file output and console output, mores the point, I dumb down the amount of output sent to the console, but open up the gates to send everything possible to the rolling file.

Doing this means I don’t get a massively cluttered console output, but when there is something I need to investigate, then I know the log files will have to complete story.

There is nothing I need to add to my startup class, and as you can see from the next bit of code, I can continue to use the static “Log” instance in there without DI injection or anything.

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using System;
using System.Linq;
 
namespace theapp
{   
  public class Startup   
  {     
    public IConfiguration Configuration { get; }     
    public IWebHostEnvironment HostEnvironment { get; }     
    
    public Startup(IConfiguration configuration, IWebHostEnvironment hostEnvironment)     
    {       
      Log.Information("[Startup] (CTOR)");       
      Configuration = configuration;       
      HostEnvironment = hostEnvironment;     
    }     
    
    public void ConfigureServices(IServiceCollection services)     
    {       
      Log.Information("[Startup] (ConfigureServices)");       
      
      if (!HostEnvironment.IsDevelopment())       
      {         
        if (!HostEnvironment.IsStaging())         
        {           
          if (!HostEnvironment.IsProduction())           
          {             
            throw new Exception("Invalid Hosting Environment!!!  Application Aborting");           
          }         
        }       
      }       
      
      // Other services     
    }     
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)     
    {       
      if (env.IsDevelopment())       
      {         
        Log.Information($"[Startup] Running in development mode.");         
        app.UseDeveloperExceptionPage();       
      }       
      else       
      {         
        Log.Information($"[Startup] Running in stage or production mode.");         
        app.UseExceptionHandler("/Error");         
        
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.         
        app.UseHsts();         
        app.UseHttpsRedirection();       
      }       
      
      app.UseStaticFiles();       
      app.UseRouting();     
      
    }   
    
  }
}

I now have the choice of the following, I can simply just use the logger as above

untitled

Or I can inject it in as normal using the DI container

untitled

The astute among you may have noticed that the API is different in each case, and you’d be correct, one is the raw ILogger interface, the other is serilog’s wrapper around the ILogger interface, the net result however is that in both cases, the logging info still gets sent through serilog and obeys the configuration set up in program.cs

I can’t tell you if the exact same methods work with other loggers that attach themselves to the Microsoft ILogger interface as I don’t routinely use anything else except for serilog, but since the interface is unified, and all loggers are expected to attach to it the same way, then it would make sense to me that this approach would work with other loggers that have dotnet core capabilities.

Making Blazor validation play nice with Bootstrap 4

There’s no doubt about it, blazor’s forms module and it’s validation features are fantastic.

You get all the benefits of using validation attributes on your models, along with a very fluid and real time UI model that works exactly the same in server and client modes.

There is however, one small problem with it all “Class Names

Continue reading “Making Blazor validation play nice with Bootstrap 4”

Rediscovering Postgres and EF core

Its ok folks, you can relax, yes I’m not ill, yes I have gotten around to writing another blog post, yes I know…. I don’t write enough anymore and I should 🙂

With that out the way, why am I suddenly getting all excited about Postgres and EF again (specifically EF core)

Continue reading “Rediscovering Postgres and EF core”

Pure HTML Validation in Blazor

There’s been a lot of talk about Validation in the Blazor gitter chat recently.

The Blazor dev team have added some validation routines in that closley mimic the way validation works in ASP.NET MVC and many folks have been playing with them to see what they can do with them.

Chris Sainty has even produced a blog post [https://chrissainty.com/using-fluentvalidation-for-forms-validation-in-razor-components/] showing how to wire in the “Fluent Validation” libraries to make form validation even more awesome.
Continue reading “Pure HTML Validation in Blazor”

Typescript for the C# developer

Over the past couple of years, my most popular talk that Iv’e taken around user groups has been the one where I describe what Typescript is, and how it relates to the backend C# developer.

Iv’e found that many back-end devs who would like to jump into client side development, are often put off from doing so simply beacuse of the percieved mess that the JavaScript eco system is in at present, and let’s be fair it’s not a compleatly unfounded reason either, beacuse JavaScript is bleeding at the edges in a great many places.
Continue reading “Typescript for the C# developer”

Build Automation for Dotnet Core Apps

In a previous blog post I documented how I built a “Build Server” to deploy .NET 4.6+ apps on windows 2012 server.

While this worked, and was a reasonably good way to do it, It wasn’t without it’s problems. During it’s use for example I frequently had timing problems, where just one little change to some JS code would cause NPM to overrun a time out by half a second, or where an SSH connection timed out just slightly before the build server completed it’s login, and on top of all that, it regularly used to take about 15 minutes to build and deploy the project it was being used for. Continue reading “Build Automation for Dotnet Core Apps”