下面是我的日志记录代码,它与我添加的扩展方法配合得很好。但是,当我将其与 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.");
}
}
}
它不起作用,因为您在服务中使用了“错误”
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.");
}
}
您可能也对此问题和答案感兴趣:
我需要向记录器注册 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”的服务。”
因此,在阅读您的帖子后,我在您的评论中提到了这一点。我想您不必将记录器注入每个类的构造函数中。假设这是可以的(我想找出原因)。我找到了两种实现此目的的方法,以及我之前创建的具有 [CallerMemberName] 的记录器扩展,这些方法如下...
private static Serilog.ILogger Log => Serilog.Log.ForContext<GreetingService>();
private readonly Serilog.ILogger _log = Serilog.Log.ForContext<GreetingService>();
推荐其中哪一个?