ASP.NET Core - Multiple instances of Logger - c#

I am right now working with Logging using ASP.NET Core LoggerFactory (Serilog extension). I want to put logs in controller and business service methods. I have done that via constructor injection of ILogger like this
In controller:
ILogger<HomeController> _logger
In Service:
ILogger<ServiceName> _logger
I believe this will be instantiated at each request, so for each HTTP request, it will create multiple instances of Logger. Say for each controller and each service class but it is bit different than previous methods of logging where we used to create only one logger instance and use that for logging stuff everywhere.
Any downside for this?

This is totally fine. Typically, it's cheap to instantiate a logger, so it's totally OK to do it like that performance-wise.
Still, consider either (1) using the global log instance which Serilog has or (2) using static field initialized in a field declaration. Again, not for the performance reasons, but rather to avoid polluting your constructor with not-so-relevant stuff.
UPD Update on implementing (1)
Basically, it's just a matter of deciding where would you put the logger init code into. In a ASP.NET Core it would be the first line of Main method (that Log is a static class from Serilog namespace):
Log.Logger = new LoggerConfiguration().WriteTo.LiterateConsole(LogEventLevel.Debug, LogTemplate)
.WriteTo.File(#"C:\logs\elbakogdabot.log", LogEventLevel.Debug, LogTemplate)
.Enrich.FromLogContext()
.CreateLogger();
(just to be clear: I took the code from a real project of mine, but the actual configuration of your logger could be different).
Then I would use it anywhere like this:
Log.Warning($"got a message for an unknown user: userid=[{userId}]");
This line could be thrown into any class and you don't have to do any extra initialization for that class.
UPD Update on implementing (2)
I guess in a typical enterprise app it would be inconvenient to have always remember to put the class name in the message every time you logging something. So I would go with static readonly field most of the time. With Serilog you can do it like that:
public class XYZService
{
private static readonly Serilog.ILogger log = Log.ForContext<XYZService>();
...
This way you both won't pollute the constructor, and will get the class name in all of your log messages automatically. I used to have this line in a ReSharper snippet, so I had to just type lg<TAB> in every new class.

Related

Dependency injection using both AddSingleton and AddTransient C#

I'm having a huge problem with the configuration/dependency injection of an application.
I have a singleton class added through DI with AddSingleton, that has in its constructor a IRequestClient, that is scoped because
busConfigurator.AddRequestClient()
which among other things, has the same effect as AddScoped.
When I start the app, it says
"Cannot consume scoped service 'MassTransit.IRequestClient`1[...]' from singleton '...'.)"
Which absolutely makes sense.
The weirdest thing is that I have another app set up the exact same way, but it just works and I would really like for that class to remain singleton.
My colleague and I spent an entire day trying to find the differences between the two applications, but they are virtually the same in their configurations, so we are having trouble in understanding why one works while the other doesn't.
I'm not entirely sure on what details could be important to better define the problem, so feel free to ask.
We've looked all around the internet trying to find a solution, but it was always "Change singleton to transient", but that's not an option, first because it HAS to be a singleton, otherwise it wouldn't make sense in our app, as that thing is what caches lots of date from our db so we can't just go around keeping on collecting heaps of data, second because the first app works with singleton, not with transient and we'd like to keep it that way
// This method is called in Main()
private static void ConfigureMassTransit(IServiceCollection services)
{
services.AddMassTransit(busConfigurators =>
{
busConfigurators.AddRequestClient<ICacheRepository>();
busConfigurators.AddConsumers(typeof(Program).GetTypeInfo().Assembly);
busConfigurators.UsingRabbitMq((context, cfg) =>
{
cfg.Host(new Uri($"rabbitmq://{Config.Settings.RabbitMq_Host}"), hostConfigurator =>
{
hostConfigurator.Username(Config.Settings.RabbitMq_User);
hostConfigurator.Password(Config.Settings.RabbitMq_Password);
});
cfg.ReceiveEndpoint("myApp", e =>
{
e.ConfigureConsumers(context);
});
});
});
// CacheRepository
public class CacheRepository : ICacheRepository
{
private readonly IClient Client;
public CacheRepository(ICacheRepository client, ILogger<CacheRepository> logger)
{
this.client = client;
this.logger = logger;
}
}
When a dependency is scoped, the implication is that a new instance is needed for each scope (which is usually an incoming HTTP request or message.) It implies that the instance should not be re-used for multiple requests.
If you have a singleton that depends on that scoped dependency, that singleton will be created using an instance of that dependency (the request client.) Because that singleton "lives forever," so does the instance of the request client it contains.
The result is that the request client is not supposed to be re-used across different scopes, but now it is. One instance is used forever.
A likely solution is to modify the class that depends on that client so that it doesn't need to be a singleton. You mentioned that it has to be a singleton because it caches data.
How does it cache data? Does it do so by storing data in a private field? If so, perhaps you could make that field static. Now the class instance isn't re-used, but those fields are shared between instances. (Verify that interaction with those fields is thread safe if they may be accessed concurrently.)
Or if there's some other cache mechanism, you could move that into its own dependency and make that a singleton.
Then your class can be scoped. It will depend on the singleton cache, always using the same instance. It will also depend on the scoped request client, using a new instance for each scope.
You could inject IServiceProvider instead, and create a scope when the singleton needs to perform a request. That way, you're sticking to the expected use of the request client.
await using var scope = provider.CreateAsyncScope();
var client = scope.ServiceProvider.GetRequiredService<IRequestClient<T>>();
await client.GetResponse(...);

Understanding C# FromServices

So iv just been thrown into some new code at work and its in C#. Now I'm not very familiar with C# and there are some things I really don't understand and the main one is Injecting into methods.
Now there is a WebAPI and it has controller that uses a class named "LocalFileStorage" which is a dependency from another project, the constructor for that class looks like this.
public class LocalFileStorageHandler : IStorageHandler
{
*Bunch of private variables
public LocalFileStorageHandler(DbContext dbContext, IConfiguration configuration, ILogger<LocalFileStorageHandler> logger)
{ code here}
Now in the controller class every method that uses the LocalFileStorage gets it injected as a parameter. Here is a example:
public async Task<IActionResult> ApiMapper([FromBody] dynamic request, [FromServices] IStorageHandler storageHandler)
Also in the project startup.cs we can find this line:
services.AddScoped<IStorageHandler, LocalFileStorageHandler>();
Now my understanding is that for each separate request made the Addscoped makes sure that the method gets its own instance of LocalFileStorage handler. I also understand that the "[FromServices]" attribute causes this instance to be injected into the method. However the one thing I don't understand and cant find anywhere in the code is where the LocalFileStorage objects get their "In parameters" for their constructor?
As by my understanding each injected instance of LocalFileStorage should also receive the parameters:
DbContext dbContext, IConfiguration configuration, ILogger<LocalFileStorageHandler> logger
What am i missing here ?
Kind regards
The DI container injects the dependencies for you. So somewhere, the DbContext, IConfiguration and ILogger has been registered/setup.
Then when you use the FromServices attribute, the DI container will try to resolve the type for you and inject all dependencies (if they are registered, if not, an exception will be thrown)
IConfiguration and ILogger are usually setup when building the host. DbContext are (usually) registered by using the AddDbContext extension method.
Link to configuration for ILogger: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-6.0#logging-providers
Link to configuration for IConfiguration: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-6.0#default-configuration
Link to Dependency Injection fundamentals in ASP.Net Core : https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-6.0

Does injecting ILogger<T> create a new logger each time?

On the logging samples in the documentation, there is an example how to inject a logger into a controller:
public class TodoController : Controller
{
private readonly ITodoRepository _todoRepository;
private readonly ILogger _logger;
public TodoController(ITodoRepository todoRepository,
ILogger<TodoController> logger)
{
_todoRepository = todoRepository;
_logger = logger;
}
}
Does the DI framework create a new logger each time I inject a logger into something like here? Is there a better way?
This is easily answered by a look into the source. When you do services.AddLogging(), the default behavior is that ILogger<T> is registered as a singleton:
public static IServiceCollection AddLogging(this IServiceCollection services, Action<ILoggingBuilder> configure)
{
// …
services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>());
services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>)));
// …
}
So no, ILogger<T> instances for a certain type T are kept around for as long as the application is running. So when injecting an ILogger<TodoController> into your controller, the same logger instance will be passed to it every time.
Of course this only applies to the logger, but not your controller itself. By default, controllers are activated outside of DI but effectively live with a scoped lifetime. So on every request, there will be a new controller instance; but that one will then get the logger instance from before.
To answer your last question, is there a better way? No. Apart from the fact that this behavior is already a good one (since there’s no need for new logger instances), the proper way to use logging is indeed to inject ILogger<T> into types T, so you get a properly categorized logger instance. There’s really no need to worry about the very thin logger here when there are so many way more expensive things going on in the background that you will likely never see ;)
Since the ILogger<T> is a singleton, its instance will be reused all throughout the application. Note that this will not have an effect on logging scopes. The ILogger<T> implementation that you use within your application is actually just a thin wrapper that forwards logging calls to the internal loggers (which are also effectively singletons). So the lifetime of ILogger<T> is actually not relevant since they do not keep any state at all.
The logging scopes themselves are persisted using an AsyncLocal which is a mechanism to keep state throughout the asynchronous call flow. That means that logging scopes will just “magically” work and not leak outside of the call flow just because some instances happen to be shared between multiple threads (or asynchronous flows).

