3

I'm writing an audit logging system to be integrated into an existing piece of software, and the database structure includes a field for a blob of detail about the action being logged. Doesn't have to be anything fancy; it won't be needed much, and when it is, in most cases it'll be read by the devs to find out which method was called and with what parameters, stuff like that. This means that while it should probably be human-readable, it doesn't need to be parsed by anything. So, rather than have lots of overloads, or use horrible switch case string replacements, I figured a nice way to write the detail blob would be for a logging call to get the names of the parameters required by the action being logged, and the values that were passed to them. I've done some research, and found something promising in the reflection methods, but I've yet to find anything that explains clearly how to do it.

Here's a rough mock of what I'm after:

Dictionary<string,string> parameters = new Dictionary<string,string>();
foreach (Parameter p in MethodCall)
{
    parameters.Add(p.Name, p.Value.ToString());
}

LogEvent(EventType.Something, userID = thisUser, details = parameters);

I understand it's likely to be more complex than that in practice, but it gives you the general idea. Inside my LogEvent method there's some stuff to add timestamps, construct descriptive sentences for the user-end log viewer (less technical detail than we show to admins and devs, so as not to expose our program/database structure) and - in theory - build some kind of blob (probably JSON or similar) from the Dictionary of parameter details.

anaximander
  • 7,083
  • 3
  • 44
  • 62

4 Answers4

1

Related is: Obtain parameter values from a stack frame in .NET? which has some useful answers.

You want to log the method name and the parameter list. The method name is easy using StackFrame but I don't think you can use simple reflection to get the parameter values out.

Community
  • 1
  • 1
Jason Kleban
  • 20,024
  • 18
  • 75
  • 125
1

If your logging method takes an expression parameter, you can get parameter names and values by this code:

static void LogSomething(Expression<Action> expression)
{
    var methodCallExpression = ((MethodCallExpression) expression.Body);
    var parameterNames = methodCallExpression.Method.GetParameters().Select(p => p.Name);
    var values = methodCallExpression.Arguments.Select(a => a.Value);
}
tukaef
  • 9,074
  • 4
  • 29
  • 45
  • Looks promising. Do you know what the overhead is on this? As this is for a logging method, it's going to be added in to the software in numerous places, and I don't want that to slow it down too much. An extra half-second or so here and there is fine, but if the whole thing starts taking forever then I'll have to find another way to do it. – anaximander Nov 06 '12 at 13:39
  • @anaximander, reflection without invoking methods or getting values of properties, fields works fast. Getting parameter values from expression tree is fast too. So I think it will take far less than a half-second :) – tukaef Nov 06 '12 at 17:50
1

I would use a Interceptor (Castle.DynamicProxy) like in my example below:

  internal class Program
  {
    private static void Main(string[] args)
    {

      var interceptor = new Interceptor();
      var businessObjectClass = new ProxyGenerator().CreateInterfaceProxyWithoutTarget<IBusinessObjectClass>(interceptor);

      businessObjectClass.Method3("what",123);

      Console.ReadLine();
    }
  }

  public class Interceptor : IInterceptor
  {
    #region IInterceptor Members

    public void Intercept(IInvocation invocation)
    {
      Console.WriteLine(string.Format("Intercepted call to: " + invocation.Method.Name));
      foreach (var argument in invocation.Arguments)
      {
        Console.WriteLine(string.Format("\t Param: " + argument.ToString()));
      }
      invocation.Proceed();
    }

    #endregion
  }

  public interface IBusinessObjectClass
  {
    void Method3(string stringValue, int intValue);
  }

  public class BusinessObjectClass : IBusinessObjectClass
  {

     public void Method3(string stringValue, int intValue)
    {
      return;
     }

  }

DynamicProxy

Fredrik Claesson
  • 609
  • 7
  • 12
  • Interesting... so this intercepts method calls? This might be worth looking into; it'd be nice if people didn't have to explicitly call the logging stuff. Having said that, I don't need to log EVERY method call, so there would need to be some logic to decide what to log... Worth researching though. – anaximander Nov 08 '12 at 09:48
  • Yes, this intercepts all method calls that are implementing the interface which is being proxied. – Fredrik Claesson Nov 08 '12 at 17:43
0

Why don't you try PostSharp? It is meant to solve problems like yours. In the samples you will find a logging aspect that prints parameter values. Combined with XML configuration, will allow the administrator/user to enable logging/tracing at will.

Panos Rontogiannis
  • 4,154
  • 1
  • 24
  • 29