8

I want to log method entry and exit using NLog. I found two approaches. First one, using PostSharp,but it needs to be purchased. Second approach,using unity,But we can implement it only on methods having interfaces.

For example, I have one controller called,SampleController

SampleController.cs

 public string Get()
        {
            BusinessLayerClass businessLayer = new BusinessLayerClass();
            return businessLayer.BusinessLayerMethod();
        }

BusinessLayerClass.cs

public class BusinessLayerClass
    {
        public string BusinessLayerMethod()
        {
            DataLayerClass dataLayerClass = new DataLayerClass();
           return  dataLayerClass.DataLayerMethod();
        }
    }

DataLayerClass.cs

public class DataLayerClass
    {
        public string DataLayerMethod()
        {
            return "Hi";
        }
    }

I have two classes BusinessLayerClass and DataLayerClass.Get method in the sample controller calls BusinessLayerMethod in the BusinessLayerClass and from which DataLayerMethod was called. I have a NLogging class for logging purpose

NLogging.cs

public static class NLogging
    {
        public static bool Enabled
        {
            get { return LogManager.IsLoggingEnabled(); }
            set
            {
                if (value)
                {
                    while (!Enabled) LogManager.EnableLogging();
                }
                else
                {
                    while (Enabled) LogManager.DisableLogging();
                }
            }
        }

        public static void Fatal(string message, Exception exception = null, [CallerFilePath] string callerPath = "", [CallerMemberName] string callerMember = "", [CallerLineNumber] int callerLine = 0)
        {
            Log(LogLevel.Fatal, message, exception, callerPath, callerMember, callerLine);
        }
        public static void Trace(string message, Exception exception = null, [CallerFilePath] string callerPath = "", [CallerMemberName] string callerMember = "", [CallerLineNumber] int callerLine = 0)
        {
            Log(LogLevel.Trace, message, exception, callerPath, callerMember, callerLine);
        }

        public static void Debug(string message, Exception exception = null, [CallerFilePathAttribute] string callerPath = "", [CallerMemberName] string callerMember = "", [CallerLineNumber] int callerLine = 0)
        {
            Log(LogLevel.Debug, message, exception, callerPath, callerMember, callerLine);
        }

        public static void Info(string message, Exception exception = null, [CallerFilePathAttribute] string callerPath = "", [CallerMemberName] string callerMember = "", [CallerLineNumber] int callerLine = 0)
        {
            Log(LogLevel.Info, message, exception, callerPath, callerMember, callerLine);
        }

        public static void Warn(string message, Exception exception = null, [CallerFilePathAttribute] string callerPath = "", [CallerMemberName] string callerMember = "", [CallerLineNumber] int callerLine = 0)
        {
            Log(LogLevel.Warn, message, exception, callerPath, callerMember, callerLine);
        }

        public static void Error(string message, Exception exception = null, [CallerFilePathAttribute] string callerPath = "", [CallerMemberName] string callerMember = "", [CallerLineNumber] int callerLine = 0)
        {
            Log(LogLevel.Error, message, exception, callerPath, callerMember, callerLine);
        }

        private static void Log(LogLevel level, string message, Exception exception = null, string callerPath = "", string callerMember = "", int callerLine = 0)
        {
            LogManager.ThrowExceptions = true;
            var logger = LogManager.GetLogger(callerPath);

            if (!logger.IsEnabled(level)) return;

            var logEvent = new LogEventInfo(level, callerPath, message) { Exception = exception };
            logEvent.Properties.Add("callerpath", callerPath);
            logEvent.Properties.Add("callermember", callerMember);
            logEvent.Properties.Add("callerline", callerLine);

            logger.Log(logEvent);
        }
    }

I cannot use Unity here, since BusinessLayerClass and DataLayerClass don't implement interfaces.

Calling NLogging.Trace(methodname) in every method will be inconvenient. For example If I change my method name, I need to change the logging code also, like NLogging.Trace("Entered into ModifiedBusinessLayerMethod").

Is there any other way to log method entry and exit without using these two approaches?

Julian
  • 33,915
  • 22
  • 119
  • 174
Kesiya Abraham
  • 753
  • 3
  • 10
  • 24
  • With C# 6 you can do NLogging.trace(nameof(BusinessLayerMethod)) to avoid problems if method name changes. – 3615 Jul 12 '16 at 10:18
  • Thanks for you response.It is a good answer.But If I add 10 classes to the project and these classes have lot of methods.At this point,it is not possible to add NLogging.trace(nameof(MathodName)) in all these methods.Anyother way?I mean I want a global logging method,which will executed before the actual method execution. – Kesiya Abraham Jul 12 '16 at 10:29
  • I'm not aware of any good way of doing what you want. Quite similar question was discussed before **[here](http://stackoverflow.com/questions/25803/how-do-i-intercept-a-method-call-in-c)**. I would go or with IoC or with tons of traces. – 3615 Jul 12 '16 at 11:02
  • Thank you,I think It will be helpful. – Kesiya Abraham Jul 12 '16 at 11:13

2 Answers2

8

You could use a Fody and the MethodDecorator.Fody add-in. Fody is an free open-source code weaving library.

How to set-up:

1. Install package

PM> Install-Package MethodDecorator.Fody

2. Decorate the method

public class BusinessLayerClass
    {
        [LogMethod] 
        public string BusinessLayerMethod()
        {
            DataLayerClass dataLayerClass = new DataLayerClass();
           return  dataLayerClass.DataLayerMethod();
        }
    }

3. Write the interceptor

 using System;
 using System.Reflection;

[module: LogMethod] // Atribute should be "registered" by adding as module or assembly custom attribute

// Any attribute which provides OnEntry/OnExit/OnException with proper args
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Assembly | AttributeTargets.Module)]
public class LogMethodAttribute : Attribute, IMethodDecorator
{
    private MethodBase _method;
    // instance, method and args can be captured here and stored in attribute instance fields
    // for future usage in OnEntry/OnExit/OnException
    public void Init(object instance, MethodBase method, object[] args)
    {
        _method = method;
    }
    public void OnEntry()
    {
        NLogging.Trace("Entering into {0}", _method.Name);
    }

    public void OnExit()
    {
        NLogging.Trace("Exiting into {0}", _method.Name);
    }

    public void OnException(Exception exception)
    {
        NLogging.Trace(exception, "Exception {0}", _method.Name);
    }
}
Julian
  • 33,915
  • 22
  • 119
  • 174
1

Use Tracer for Fody

It is highly configurable and gives you immediate results without forcing you to decorate your code. You can auto-log every single method call or exercise fine-grained control with explicit inclusion and exclusion of particular classes and methods.

https://github.com/csnemes/tracer

For most situations, the other suggested answers are too complicated and time-consuming compared to the elegance afforded by Tracer.

JamesHoux
  • 2,999
  • 3
  • 32
  • 50