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
{
}
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?
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>
.
In short, I'm trying to find solutions for the following:
ColoredConsoleAppender
, I should see all ASP.NET logs and class library logs appearing within the same console.So, go to the Startup. cs file and add the ILoggerFactory parameter in the Configure() method. Then, call the AddFile() extension method to add Serillog file provider, as shown below. ASP.NET Core dependency injection will automatically pass an instance of the LoggerFactory for this parameter.
Logging providers store logs, except for the Console provider which displays logs. For example, the Azure Application Insights provider stores logs in Azure Application Insights. Multiple providers can be enabled. The default ASP.NET Core web app templates: Use the Generic Host.
TRACE Level and Debug Logs NET Core agent logs information to the logs folder within C:\ProgramData\Contrast\dotnet-core\ on Windows or /var/tmp/contrast/dotnet-core/ on Linux, by default.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With