在多个项目中使用Asp.Net Core 2注入Serilog

问题描述 投票:12回答:4

我为Asp.Net Core 2.0配置了Serilog,它在我的启动Web项目中通过.Net Core依赖注入工作得很好(如果我通过Microsoft.Extensions.Logging使用它),但我无法从任何其他项目访问它。

这就是我所拥有的:

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

启动.cs是纯默认的。我没有从新的Asp.Net Core MVC项目的原始模板中以任何方式改变它。

我原以为我可能需要在IServiceCollection服务中添加Serilog,但文章Leaner,更简单的ASP.NET核心2登录https://nblumhardt.com/2017/08/use-serilog/告诉我这是不必要的。

然后你可以继续删除任何其他记录器配置:appsettings.json中不需要“Logging”部分,任何地方都不需要AddLogging(),也不需要通过Startup.cs中的ILoggerFactory进行配置。

UseSerilog()替换内置的ILoggerFactory,以便应用程序中的每个记录器,无论是Serilog的Log类,Serilog的ILogger还是Microsoft.Extensions.Logging.ILogger,都将使用相同的Serilog实现进行备份,并通过相同的配置进行控制。

我所期待的

能够在解决方案的任何项目中的任何类中通过构造函数简单地注入记录器。例如:

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

我得到了什么 - Web(启动)项目

如果我使用Microsoft.Extensions.Logging包装器,注入在HomeController中工作:

using Microsoft.Extensions.Logging;

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

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

如果我尝试注入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:尝试激活'ObApp2.Web.Mvc.Controllers.HomeController'时无法解析类型'Serilog.ILogger'的服务。

我的更大问题 - 其他项目

我更大的问题是,如果我在解决方案中的另一个项目中工作,我无法通过我尝试过的任何方法获取记录器。

当我尝试使用Microsoft.Extensions.Logging或使用Serilog注入时,我在构建时得到一个缺少的参数异常。

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

- or -

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

两者都生成类似于的构建错误:

没有任何参数对应于'MyTestClass.MyTestClass(ILogger)'所需的形式参数'logger'

问题

  1. 注入这种类型的Serilog配置时,是否建议我在我正在进行注入的类文件中引用Microsoft.Extensions.Logging或Serilog?
  2. 如何让DI在所有项目中运作?
c# asp.net logging asp.net-core-mvc serilog
4个回答
5
投票

一种对我有用的方法:

我使用ConfigureServices方法中的AddSingleton()方法添加了一个Serilog.Core.Logger实例。这解决了DI问题。此步骤将替换在StartUp构造函数中将Logger实例分配给Log.Logger的步骤。

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

还要更改类文件中的引用以指向Serilog.Ilogger


4
投票

此解决方案正确地将记录器注入外部项目或库中的类。

我也试过Rufus建议的答案。我没有OP所描述的问题,但我有一个更奇怪的问题:在将ILogger注入外部项目后,登录到MS SQL停止工作。控制台记录工作。由于Serilog主要是静态服务,我意识到我应该把Log.Logger当作工厂。好处是您仍然可以自定义ILogger引用(例如,在构造函数中添加ForContext)而不会影响“singleton”服务。

我在github上发布了一个完整的工作解决方案,包括一个设置DI的控制台程序(将从ASP.NET Core中运行相同),一个包含Serilog入门的零除演示的外部库,以及另一个库管理Serilog作为服务。

重要的部分,Serilog服务注册,看起来像这样(作为奖励,我们绑定到ProcessExit进行透明清理):

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);
        }
    }
}

2
投票

您不能通过Net Core中的DI访问Serilog。这是一个静态的。在Program.Main()中正确配置和初始化Logger后,只需像启动,控制器等中的任何其他静态访问即

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

或者在顶部添加:

using static Serilog.Log;

简单地说:

Information("keystroke saving");


1
投票

我实际上一直在努力与相同的设置,但我设法让它工作。解决方案与您的代码示例在这些方面略有不同:

  • 这取决于Microsoft.Extensions.Logging.ILogger
  • 记录器使用ILogger<Type>注入

您的控制器代码已经符合这些要求,这就是为什么它确实有效。

using Microsoft.Extensions.Logging;

public class HomeController : Controller
{
    ILogger _logger;

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

对于其他项目中的其他类,只需确保依赖于默认的ILogger接口,而不是Serilog的实现。这样做的好处是,如果您想稍后替换Serilog,您可以执行此操作,而无需在整个其他项目中删除对它的所有引用。

using Microsoft.Extensions.Logging;

public class MyBusinessObject
{
    ILogger _logger;

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

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

出于某种原因,DI仅在需要ILogger<Type>而不是ILogger时才设法实例化Logger实例。为什么这是我不知道的,也许其他人可以回答那一个。

© www.soinside.com 2019 - 2024. All rights reserved.