web-dev-qa-db-fra.com

Utilisation de Asp.Net Core 2 Injection pour Serilog avec plusieurs projets

J'ai Serilog configuré pour Asp.Net Core 2.0 et cela fonctionne très bien via l'injection de dépendance .Net Core dans mon projet Web de démarrage (si je l'utilise via Microsoft.Extensions.Logging), mais je ne peux pas y accéder depuis un autre projet.

Voici ce que j'ai

Program.cs

using System;
using System.IO;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Serilog;

namespace ObApp.Web.Mvc
{
    public class Program
    {
        public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
            .Build();

        public static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(Configuration)
                .CreateLogger();

            try
            {
                Log.Warning("Starting BuildWebHost");

                BuildWebHost(args).Run();
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Host terminated unexpectedly");
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .UseSerilog()
                .Build();
    }
}

Startup.cs

Startup .cs est un logiciel par défaut. Je ne l'ai en aucune manière modifié par rapport au modèle d'origine du nouveau projet Asp.Net Core MVC.

J'ai pensé que je pourrais avoir besoin d'ajouter Serilog dans les services IServiceCollection, mais l'article Leaner, signifiant de la journalisation ASP.NET Core 2} à l'adresse https://nblumhardt.com/2017/08/use- serilog/ me dit que c'est inutile.

Vous pouvez ensuite continuer et supprimer toute autre configuration de consignateur qui est traîner: il n’est pas nécessaire de créer une section "Enregistrement" dans appsettings.json, aucun AddLogging () n’importe où, et aucune configuration via ILoggerFactory dans Startup.cs.

UseSerilog () remplace ILoggerFactory intégré de sorte que chaque enregistreur dans votre application, qu’il s’agisse de la classe Log de Serilog, du fichier .il. ILogger ou Microsoft.Extensions.Logging.ILogger sera sauvegardé avec la même implémentation Serilog, et contrôlée par la même configuration.

Ce que j'attendais

La possibilité d'injecter simplement un logger via un constructeur dans n'importe quelle classe de n'importe quel projet de la solution. Par exemple:

using Serilog;
public MyTestClass(ILogger logger) { ... }

Ce que j'ai obtenu - Projet Web (démarrage)

L'injection fonctionne dans le HomeController si j'utilise le wrapper Microsoft.Extensions.Logging:

using Microsoft.Extensions.Logging;

public class HomeController : Controller
{
    ILogger<HomeController> _logger;

    public HomeController(ILogger<HomeController> logger)
    {
        _logger = logger;
        _logger.LogDebug("Controller instantiated.");
    }
}

L'injection échoue dans n'importe quel projet/classe si j'essaie d'injecter Serilog.ILogger

using Serilog;
using Serilog.Extensions.Logging; // Not sure if this would help.

public class HomeController : Controller
{
    ILogger _logger;

    public HomeController(ILogger logger)
    {
        _logger = logger;
        _logger.Debug("Controller instantiated.");
    }
}

InvalidOperationException: Impossible de résoudre le service pour le type 'Serilog.ILogger' en essayant d'activer "ObApp2.Web.Mvc.Controllers.HomeController".

Mon plus gros problème - AutreProjets

Mon plus gros problème est que je ne peux pas obtenir un enregistreur via DI par le biais d'une méthode que j'ai déjà utilisée si je travaille dans un autre projet de la solution.

Lorsque j'essaie d'injecter à l'aide de Microsoft.Extensions.Logging ou de Serilog, je reçois une exception de paramètre manquant au moment de la génération.

using Microsoft.Extensions.Logging;
namespace ObApp.Domain
{
    public class MyTestClass(ILogger<MyTestClass> logger) { ... }

- or -

using Serilog;
namespace ObApp.Domain
{
    public class MyTestClass(ILogger logger) { ... }

Les deux génèrent une erreur de construction semblable à:

Il n'y a pas d'argument donné qui correspond au formel requis paramètre 'enregistreur' de 'MyTestClass.MyTestClass (ILogger)'

Des questions

  1. Lors de l'injection avec ce type de configuration Serilog, est-il recommandé de faire référence à Microsoft.Extensions.Logging ou Serilog dans les fichiers de classe où l'injection est effectuée?

  2. Comment puis-je faire en sorte que DI travaille avec tous les projets?

10
platypusjh

Une méthode qui a fonctionné pour moi:

J'ai ajouté une instance de Serilog.Core.Logger à l'aide de la méthode AddSingleton () de la méthode ConfigureServices. Cela a résolu le problème de DI. Cette étape remplace l'étape d'affectation d'une instance de journal à Log.Logger dans le constructeur de démarrage.

services.AddSingleton((ILogger)new LoggerConfiguration()
            .MinimumLevel.Information()
            .WriteTo.File(<...>)
            .CreateLogger());

Modifiez également les références dans vos fichiers de classe pour qu'elles pointent vers Serilog.Ilogger.

4
Rufus Lobo

Cette solution injecte correctement l'enregistreur dans les classes d'un projet ou d'une bibliothèque externe.

J'ai aussi essayé la réponse suggérée par Rufus. Je n'avais pas le problème décrit par l'OP, mais j'avais un problème encore plus étrange: après que ILogger ait été injecté dans le projet externe, la connexion à MS SQL a cessé de fonctionner. La journalisation de la console a fonctionné. Étant donné que Serilog est principalement un service statique, j'ai compris que je devrais traiter Log.Logger en tant qu'usine. L'avantage est que vous pouvez toujours personnaliser les références ILogger (par exemple, ajouter ForContext dans le constructeur) sans affecter le service "singleton".

J'ai posté une solution de travail complète sur github , y compris un programme de console qui configure DI (fonctionnerait de la même manière à partir d'ASP.NET Core), une bibliothèque externe contenant la démo de démarrage par Serilog, divisée par zéro et une autre bibliothèque qui gère Serilog en tant que service. 

L'important, l'enregistrement du service Serilog, ressemble à ceci (et en prime, nous associons ProcessExit pour un nettoyage transparent):

using Microsoft.Extensions.DependencyInjection;
using System;

namespace Serilog.Injection
{
    public static class RegisterSerilogServices
    {
        public static IServiceCollection AddSerilogServices(this IServiceCollection services)
        {
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Verbose()
                .WriteTo.Console()
                .WriteTo.MSSqlServer(@"xxxxxxxxxxxxx", "Logs")
                .CreateLogger();

            AppDomain.CurrentDomain.ProcessExit += (s, e) => Log.CloseAndFlush();

            return services.AddSingleton(Log.Logger);
        }
    }
}
3
McGuireV10