3

I have a project using ASP.NET 5 and MVC 6. I have several layers :

  • My presentation layer
  • My domain model layer
  • My infrastructure layer
  • My business logic layer
  • And my data access layers.

For the moment, I log exceptions in my controllers methods using Dependency Injection.

private readonly ILogger<CustomerController> _logger;
public CustomerController(ILogger<CustomerController> logger)
{
     _logger = logger;
}

public bool TestLog()
{
     _logger.LogError((int)LoggingEvents.LIST_ITEMS, "Test info #543434");
     return true;
}

What is the best practice to use the logger in my others layers, like my data access layer ?

AdrienTorris
  • 9,111
  • 9
  • 34
  • 52

3 Answers3

3

Best is to use ILogger<T> only in your application layer (Controllers, application services - but not domain services).

If you want to add logging around your domain services/repositories, it'd be best to create decorators for it. This only works well if you have abstracted your interfaces from their implementations and use/inject interfaces everywhere).

However, there are limitations on what you can do with decorators. You can log before and after the call to a method defined in the public interface. Not inside the call.

Why avoid logging/injecting your logger into the service?

Coupling

You should avoid injecting ILogger<T> in your domain objects, as this couples your domain to ASP.NET Core logging framework/base classes. However, you can always define your own logger interface to use it within your domain and implement it as wrapper around ILogger<T>.

SOLID Principle

S in SOLID principle stand for SRP (Single Responsibility Principle) and says, one object should just have one and only one responsibility. Doing logging and persistence or businesses logic into one object violates this principle.

But in the end, you got to weight up costs of development with the benefits you gain with it. If it's a long living application (to be used for the next 10 years or so) and a complex one, logging as decorators makes sense for sure. If it's a small project with only a few weeks of development time, the benefit of this abstraction may not outweigh the costs.

Community
  • 1
  • 1
Tseng
  • 61,549
  • 15
  • 193
  • 205
  • 4
    *“as this couples your domain to ASP.NET Core logging framework/base classes”* – The ILogger interfaces included in ASP.NET Core are already very generic and open to other implementations; there’s really not much coupling here. It’s also in the Extensions namespace (not ASP.NET), so it’s a separate entity, and actually being considered to be adapted into .NET Core directly. – poke Feb 22 '16 at 16:18
  • @poke: Right, but if the interface is ever to change, it breaks your whole domain. When you have your own interface, then it only breaks if you modify it. If the .NET Core logger interfaces gets changed, only your wrapper/provider around it fails which isn't an issue, because it's infrastructure and outside of your domain. But it's up to the company/developer to decide who far to drive the abstraction :P – Tseng Feb 22 '16 at 16:22
2

The “best practice” would be to just inject own loggers for every component and let every component do its own logging.

If you think this becomes too noisy for your application, then you can change the verbosity level for namespace parts, so you only get e.g. Information level logs for your controllers, and Warning (or worse) for everything else:

loggerFactory.AddConsole(new ConsoleLoggerSettings()
{
    Switches = new Dictionary<string, LogLevel>()
    {
        ["MyNamespace.Services"] = LogLevel.Warning,
        ["MyNamsepace.Controllers"] = LogLevel.Information
    }
});
poke
  • 369,085
  • 72
  • 557
  • 602
  • Upvoted - not sure why someone had downvoted this? I think it may improve the answer to mention that it assumes dependency injection is going to be used throughout the other layers as well. – Nicholas Blumhardt Feb 23 '16 at 06:12
1

If you want to log unhandled exceptions, you should probably add a middleware that can take care of that. You can see an example in this SO answer: https://stackoverflow.com/a/31054664/5795

Community
  • 1
  • 1
henningst
  • 1,664
  • 2
  • 20
  • 30