How should I use appsettings.json config key/values in my ConfigurationServices ASP.NET Core 2.0 on startup?

I'm trying to configure my services for an ASP.NET Core 2.0 app/website.
I wish to reference some key/values from my appsettings.json file, in this method.
I'm not sure if what I'm going is OK or not:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddJsonFormatters()
.AddCors();
var applicationSettings = Configuration.GetSection("Settings").Get<ApplicationSettings>();
services.AddSingleton(applicationSettings);
// ** THIS IS WHAT I ORIGINALLY HAD, BUT IT'S ONLY SETTING
// THE VALUE IN DI/IOC.
//services.Configure<ApplicationSettings>(options => Configuration.GetSection("Settings")
// .Bind(options));
var foo = new Foo(applicationSettings.SomeSetting);
services.AddSingleton(foo);
}
See how i'm manually adding a singleton and then later, referring a value from the app settings instance?
vs
just configuring ...
So, is either way OK or is there a specific reason for either/or?
Remember -> i'm going to need to inject my settings into controllers, etc...
Technically, you can do either. In both cases, you have the configuration registered and available through dependency injection, so everything can depend on it and will get the configuration instance.
You are also using the centrally set up Configuration there, so you have all the benefits from the configuration stack there, e.g. multiple providers or environment specific overrides.
However, the favor has definitely moved to the IOptions way of consuming custom configuration. It’s the “state of the art” and used throughout ASP.NET Core for literally everything. It also allows you to switch to options that can be updated at runtime. That’s very powerful and might become useful eventually (not necessarily for your specific situation with the singleton, but maybe for something else).
It’s also really easy to set this up, actually shorter than what you tried:
services.Configure<ApplicationSettings>(Configuration.GetSection("Settings"));
services.AddSingleton<Foo>();
Note that, even for singletons, you shouldn’t explicitly create a new instance of it, but let DI handle that. If your class has the correct constructor, dependencies will be automatically injected anyway:
public class Foo
{
private readonly ApplicationSettings _settings;
public Foo (IOptions<ApplicationSettings> settings)
{
_settings = settings.Value;
}
}
Of course, Foo can also have more dependencies here. Since it’s going to be constructed by DI, you can just add more dependencies in the constructor, without having to update some new call somewhere.
If you need to configure certain services with settings that depend on your configuration, you still should not bind your configuration there directly. All of configuration is DI-based, so you just need to inject the right thing; a IConfigureOptions<T>. That’s basically the thing that provides the IOptions<T> to services later. In your JWT case, this could look like this:
// instead of passing an option configuration delegate here…
services.AddAuthentication().AddJwtBearer();
// … we register a IConfigureOptions<JwtBearerOptions> instead
services.AddSingleton<IConfigureOptions<JwtBearerOptions>, ConfigureJwtBearerOptions>();
// … ConfigureJwtBearerOptions could look like this:
class ConfigureJwtBearerOptions : IConfigureOptions<JwtBearerOptions>
{
private readonly ApplicationSettings _settings;
public ConfigureJwtBearerOptions(IOptions<ApplicationSettings> settings)
{
_settings = settings.Value;
}
public void Configure(JwtBearerOptions options)
{
// configure JwtBearerOptions here, and use your ApplicationSettings
options.MetadataAddress = _settings.JwtMetadataAddress;
}
}
This might seem unnecessarily verbose compared to just passing a delegate to AddJwtBearer() but note that this is exactly what happens under the hood when you pass that delegate: An IConfigureOptions<JwtBearerOptions> object will be created that calls your delegate in the Configure() call. So this is really just the same.
Note that for authentication schemes, you might actually set up a IConfigureNamedOptions<T> instead, which is almost the same thing except it can configure the options based on a name. For authentication schemes, that is the scheme name, so basically you check the scheme name in Configure() and then decide how to configure your options.
As for creating singleton instances, especially expensive ones, I would argue that ConfigureServices is the wrong place for such a thing. ConfigureServices is called very early in the application startup phase, when the whole DI infrastructure does not exist yet. So you could not rely on anything when creating your instance. I would also argue that it is still not your job to create the object but you should DI handle the creation of it and as such give it also control over its lifecycle.
If you absolutely need to control when the instance is created, I would suggest you to use the lifecycle events for this: Basically, after the application has set up properly but before a first request comes in, you request the instance of your services and initialize it. That way, you can still have it fully depend on DI, and it won’t be created lazily with the first request.
You can register lifecycle handlers in the Configure method:
public void Configure(IApplicationBuilder app, IApplicationLifetime applicationLifetime)
{
applicationLifetime.ApplicationStarted.Register(() =>
{
// application has started, request the singleton here to trigger DI to
// create the instance
app.ApplicationServices.GetService<ExpensiveSingleton>();
});
// …
}
}
Well the problem with that approach is that it will be impossible to load multiple configuration sections through DI. The Configuration API has many features, such as pluggable configuration provides, snapshots, etc.
I would suggest you at least use a class to bind you configuration section against, so DI can inject it based on its type. If you further down the line have need to another configuration class you won't run into issues.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration

