0

I need to programatically create loggers for each controller (let's say: Documents, Customers and Warehouses, to log some operations inside them) and for a filter above controllers (to log parameters sent to each action). Each controller logic should be logged to another file ex. Documents.csv, Customers.csv and Warehouses.csv.

Currently I have a wrapper for nlog logger. The wrappers are injected to controllers instances via constructor and then nlog logger inside in initialized via LogManager. Initialization creates new target and loglevel and assigns it to LogManager.Configuration.

The problem is that after a few requests each loggers logs to each file, so ex. logic from Customers is logged to Customers.csv and vice versa, same thing with logger from Filters.

How should I then configure separate blog loggers with different target for each controller and separate for filter?

I prefer programmatic configuration than via xms etc.

Adapter as requested:

public class Logger : ILogger
{
    private NLog.Logger _logger;
    string _deployVersion;

    public Logger(string deploymentVersion)
    {
        _deployVersion = deploymentVersion;
    }

    public void Init(string loggerName)
    {
        _logger = NLog.LogManager.GetLogger(loggerName);
    }

    public void Init(string loggerName, string header, string layout, Level level)
    {
        LoggingConfiguration config;
        if (NLog.LogManager.Configuration == null)
        {
            config = new LoggingConfiguration();
        }
        else
        {
            config = LogManager.Configuration;
        }

        if (config.FindTargetByName(loggerName) == null)
        {
            var target = CreateTarget(loggerName, header, layout, level); //configures target: path, archives and layout
            config.AddTarget(loggerName, target);

            var logLevel = GetLogLevel(level); //translates Level enum to NLog level
            var rule1 = new LoggingRule("*", logLevel, target);
            config.LoggingRules.Add(rule1);

            LogManager.Configuration = config;
        }
        _logger = LogManager.GetLogger(loggerName);
    }
    ...
    //Info, Debug etc. methods
4
  • 1
    "Currently I have a wrapper for nlog logger." - Can you provide this is in your question so answers can adapt it where necessary? Commented Jan 3, 2017 at 9:16
  • Wrapper code added :) Commented Jan 3, 2017 at 9:32
  • if the requirement is "one file per controller", then there is a far more easier approach possible. Or am I missing something? Commented Jan 3, 2017 at 15:15
  • @Julian the thing is loggers are passed to some classes below and used there, not directly in controllers. But I want to use logic created in such architecture for another log file. Yeah, I know the whole loggin could've been done better, but no time for refactor now :/ G0dsquad - thanks, I'll try to use your solution soon and will come back to the answer :) Commented Jan 5, 2017 at 10:39

1 Answer 1

2

Great, you've used an interface here already which makes a pattern for this easier to produce.

Your main problem here is responsibility, as your controller code will (I assume) call Init(string loggerName, string header, string layout, Level level). This is probably not best practice as you may have to repeat this code a lot and the controller probably shouldn't care about where the log goes or what it's formatted like...just the fact that a log is used.

Instead of injecting these directly, use a Factory to obtain the correct logger type. For example, CustomerLogger : ILogger:

public class LogFactory
{
    public ILogger Get<T>() where T : ILogger
    {
        ILogger instance = null;
        if (typeof(T) == typeof(CustomerLogger))
        {
            instance = (T)Activator.CreateInstance(typeof(T), "CustomerLogger", "Header", "Layout", Level.Verbose);
        } 
        else if (...)
        {
            ...etc
        }
        return instance;
    }
} 

This means you can leave the management and creation of ILogger concretes up to the Factory.

Notice I'm passing in string loggerName, string header, string layout, Level level so you can call Init in the CustomerLogger constructor. You then have different implementations of ILogger to suit your needs.

You can then either make the LogFactory use an interface and inject that into your controller, or simply new up the factory in the controller, either way you call LogFactory.Get<T>(). Option A would be better for unit testing purposes.

Hope this helps!

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.