When logging, you always get entangled in string literals.
I solved that nicely for properties, fields and variables by passing an Expression<Func<T>> expression
(as explained here), so you can do things like this:
public void Demo(string someArgument)
{
LogFrameWork.LogLine("Demo"); // goal is to get rid of these string literals
LogFramework.Log(() => someArgument);
}
I want to do something similar for the method Demo
itself:
public void Demo(string someArgument)
{
LogFramework.Log(this.Demo);
}
I tried things like this:
public static void Log(Delegate method)
{
string methodName = method.Method.Name;
LogLine(methodName);
}
and this:
public static void Log(Action method)
{
string methodName = method.Method.Name;
LogLine(methodName);
}
But I get compiler errors like these:
Argument 1: cannot convert from 'method group' to 'System.Delegate'
Argument 1: cannot convert from 'method group' to 'System.Action'
I could introduce a bunch of overloads using Func<...> and Action<...>, but that sounds overly complex.
Is there a way to cover this for any method with any number of parameters and an optional result?
--jeroen
PS: I think this question might have some relevance here, but no answers that got me a 'aha' feeling :-)
You can also achieve this without using ExpressionTrees through System.Diagnostics.StackTrace
.
StackTrace trace = new StackTrace();
And then:
trace.GetFrame(0).GetMethod().Name
To get the MethodInfo and then name of the current method, or:
trace.GetFrame(1).GetMethod().Name
To get the calling method.
Instead of trying to pass the method in as a parameter to your logger, look at it from the perspective of having the logger identify the calling method.
Here's an (pseudo) example:
Logger Class
public void Debug( string message )
{
message = string.Format( "{0}: {1}", GetCallingMethodInfo(), message );
// logging stuff
}
/// <summary>
/// Gets the application name and method that called the logger.
/// </summary>
/// <returns></returns>
private static string GetCallingMethodInfo()
{
// we should be looking at the stack 2 frames in the past:
// 1. for the calling method in this class
// 2. for the calling method that called the method in this class
MethodBase method = new StackFrame( 2 ).GetMethod();
string name = method.Name;
string type = method.DeclaringType.Name;
return string.Format( "{0}.{1}", type, name );
}
Anywhere that uses the logger:
// resides in class Foo
public void SomeMethod()
{
logger.Debug("Start");
}
The output from the logger will then be: Foo.SomeMethod: Start
This is much harder than it looks. I think you might be best with the generic Func and Action overloads, but there is a way to do this with expression trees. Here's an example in LINQPad:
public static void Log(Expression<Action> expr)
{
Console.WriteLine(((MethodCallExpression)expr.Body).Method.Name);
}
void Main()
{
Log(() => DoIt());
Log(() => DoIt2(null));
Log(() => DoIt3());
}
public void DoIt()
{
Console.WriteLine ("Do It!");
}
public void DoIt2(string s)
{
Console.WriteLine ("Do It 2!" + s);
}
public int DoIt3()
{
Console.WriteLine ("Do It 3!");
return 3;
}
This outputs:
DoIt DoIt2 DoIt3
Note that I had to use lambdas and specify dummy arguments when calling the Log method.
This is based on Fyodor Soikin's excellent answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With