Singleton logger, static logger, factory logger... how to log?

I am wrapping the patterns & practices Enterprise Library Logging Application Block for an application written in .NET.
I want to be able to subclass a logger (i.e to provide domain specific logging).
What is the best way to do this?
For e.g, I have a static Logger class at the moment, but this does not allow me to specialize it for domain specific logging.
For example,
Log(MyDomainObj obj, string msg)
Check out NLog. They use this sort of pattern:
private static Logger myDomainLogger = LogManager.GetCurrentClassLogger();
You can then specialize the output based on the class that myDomainLogger belongs to.
More detail:
class MyDomain
{
private static Logger _logger = LogManager.GetCurrentClassLogger();
private void SomeFunc()
{
_logger.Trace("this is a test");
}
}
Then in your output you can have it output "MyDomain.SomeFunc" as part of the "this is a test" message.
Also, checkout log4net. I never found the EL's logging to be as flexible as log4net. I chose log4net since I was already familiar with using log4j.
protected readonly log4net.ILog LOG = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
Doing it this way, I can get logs like this:
2009-07-15 09:48:51,674 [4420] DEBUG
SampleNamespace.SampleClass [(null)] -
Sample message you want to output
You could even do better than that. Write a wrapper class that wraps either Nlog or log4net or whatnot. You can then use that wrapper class (maybe use an interface to it if you really want to decouple things) in your code. This way, if you decide to change logger class, you need to change just one class and not edit all your classes.

Categories