如何让 Serilog 与依赖注入一起工作?

问题描述 投票:0回答:3

下面是我的日志记录代码,它与我添加的扩展方法配合得很好。但是,当我将其与 DI 一起使用时,它不起作用......为什么?

using ConsoleUI.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Context;
using System;
using System.IO;
using System.Runtime.CompilerServices;

/// <summary>
/// 1. We want to use Dependenxy Injection or DI.
/// 2. We want to use logging, but with Serilog.
/// 3. We want to use our appsettings.json file.
/// </summary>
namespace ConsoleUI
{
    partial class Program
    {
        /// <summary>
        /// This is the main application.
        /// </summary>
        /// <param name="args">These are the arguments for the main application.</param>
        static void Main(string[] args)
        {
            //Used to get the method name for logging. This is extremely slow and should not be used.
            var stackTrace = new System.Diagnostics.StackTrace();
            var methodName = stackTrace.GetFrame(0).GetMethod().Name;

            //Used to setup your application settings configuration.
            var builder = new ConfigurationBuilder();
            BuildConfig(builder); //With objects we are passing a reference to that instance. When it is modified here, it is modfied for everyone!

            //There is a way to get the method name out for Serilog, by extendeding it, but this does not work for DI when using a general type for logging.
            //https://stackoverflow.com/questions/29470863/serilog-output-enrich-all-messages-with-methodname-from-which-log-entry-was-ca

            //Setup logging for Serilog.
            Log.Logger = new LoggerConfiguration()
                    .ReadFrom.Configuration(builder.Build())
                    .MinimumLevel.Verbose()
                    .Enrich.FromLogContext()
                    .Enrich.WithMachineName()
                    .CreateLogger();

            //The first thing to gett logged in the application.
            Log.Logger.Information("{name} --> Application Starting.", methodName);

            //This will help setup Depedency Injection. It also has our logger and appsettings.json, it has everything.
            var host = Host.CreateDefaultBuilder()
                .ConfigureServices((context, services) =>
                {
                    services.AddTransient<IGreetingService,GreetingService>(); //Give me a new instance of the GreetingService every time I ask for it.
                })
                .UseSerilog()
                .Build();

            //From all the services you have, create an instance of GreetingService.
            var svc = ActivatorUtilities.CreateInstance<GreetingService>(host.Services); //Normally, you should use the interface here and not the concrete class. Research this.
            svc.Run();

            //The first thing to gett logged in the application.
            Log.Logger.Information("{name} --> Application Finished Normally.", methodName);

            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            //THIS WORKS FINE!!!!
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            Log.Logger.Here<Serilog.ILogger>().Information("Application Finished Normally.");


        }

        #region BuildConfig

        /// <summary>
        /// This will allow us to do logging, before we work with our actual configuration.
        /// </summary>
        /// <param name="builder">Our Configuration Builder.</param>
        static void BuildConfig(IConfigurationBuilder builder)
        {
            //Wherever the execitable is running, get the current directory, find file appsettings.json, and this is not option and if it changes, reload it!
            //Also - Get environment settings for the environment we are in as well. This second appsettings file will override the first one here.
            //       If APSNETCORE_ENVIRONMENT does not exist, assume this is Production and add .json at the end. If this file is not there, than that's cool, it's optional.
            //Also - If you have environmental variables, they can override what they are in appsettings.json.
            builder.SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("APSNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
                .AddEnvironmentVariables();
        }

        #endregion

    }

    public static class Extensions
    {
        public static Serilog.ILogger Here<T>(
            this Serilog.ILogger logger,
            [CallerMemberName] string memberName = "",
            [CallerFilePath] string sourceFilePath = "",
            [CallerLineNumber] int sourceLineNumber = 0)
        {
            return logger
            .ForContext("MemberName", memberName)
            .ForContext("FilePath", sourceFilePath)
            .ForContext("LineNumber", sourceLineNumber)
            .ForContext<T>();

        }
    }
}

这就是我试图通过 DI 在课堂上使用的内容。这不起作用,我不明白为什么。我需要扩展方法来向我的日志记录添加额外的上下文。

这是我收到的错误:

严重性代码描述项目文件行抑制状态 错误 CS1929 'ILogger' 不包含 'Here' 的定义,并且最佳扩展方法重载 'Extensions.Here(ILogger, string, string, int)' 需要类型为 'ILogger' ConsoleUI D:\Code\Powerful Console 的接收器应用\BetterConsoleApp\ConsoleUI\Services\GreetingService.cs 42 活动

namespace ConsoleUI.Services
{
    public class GreetingService : IGreetingService
    {
        private readonly ILogger<GreetingService> _log;
        private readonly IConfiguration _config;

