Below my logging works fine with the extension method I added. However, when I use this with DI, it does not work.... Why?
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>();
}
}
}
This is what I am trying to use in my class by using DI. This is not working and I cannot figure out why. I nee the extension method to add additional context to my logging.
Error I am getting.: Severity Code Description Project File Line Suppression State Error CS1929 'ILogger' does not contain a definition for 'Here' and the best extension method overload 'Extensions.Here(ILogger, string, string, int)' requires a receiver of type 'ILogger' ConsoleUI D:\Code\Powerful Console App\BetterConsoleApp\ConsoleUI\Services\GreetingService.cs 42 Active
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.");
}
}
}