13

We're using NLog for logging in an C# MVC3 web application. All of our controllers extend a custom base "ApplicationController" that gives us access to a constantly needed methodes and s members.

I'd like all controllers to have access to the Logger via this base class, but want the detail of knowing what derived class the log statements originated in.

Our application controller looks like this:

public abstract class ApplicationController : Controller
{
    protected Logger _logger;
    protected virtual Logger Logger
    {
        get { return _logger ?? (_logger = LogManager.GetCurrentClassLogger()); }
    }

    protected ApplicationController()
    {
        Context = new Entities();
    }

If a derived controller doesn't override the Logger than all statements will show they originated from the Application controller. Currently, I've got essentially the same Logger statement in all derived controllers. For example:

public class PropertyController : ApplicationController
{
    private readonly DatatapeService _datatapeService;
    private readonly PropertyService _propertyService;
    protected override Logger Logger
    {
        get { return _logger ?? (_logger = LogManager.GetCurrentClassLogger()); }
    }

Obviously this is poor implementation practice.

  1. How can I dry this up? Specifically, what is my understanding of C# lacking to accomplish exactly this specific task?
  2. Is there a logging pattern that I should be following where I'm not exposing the logging class (NLog) directly?

TIA!

Bobby B
  • 2,372
  • 3
  • 24
  • 31

4 Answers4

15

I am unfamiliar with NLog but in Log4Net the syntax LogManager.GetLogger(this.GetType()) will accomplish what you want. GetTypereturns the leaf type in your inheritance hierarchy, even if called in the base ApplicationController class, when the logger is first created (ie: on first access to the Logger property) it will instantiate it with type PropertyController

Saifur
  • 16,081
  • 6
  • 49
  • 73
Dr. Andrew Burnett-Thompson
  • 20,980
  • 8
  • 88
  • 178
  • 1
    I think `this.GetType().Name` should do it. – H H Jan 03 '12 at 11:24
  • 1
    This is exactly what I went with. I didn't realize that this.GetType would return the most derived class. This allowed me to create a single property on my ApplicationController and then use that logger throughout the application. – Bobby B Jan 04 '12 at 14:34
  • Update 07/2017: They (NLog) now adress this in their Tutorial. See: https://github.com/NLog/NLog/wiki/Tutorial#expose-logger-to-sub-classes – Fildor Jul 28 '17 at 12:20
6

NLog API is slightly different than Log4net. You need to use

Logger = LogManager.GetLogger(GetType().Name);

if you only pass the type, LogManager will expect a logger type (i.e. a custom logger)

Sebastian Slutzky
  • 382
  • 1
  • 5
  • 22
  • 1
    I find LogManager.GetLogger(GetType().FullName) to be more usefull – David Hayes Jun 08 '16 at 14:58
  • Thanks @DavidHayes -- agree with you. So the equivalent to `GetCurrentClassLogger()` for a base class property would use: `get { return _logger ?? (_logger = LogManager.GetLogger(this.GetType().FullName)); }` – Beel Dec 03 '16 at 15:51
2

The How to create Logger for sub classes page of the NLog wiki now suggests the following pattern:

class BaseClass
{
    protected BaseClass()
    {
        Log = LogManager.GetLogger(GetType().ToString());
    }

    protected Logger Log { get; private set; }
}

class ExactClass : BaseClass
{
    public ExactClass() : base() { }
    ...
}
Holistic Developer
  • 2,458
  • 1
  • 18
  • 27
-1

Just check nLog wiki here

In most cases you will have one logger per class, so it makes sense to give logger the same name as the current class.

Makes sense to do it like this

public abstract class ApplicationController : Controller
{
    protected Logger _logger;
    protected virtual Logger Logger(string className)
    {
       return LogManager.GetLogger(className);
    }
}


public class PropertyController : ApplicationController
{
    private readonly DatatapeService _datatapeService;
    private readonly PropertyService _propertyService;
    protected override Logger Logger()
    {
        return base.Logger("PropertyController ");
    }
}
ccellar
  • 10,326
  • 2
  • 38
  • 56
Anand
  • 14,545
  • 8
  • 32
  • 44
  • 6
    I really just don't like that approach. If I do that, I'm copy and pasting code in every controller. It feels wrong to have that much duplication. – Bobby B Jan 04 '12 at 14:33
  • @BobbyB snippet, common base class? NLog uses a flyweight pattern for logger configurations so the overhead is minimal. The lines that due the actual logging are largely duplicate. – Gusdor Dec 03 '15 at 13:51