C# .NET Core 3.1 Serilog usage in BackgroundService - serilog

Following a tutorial on how to use Serilog I read the settings from an appsettings.json file - and all below works well, when things are simple:
Main program (in simple MVP version):
public static void Main(string[] args)
{
//Read Configuration from appSettings
var config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
//Initialize Logger
Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(config).CreateLogger();
try
{
Log.Information("Starting up the service");
CreateHostBuilder(args).Build().Run();
....
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
})
.UseSerilog();
Then the class with the BackgroundService:
....
using Microsoft.Extensions.Logging;//and not Serilog!!
public class Worker : BackgroundService
{
private readonly ILogger _logger;
....
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
---------------------SIMPLE THINGS FOR ME ENDS HERE :-) ----------------------------------
Now, I want to build it up to a "real" program from this first MVP - placing function in seperate classes according to SOLID principles.For instance my first class is a ConfigurationCollector collecting all relevant paramters from the appsettings.json file that I place in a seperat class. The class works and does it jobs - but I want logging to work - and I try to use the allready configured Serilog logger used accross class (just as I did with ILogger, so that the Worker object creates a new ConfigurationCollector class like
public Worker(ILogger<Worker> logger)
{
_logger = logger;
_configCollector = new ConfigCollector(logger);
}
I know that logger is now a type of
ILogger<ConfigCollector>
which is not
ILogger<Worker>
But how do I inject the original Serilog from the initiation of the service into each class used, such that the objects are shown properly in the logs?
public class ConfigCollector
{
private readonly ILogger<ConfigCollector> _logger;
....
public ConfigCollector(ILogger<ConfigCollector> logger)
{
_logger = logger;
}

.NET 6 Background service
.UseSerilog((builder, loggerConfig) =>
loggerConfig.ReadFrom.Configuration(builder.Configuration))
.Build();

Related

How to inject and use Serilog (Ilogger) when web-api startup

I'm developing a .NET core 3.1 Console application (web-api).
I use a Serilog service (it is basically using the Microsoft.Extensions.Logging).
The Serilog is injected and can be used in the FW Controllers methods.
Now - I need something a little bit different - Whenever the system is starting up (after being down) I need to make an http post request - you can see it when executing the ConnectionInitiator.Initiate(), in the startup method. In that same scope (method\class) - I need to use the logger, in order to log some data. Now - If the request would be through the controller - the logger, would be available (by the DI).
To make a long story short - I need somehow to inject the Ilogger to the class or to make it available in some other way. I've tried use the logger in the startUp, but this seems to be impossible (since .net core 3.0 - if I understand correctly)
See my code:
Program.cs:
public class Program
{
public static void Main(string[] args)
{
var loggerConfig = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
//Reading the appconfig.json
Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(loggerConfig).CreateLogger();
try
{
Log.Information("System Started up");
CreateWebHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "THE APPLICATION FAILED TO START UP");
}
finally
{
Log.CloseAndFlush();
}
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
return WebHost.CreateDefaultBuilder(args).ConfigureLogging((context, logging) =>
{
logging.ClearProviders();
}).UseSerilog().UseStartup<Startup>();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
StartUp.cs
public class Startup
{
public Startup(IConfiguration configuration/*, Microsoft.Extensions.Logging.ILogger logger*/)
{
Configuration = configuration;
ConnectionInitiator.Initiate(configuration/*, logger*/);
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddControllers();
services.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(dispose: true));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors(builder => builder.AllowAnyHeader().AllowAnyMethod().SetIsOriginAllowed((host) => true).AllowCredentials());
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
ConnectionInitiator.cs:
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
namespace AAA.BL
{
public static class ConnectionInitiator
{
private static readonly object threadlock = new object();
private static MyConfig myConfig;
private static ILogger ilogger;
/*
public ConnectionInitiator(ILogger _logger)
{
ilogger = _logger;
}
*/
public static void/*async Task*/ Initiate(IConfiguration configuration/*, ILogger ilogger*/)
{
HttpRequester httpRequester = new HttpRequester();
if (myConfig == null)
{
myConfig = new myConfig(configuration);
}
IssueOTPResponse response = /*await*/ httpRequester.PostSomething(myConfig, ilogger).Result; //Double check thread safe singleton implementation
if (response.ststuacode != 200)
{
ilogger.logcritical($"critical error when initiate connection (connectioninitiator): {response.statusdescription}");
}
}
}
}
It seems like the answer is much simpler that I expected - By using the Serilog and was added as a service in the Configure method - It can be reached globally (in every place of the namepsace) by using the static class Log and its static method Logger, for example:
Log.Logger.Information("XXXXX");

Using Unity DI with HostBuilder based .NET Core console application

With ASP.NET MVC Core it is possible to use the Unity DI packages by loading the appropriate NuGet packages (Unity.Container and Unity.Microsoft.DependencyInjection) and then calling the UseUnityServiceProvider() extension method when building the web host in Program.cs. This extension method is based off of the IWebHostBuilder interface. After initializing the host it is possible to access the Unity DI functionality via the .NET Core GetService interface and any constructor injection.
I'm working on a console based application that will use HostBuilder and the IHostBuilder interface. I've tried reimplementing the registration logic from the UseUnityServiceProvider() extension (available here: https://github.com/unitycontainer/microsoft-dependency-injection/blob/master/src/HostingExtension.cs) as part of the ConfigureServices() method call against IHostBuilder, but the change in service provider does not appear to be visible to downstream DI calls.
Has anyone been successful in getting Unity to work with the Microsoft DI methods in an application created using HostBuilder?
Update As requested, here is an example of what I was trying (taken from the Unity source). Of course, this does not work. TestService has a constructor which should be injected with an object defined in MyUnityExtension. This does not happen.
private static async Task MainTest()
{
var container = new UnityContainer().AddNewExtension<MyUnityExtension>();
var factory = new ServiceProviderFactory(container);
var hostBuilder = new HostBuilder()
.ConfigureServices((hostBuilderContext, services) =>
{
services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IUnityContainer>>(factory));
services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IServiceCollection>>(factory));
services.AddHostedService<TestService>();
});
await hostBuilder.RunConsoleAsync();
}
Not sure what you want to achieve with new UnityContainer().AddNewExtension<MyUnityExtension>().
If you just want some registered service to be injected into TestService, why not just use IUnityContainer.RegisterType<TInterface, TImplementation>() ?
Here is a working IHost setup in Program.cs (.NET Core 3.1, Microsoft.Extensions.Hosting v3.10, Unity.Microsoft.DependencyInjection v5.11.5):
public static async Task Main(string[] args)
{
var container = new UnityContainer();
container.RegisterType<IService, MyService>();
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<TestService>();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConsole();
})
.UseUnityServiceProvider(container);
await builder.RunConsoleAsync();
}
The interface:
public interface IService
{
string Name { get; }
}
The implementation:
public class MyService : IService
{
public string Name => "My name";
}
The TestService:
public class TestService : IHostedService
{
private readonly IService service;
private readonly ILogger logger;
public TestService(IService service, ILogger<TestService> logger)
{
this.service = service;
this.logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
this.logger.LogInformation("Hello {n}", this.service.Name);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

IServiceProvider with Autofac and webjob

I'm working in an Azure webJob.
I'm using autofac with a IJobActivator an everything is ok.
But now, I need to call code that is using IServiceProvider and at that moment I get an error from Autofac because IServiceProvider is not known.
When I'm using the Microsoft.Extensions.DependencyInjection.ServiceCollection() to register my Interfaces in place of Autofac it is working (I don't know where the IServiceProvider is registered but it is working).
The company I work for is asking me to explicitly use Autofac.
I struggle to find to way to use Autofac but declare the IServiceProvider within the WebJob.
Have someone an idea?
I need to call code that is using IServiceProvider and at that moment I get an error from Autofac because IServiceProvider is not known.
Does that mean when running your Webjob, you could not find the IServiceProvider in AutofacActivator.
I am not clear about how do you define IServiceProvider and how do you inject it?
I think you could inject IServiceProvider into job activator and register it then you could use this instance to get service.
You could register the IServiceProvider interface before build in ContainerConfig:
public static class ContainerConfig
{
public static IContainer GetContainer()
{
var builder = new ContainerBuilder();
builder.RegisterType<Functions>();
builder.RegisterType<HelloGenerator>().As<IStringGenerator>().SingleInstance();
builder.Register<IServiceProvider>(context =>
{
var serviceCollection = new ServiceCollection();
//todo: register the interfaces
return serviceCollection.BuildServiceProvider();
}).SingleInstance();
return builder.Build();
}
}
Get service when triggered in Functions:
public class Functions
{
private readonly IStringGenerator _stringGenerator;
private readonly IServiceProvider _serviceProvider;
public Functions(IStringGenerator strGenerator,IServiceProvider serviceProvider)
{
_stringGenerator = strGenerator;
_serviceProvider = serviceProvider;
}
public void ProcessQueueMessage([QueueTrigger("queue")] string message, TextWriter log)
{
log.WriteLine(_stringGenerator.GetWord());
log.WriteLine(_serviceProvider.GetService(xxxxxx));
}
}
In Progtam:
static void Main()
{
var config = new JobHostConfiguration
{
JobActivator = new AutofacActivator(ContainerConfig.GetContainer())
};
var host = new JobHost(config);
host.RunAndBlock();
}
In AutofacActivator:
public class AutofacActivator : IJobActivator
{
private readonly IContainer _container;
public AutofacActivator(IContainer container)
{
_container = container;
}
public T CreateInstance<T>()
{
return _container.Resolve<T>();
}
}
If this is not what you want, hope you could give me more detailed description and your main idea code.
In fact I solved my issue by using Extensions:
using Autofac.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
by using them doing the following:
_containerBuilder = new ContainerBuilder();
_containerBuilder.Populate(new ServiceCollection());
_containerBuilder.RegisterType<MyGreatType>().InstancePerDependency();
_container = _containerBuilder.Build();
It generates automatically the IServiceProvider for you

Hangfire with Ninject using InRequestScope

I have installed the Hangfire.Ninject package to an ASP MVC 5 application so that I can run some background jobs.
I've read through the documentation but I'm baffled as to how to implement it.
My existing configuration uses InRequestScope for my IUnitOfwork class to ensure only a single instance is instantiated per HTTP request as follows:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
}
To use ninject with hangfire having followed the documentation I have updated the configuration as follows in my ninjectwebcommon.cs class:
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
GlobalConfiguration.Configuration.UseNinjectActivator(kernel);
RegisterServices(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IUnitOfWork>()
.ToSelf()
.InNamedOrBackgroundJobScope(context => context.Kernel.Components.GetAll<INinjectHttpApplicationPlugin>()
.Select(c => c.GetRequestScope(context))
.FirstOrDefault(s => s != null));
}
But now I get the following error:
Error activating IUnitOfWork using self-binding of IUnitOfWork
No constructor was available to create an instance of the implementation type.
I have a class I would like to use to process my background job using hangfire is as follows:
public class EmailJob
{
private readonly IUnitOfWork _unitOfWork;
private readonly IMailer _mailer;
public EmailJob(IUnitOfWork unitOfWork, IMailer mailer)
{
_unitOfWork = unitOfWork;
_notificationMailer = notificationMailer;
}
public void Execute()
{
// DO Stuff
}
}
Any one know what I'm doing wrong? The documentation also states:
Services registered with InRequestScope() directive will be unavailable during job activation, you should re-register these services without this hint.
What does this mean? I still want to ensure that only one IUnitOfwork class which implement dbContext is used per http request. How is this now going to affect the rest of the application if I remove the InRequestScope?
I think the issue is that you are binding IUnitOfWork to itself.
Niject would need a concrete class to activate something like UnitOfWork.
kernel.Bind<IUnitOfWork>()
.To<UnitOfWork()
.InNamedOrBackgroundJobScope(context => context.Kernel.Components.GetAll<INinjectHttpApplicationPlugin>()
.Select(c => c.GetRequestScope(context))
.FirstOrDefault(s => s != null));

Best way to inject Serilog into repository for logging

I can inject the ILogger into a controller but I'm having an issue trying to inject it into a repository class.
Here is my Startup method:
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Error()
.WriteTo.RollingFile(Path.Combine(
env.WebRootPath, "log-{Date}.txt"))
.CreateLogger();}
This is in my Configure method:
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
loggerFactory.AddSerilog();
My Controller:
private readonly ISkillsRepo _repo;
private readonly ILogger _logger;
public HistoryAndReportsController(Context db, ILogger<SkillsRepo> logger)
{
_repo = new SkillsRepo(db, logger);
_logger = logger;
}
The repo:
private readonly Context _db;
private readonly ILogger _logger;
public SkillsRepo(Context db, ILogger<SkillsRepo> logger)
{
_db = db;
_logger = logger;
}
I can inject the logger into the controller and then pass it to the repo but there must be a way to inject the logger into the repo directly but I can't find any examples.
I can create another logger instance in the repo but that defeats the purpose of DI. I have the same issue understanding how to do the same with the DBContext.
As it has already been mentioned you need to register your repository in the dependency injection (DI) container first.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<IUserRepository, AwesomeUserRepository>();
...
}
ILoggerFactory (and therefore ILogger<T>) are framework provided injections and don't need to be added manually.
Igor's answer was the solution with the addition of adding your repo to the services otherwise you get an error.
"Unable to resolve service for type "

Resources