NET Developer I am currently working on a ASP NET MVC 5 library. The code will eventually be built into a DLL and shipped as a nuget package to be consumed else where.
My Problem: I am looking at how to have some method of logging in this file that is flexible and can be used by all the different users who consume it. After doing some research, it looks like most people who are working in some form of .NET have access to Microsoft.Extensions.Logging and ILogger. This seems to fit my requirements since it has logging for errors, informational, warnings, etc.
Is this how I properly use it? I currently have
var factory = new LoggerFactory();
ILogger logger = factory.CreateLogger("MyLogger");
// Now I can call the generic methods
log.LogInformation("This is an informational log");
Am I missing key functionality? Online I have seen individuals use code such as ILoggerFactory factory = new LoggerFactory().AddConsole(); but I do not have access to `AddConsole after instantiating a new LoggerFactory. I mostly just want a generic way to log events so that others can use my code and appropriate logs are created and I don't have to worry about custom implementation of logging, etc. I'm more focused on logging string messages.
Related
How it works in Serilog
Every ILogger in Serilog has useful method ForContext which essentially creates completely independent child logger with some properties attached.
This is useful for my case, when inside one ASP .NET Core host there are N independent device API's running, each managing some physically connected device. Each API can get it's own ILogger which will for contain property like DeviceName = "Device1", so one can setup appropriate filters in Seq/Greylog/etc.
How it works ASP .NET Core logging API
However, if one uses ILogger from Microsoft.Extensions.Logging, there is no direct counterpart for the ForContext method. The only alternative is BeginScope, which, unfortunately, does not create new isolated logger instance but instead adds values to the global logger scope, from what I understand.
To mitigate this problem, tools like AsyncLocal and SynchronizationContext are used. From what I understand, this works fine for things like correlating HTTP requests because they usually have clear async processing pipeline.
However, in my case each device API runs a chain of somewhat complex Rx .NET observable sequences on task pool scheduler. Even if it is possible to make all of that preserve SynchronizationContext, this will introduce a lot of unneeded complexity.
Question
Is there any way to create independent loggers and/or logger factories, each with their own scope/state? So that, similar to Serilog, anything that uses such logger will have additional properties attached regardless of calling thread or anything else, but these properties will be invisible to any other logger in the application?
Or should I just use Serilog.ILogger instead of Microsoft API, given that this app currently don't use Web API functionality anyway?
I have one part of my application where I only want to log to the EventLog. The rest of the time, I want to to log to all the normal event sinks that are setup for my ILoggers.
I can make an ILogger in those parts by doing this:
var eventLogLoggerProvider = new Microsoft.Extensions.Logging.EventLog.EventLogLoggerProvider();
var eventLogger = eventLogLoggerProvider.CreateLogger("EventLogger");
eventLogger.LogWarning("We logged to the event log!");
But I feel like I should do it via dependency injection. But all the examples just show setting up one time for the whole application.
Is there a way to configure an ASP.Net Core 3.1 application to have two different kinds of ILoggers? (And allow injecting one or the other into a class?)
There are several options at your disposal. However, none of them are going to be especially nice. The .NET Extensions DI is somewhat simplistic whilst your scenario seems rather advanced.
Use EventLog directly
If you are so bound on using EventLog in a particular place of the application, maybe it would make sense to use it directly as the ILogger abstraction is basically in opposition to what you require.
With ILogger dependency, you are saying "Give me a logger, whatever that is, I don't care". But you do care. So why not use EventLog directly? Or perhaps hide it behind an interface and adapter for testability and inject it.
Logging configuration
Alternatively, you will encounter somewhat less friction if you configure the logging through appsettings.json so that EventLog has default LogLevel = None and LogLevel = Information or whatever you like in your event-logged assembly/namespace. And that the rest of the loggers have None LogLevel for the event-logged area. For example:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"My.EventLogged.Assembly": "None"
},
"EventLog": {
"Default": "None",
"My.EventLogged.Assembly": "Information"
}
}
}
This would work nicely if you have your code isolated into an assembly or a few namespaces but it's easy to add another place where you only want event log and forget to configure it as such.
For more information, see
Logging in .NET Core and ASP.NET Core, Configuration section.
Providing additional context
If the area where you only require event logging is easily separated, you can provide a facade with a singleton lifetime that will create a separate IServiceCollection/IServicePrivoder with event log being the only registered ILogger in that container.
Any call to your event-logged code should go through the facade which would invoke the expected implemenatation using the internal IServiceProvider. If you need to call from your event-logged code to the rest of your application, you will need to do the reverse.
This is not straightforward, but even ASP.NET Core itself does (or at least did) this in order to provide logging during startup, when services initialization is not complete yet.
But it definitely has element of surprise and could lead to some headaches with debugging and diagnosing service setup. There will also be duplicities within the setup. I do believe this is not a worthwhile approach.
Use a different IoC container
Other containers such as autofac or castle might be able to resolve multiple services (in this case ILogger) based on instance identification using names. See e.g. AutoFac FAQ: How do I pick service implementation by context.
However, I cannot advise on how to do this with something as ILogger as the logging registration is rather complicated.
Those are just some directions you could take I could think of off top of my head. I don't believe there is a simple, cannonical answer to what you're requesting and you will have to research the options further based on your specific needs and situation.
For my current project, a WebAPI / MVC environment, I am using Unity IoC for DI.
As MVC 5 / .Net framework do not have an inbuilt logging so I need something, but want to also use DI for the referencing the service, but I don't know how to set up / register the logger service.
For all other services in the application, its simply a case of adding the mappings to the RegisterTypes in UnityConfig.cs
Target deployment will be Azure Web App service, so I need something with those capabilities.
If I was able to use .Net Core, problem solved, its all there ready, but unable to do that (don't ask why, but I am trying) ...
First of all, you need to select the logging library - common ones are log4net and NLog.
There are already tons of materials with installation instruction, including official notes. You don't need DI for NLog it already has a static helper, but you can encapsulate it into your own abstraction.
So you need to have an interface, for example, ILogger and it's realization - class NLogLogger. Put the methods you need inside ILogger in manner you like - it can be LogError(msg, ex), LogDebug(msg), LogInfo(msg), or instead one method with level param Log(level, msg, ex), where level your is enum: Error, Debug, Trace etc. You can put that interface inside application core.
The NLogLogger would be the concrete implementation of the logger, so you can later substitute it. Put it into infrastructure.
Implement it along with official tutorial you can find on GitHub repo.
You can configure NLog with XML file, just put it in app root, and copy one of example for writing logs into file or console.
At the end just bind interface with class inside DI configuration, like any other abstraction. Now you can inject it in every place you want.
We have c# application with several different modules. We are using log4net for logging. What should be done to log the messages:
Create a centralized logging project: The application classes to use centralized logging project's dll's static methods to log the messages. So, no class in application creates the logger, all logging requirements to be fulfilled by this dll OR
All types in the application itself creating their own loggers: Easy to check which type generates which message, easy to manage different type of logging requirements i.e.we can throttle logging behavior of each type independently, like one class logging ALL logs but other only loggin ERROR.
What is the preferred practise?
The approach2 has its own benefit but approach1 looks clean as each class would no longer be responsible for calling "GetLogger(typeOf(className))".
What is the recommended way?
It really depends on the usecase, when making a library it can make sense to use method 1. However when making a complex program method 2 will help you to manage different logger independently. Method 2 will give the also the option to log all in you project like method 1 does. However method 1 does not support differing on logger. So method 2 seems a better choice in most cases.
Overview
I'm trying to port a number of projects based on the .NET Framework to .NET Core. This involves porting a number of class libraries as well as top-level console/web applications that consume these libraries.
Part of my requirements is that my top-level applications should support a plugin-based system where I can easily swap out functionality by referencing different subsets of these class libraries. I've used MEF to do this. As an example, one of my ASP.NET Core web applications involves communicating with devices through an ICommunicationService, and I have different Nuget packages that export different implementations of this service:
[Export(typeof(ICommunicationService))]
[Shared]
public sealed class UsbCommunicationService : ICommunicationService
{
}
Redesigning Class Libraries
At the moment, these class libraries reference Common.Logging and instantiate loggers as read-only static fields:
[Export(typeof(ICommunicationService))]
[Shared]
public sealed class UsbCommunicationService : ICommunicationService
{
...
private static readonly ILog Log = LogManager.GetLogger<UsbCommunicationService>();
....
}
I used Log4Net within my top-level applications and facilitated logging from within my class libraries by referencing the Common.Logging.Log4Net adapter.
However, I know that ASP.NET Core relies on Microsoft's new logging abstraction framework Microsoft.Extensions.Logging and that ASP.NET Core applications should be designed to support logging via constructor dependency injection of loggers, like this:
public class HomeController : Controller
{
private ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
_logger.LogInformation("Index action requested at {requestTime}", DateTime.Now);
return View();
}
}
I'm not entirely sure which combination of logging frameworks to use within my new .NET Core libraries and applications (see this related post), but I'm leaning towards switching from using Common.Logging to Microsoft.Extensions.Logging within my class libraries. In that case, I'm wondering how I should handle instantiation of loggers. Would something like this be appropriate?
using Microsoft.Extensions.Logging;
...
[ImportingConstructor]
public UsbCommunicationService(
[Import] IUsbMessageEncoder encoder,
[Import] IUsbMessageDecoder decoder,
[Import] ILogger<UsbCommunicationService> logger /* Add this new import */)
{
...
}
In other words, should I switch all my class libraries that require logging to having those loggers injected during construction?
Consuming Class Libraries
Based on this answer to a similar question, I feel like the approach detailed above is along the right lines. However, I'm not sure how I would consume and properly instantiate services defined within class libraries within, say, an ASP.NET Core application.
ASP.NET Core uses its own dependency injection service which is completely separate to MEF. I can't simply write something like services.AddSingleton<ICommunicationService, UsbCommunicationService>(); within my web application for two reasons:
The idea is to support a plugin-based system where plugins are discovered dynamically and therefore can't be hard-referenced by the "core" application itself.
The other dependencies for UsbCommunicationService - IUsbMessageEncoder and IUsbMessageDecoder - are not known by ASP.NET Core's service injector and wouldn't be resolved.
Likewise, I can't use MEF to get an instance of UsbCommunicationService either as it wouldn't be able to resolve the reference to ILogger<UsbCommunicationService>.
Summary
In short, I'm trying to find solutions for the following:
Facilitating logging within .NET Core libraries with maximum flexibility for logging providers.
Allowing loggers to be supplied to these class libraries using dependency injection.
Allowing top-level ASP.NET Core or .NET Core console applications to dynamically discover and load these .NET Core libraries at run-time and provide them all with loggers or logger factories so that the top-level application and all loaded plugins use a common logging provider (e.g. Serilog, NLog, Log4Net, etc.).
For instance, if I wanted to use Log4Net's ColoredConsoleAppender, I should see all ASP.NET logs and class library logs appearing within the same console.
Microsoft.Extensions.Logging is not strictly a logging framework; it's a facade. There's built-in implementations for things like Debug, Trace, Console, etc., but that's just for ease. In actual use, you'd likely plug in something like Serilog. Serilog is what is actually handling the logging, while Microsoft.Extensions.Logging just provides an abstracted way of "logging" without having to actually make your app code depend explicitly on Serilog's API.
Common.Logging is also a facade, where in this case, you've chosen to plug in log4net as the actual logger being used. However, that can be changed. Given this, you have a few possible paths you can take.
Switch out Microsoft.Extensions.Logging with Common.Logging in your ASP.NET Core app. Yep, you can do that. Personally, I think Microsoft.Extensions.Logging is better, but you can use whatever you like.
Switch out Common.Logging with Microsoft.Extensions.Logging in your class libraries. If you're rewriting them anyways, this might make the most sense, but it's also involves the most friction, in terms of things that need to be changed in your code.
Write an adapter for Common.Logging that uses Microsoft.Extensions.Logging. This is admittedly a bit meta, but there's technically nothing wrong with simply using one facade to work with another facade to eventually work with a particular logging framework. That's sort of the entire point of the adapter pattern. This also nets you the best of both worlds: you don't need to change much in your libraries or your ASP.NET Core app. However, it does add to the entropy of your code due to the multiple facades in play. While it's impossible to say, I actually don't see Common.Logging continuing to be maintained going forward. Now that Microsoft sort of has an almost built-in facade, I expect to see that pretty much dominate. It might be for the best to jump ship now, while you're already incurring some amount of rework.