        public static string GetActualAsyncMethodName([CallerMemberName] string name = null) => name;

        /// <summary>
        /// Constructor for the GreetingService Class. This is bring in information from the Depedency Injection System.
        /// </summary>
        /// <param name="log">So the logger knows from what type of class we are going to call from. Don't modify this, just use it.</param>
        /// <param name="config">The configuration of the application. Don't modify this, just use it.</param>
        public GreetingService(ILogger<GreetingService> log, IConfiguration config)
        {
            _log = log;
            _config = config;
        }

        public void Run()
        {
            //Used to get the method name for logging.
            var stackTrace = new System.Diagnostics.StackTrace();
            var name = stackTrace.GetFrame(0).GetMethod().Name;

            for (int i = 0; i < _config.GetValue<int>("LoopTimes"); i++)
            {
                _log.LogInformation("{name} Run number {runNumber}", name, i); //Log "i" seperately under ther varianle name "runNumber". Can do query on run number 3.
            }

            //THIS DOES NOT WORK!?!?! Why?
            _log.Here<Serilog.ILogger>().Information("Application Finished Normally.");
        }
    }
}
c# dependency-injection asp.net-core-3.1 serilog
3个回答
1
投票

它不起作用,因为您在服务中使用了“错误”

ILogger
。您的扩展方法适用于
Serilog.ILogger
的实例,但您试图在
Microsoft.Extensions.Logging.ILogger<T>
上调用它。

要么将

GreetingService
更改为在任何地方都使用
Serilog.ILogger
,要么必须将扩展方法更改为目标
Microsoft.Extensions.Logging.ILogger<T>

public class GreetingService
{
    private readonly Serilog.ILogger _log; // <#<#<#<#<#<#<#<#<#<#
    private readonly IConfiguration _config;

    public static string GetActualAsyncMethodName([CallerMemberName] string name = null) => name;

    public GreetingService(Serilog.ILogger log, IConfiguration config) // <#<#<#<#<#<#<#<#<#<#
    {
        _log = log;
        _config = config;
    }

    public void Run()
    {
        // ...

        _log.Here<Serilog.ILogger>().Information("Application Finished Normally.");
    }
}

您可能也对此问题和答案感兴趣:

ASP.NET Core中的Serilog DI,要注入哪个ILogger接口?


0
投票

我需要向记录器注册 GreetingService。这就是我必须为 GreetingService 的构造函数做的事情。

        //Passing in the configuration throgh depedency injection.
        private readonly Serilog.ILogger _log;
        private readonly IConfiguration _config;

        /// <summary>
        /// Constructor for the GreetingService Class. This is bring in information from the Depedency Injection System.
        /// </summary>
        /// <param name="config">The configuration of the application. Don't modify this, just use it.</param>
        public GreetingService(Serilog.ILogger<GreetingService> log, IConfiguration config)
        {
            _log = log;
            _config = config;
        }

如果我不这样做,那么如何通过 DI 为此类注册 GreetingService?如果我从构造函数中删除,则会收到以下错误:“尝试激活“ConsoleUI.Services.GreetingService”时无法解析类型“Serilog.ILogger”的服务。”


0
投票

因此,在阅读您的帖子后,我在您的评论中提到了这一点。我想您不必将记录器注入每个类的构造函数中。假设这是可以的(我想找出原因)。我找到了两种实现此目的的方法,以及我之前创建的具有 [CallerMemberName] 的记录器扩展,这些方法如下...

private static Serilog.ILogger Log => Serilog.Log.ForContext<GreetingService>();
private readonly Serilog.ILogger _log = Serilog.Log.ForContext<GreetingService>();

推荐其中哪一个?

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