web-dev-qa-db-de.com

Verwenden von Asp.Net Core 2 Injection für Serilog mit mehreren Projekten

Ich habe Serilog für Asp.Net Core 2.0 konfiguriert und es funktioniert hervorragend über .Net Core-Abhängigkeitseinspritzung in meinem Startprojekt (wenn ich es über Microsoft.Extensions.Logging verwende), aber ich kann von keinem anderen Projekt darauf zugreifen.

Folgendes habe ich:

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 ist reine Standardeinstellung. Ich habe es in keiner Weise von der ursprünglichen Vorlage für das neue Asp.Net Core MVC-Projekt geändert.

Ich habe gedacht, dass ich möglicherweise Serilog in den IServiceCollection-Services hinzufügen muss, aber der Artikel Leaner, gemeint ist ASP.NET Core 2-Protokollierung unter https://nblumhardt.com/2017/08/use- serilog/ sagt mir, dass es unnötig ist.

Sie können dann fortfahren und jede andere Logger-Konfiguration löschen, die .__ ist. Herumhängen: In .__ ist kein Abschnitt "Logging" erforderlich. appsettings.json, kein AddLogging () irgendwo und keine Konfiguration über ILoggerFactory in Startup.cs.

UseSerilog () ersetzt die eingebaute ILoggerFactory, sodass jeder Logger Geben Sie in Ihrer Anwendung an, ob es sich um die Protokollklasse von Serilog handelt. ILogger oder Microsoft.Extensions.Logging.ILogger wird gesichert mit derselben Serilog-Implementierung und durch dieselbe .__ gesteuert. Aufbau.

Was ich erwartet habe

Die Möglichkeit, einen Logger einfach über einen Konstruktor in einer beliebigen Klasse in einem beliebigen Projekt in der Lösung einzufügen. Zum Beispiel:

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

Was ich habe - Webprojekt (Startup)

Injection funktioniert im HomeController, wenn ich den Wrapper Microsoft.Extensions.Logging verwende:

using Microsoft.Extensions.Logging;

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

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

Die Injektion schlägt in keinem Projekt/Klasse fehl, wenn ich versuche, Serilog.ILogger zu injizieren

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: Dienst .__ kann nicht aufgelöst werden. für den Typ 'Serilog.ILogger' beim Versuch, .__ zu aktivieren. "ObApp2.Web.Mvc.Controllers.HomeController".

Mein größeres Problem - AndereProjekte

Mein größeres Problem ist, dass ich über DI keine Methode bekommen kann, die ich ausprobiert habe, wenn ich in einem anderen Projekt der Lösung arbeite.

Wenn ich versuche, entweder mit Microsoft.Extensions.Logging oder mit Serilog zu injizieren, erhalte ich zur Buildzeit eine fehlende Parameterausnahme.

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

- or -

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

Beide erzeugen einen Build-Fehler ähnlich wie:

Es wird kein Argument angegeben, das dem erforderlichen formalen .__ entspricht. Parameter 'Logger' von 'MyTestClass.MyTestClass (ILogger)'

Fragen

  1. Wird bei der Injektion dieser Art von Serilog-Konfiguration empfohlen, auf Microsoft.Extensions.Logging oder Serilog in den Klassendateien zu verweisen, in denen ich die Injektion durchführe?

  2. Wie kann ich DI dazu bringen, alle Projekte durchzuarbeiten?

10
platypusjh

Eine Methode, die für mich funktioniert hat:

Ich habe eine Instanz von Serilog.Core.Logger unter Verwendung der AddSingleton () - Methode in der ConfigureServices-Methode hinzugefügt. Dies löste das DI-Problem. Dieser Schritt ersetzt den Schritt des Zuweisens einer Logger-Instanz zu Log.Logger im StartUp-Konstruktor.

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

Ändern Sie auch die Referenzen in Ihren Klassendateien, um auf Serilog.Ilogger zu verweisen

4
Rufus Lobo

Diese Lösung fügt den Logger korrekt in Klassen in einem externen Projekt oder einer externen Bibliothek ein.

Ich habe auch die Antwort von Rufus ausprobiert. Ich hatte das vom OP beschriebene Problem nicht, aber ich hatte ein noch komischeres Problem: Nachdem ILogger in das externe Projekt eingefügt wurde, funktionierte die Protokollierung in MS SQL nicht mehr. Konsolenprotokollierung hat funktioniert. Da Serilog hauptsächlich ein statischer Dienst ist, wurde mir klar, dass ich Log.Logger als Fabrik behandeln sollte. Der Vorteil ist, dass Sie ILogger-Referenzen noch anpassen können (z. B. ForContext im Konstruktor hinzufügen), ohne den "Singleton" -Dienst zu beeinflussen.

Ich habe eine voll funktionsfähige Lösung auf github veröffentlicht, die ein Konsolenprogramm enthält, das DI einrichtet (würde auch von ASP.NET Core aus funktionieren), eine externe Bibliothek, die die Serilog-Demo "Erste Schritte mit Nullen" enthält und eine andere Bibliothek, die Serilog als Dienst verwaltet. 

Der wichtige Teil, die Serilog-Serviceregistrierung, sieht folgendermaßen aus (und als Bonus binden wir an ProcessExit für eine transparente Bereinigung):

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

Sie greifen nicht über DI in Net Core auf Serilog zu. Es ist eine statische. Nachdem Sie den Logger korrekt in Ihrem Program.Main () konfiguriert und initialisiert haben, greifen Sie einfach wie jedes andere statische Element in Ihrem Startup, Controller usw. zu, d. H.

public void ConfigureServices(IServiceCollection services)
            {
    Serilog.Log.Information("here I am in startup");
    //further options
};

oder oben hinzufügen:

using static Serilog.Log;

und einfach:

Information("keystroke saving");

2
gorytus

Eigentlich hatte ich Probleme mit dem gleichen Setup, aber ich habe es geschafft, es zum Laufen zu bringen. Die Lösung unterscheidet sich in folgenden Punkten nur geringfügig von Ihren Codebeispielen:

  • Es kommt auf die Microsoft.Extensions.Logging.ILogger.
  • Der Logger wird mit ILogger<Type>

Ihr Controller-Code entspricht bereits diesen Anforderungen, weshalb dies funktioniert hat.

using Microsoft.Extensions.Logging;

public class HomeController : Controller
{
    ILogger _logger;

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

Stellen Sie für andere Klassen in anderen Projekten nur sicher, dass Sie von der ILogger-Standardschnittstelle und nicht von der Serilog-Implementierung abhängig sind. Dies hat auch den Vorteil, dass Sie, wenn Sie Serilog später ersetzen möchten, dies tun können, ohne alle Verweise darauf auch in Ihren anderen Projekten entfernen zu müssen.

using Microsoft.Extensions.Logging;

public class MyBusinessObject
{
    ILogger _logger;

    public MyBusinessObject(ILogger<MyBusinessObject> logger)
    {
        _logger = logger;
    }

    public void DoBusinessLog()
    {
        _logger.Information("Executing Business Logic");
    }
}

Aus irgendeinem Grund kann DI eine Logger-Instanz nur instanziieren, wenn ein ILogger<Type> aber nicht für ILogger. Warum das genau ist, weiß ich nicht, vielleicht kann jemand anderes darauf antworten.

1