Reputation: 634
I wrote a class which is intended to provide robust logging messages. One of the features I want to include is to include the class and method from which a call to generate a log message originated.
The problem I'm having is that StackTrace.GetFrame behaves inconsistently between debug and release builds. As far as I can tell, it behaves as expected when I execute programs from VS in debug. When I execute a release build deliverable, StackTrace.GetFrame throws a NullReferenceException.
Here's a simplified example of my code:
using Logging;
namespace MyApp
{
class Program
{
public static void Main(string[] args)
{
Logger Log = new Log(Level.INFO);
Log.Info(@"Hello, world!");
}
}
}
namespace Logging
{
class Logger
{
//Assume a constructor
public void Info(string message)
{
//We will need to go 3 method calls back from StandardEntry()
//to get the name of the method which called Log.Info
StandardEntry(text, Level.INFO, 3);
}
//This method is called by Logger.Info, Logger.Error, etc
private void StandardEntry(string message, Level entryLevel, int frameCount)
{
if (entryLevel >= this.level)
{
string message = $"[{CallingMethod(frameCount)}] {<entryLevel.ToString()>} {message}\r\n";
File.AppendText(this.LogPath, message);
}
}
private string CallingMethod()
{
string retVal;
StackTrace st = new StackTrace();
StackFrame sf = st.GetFrame(frameCount);
MethodBase callingMethod = sf.GetMethod();
return $"{callingMethod.ReflectedType.Name}.{callingMethod.Name}";
}
}
}
I've tried playing around with the value I'm passing in for frameCount, but a lower number returns a method call that is still within the Logger class. I need the method that actually called Log.Info
When running a release deliverable, Log.Info() in Program.Main throws a NullReferenceException, which leads me to believe that it's trying to access a stack frame index that is beyond the scope of the invoked executable.
When I debug in Visual Studio, there is no exception and CallingMethod() returns the expected expression.
Upvotes: 1
Views: 1116
Reputation: 1196
In release builds, one of the optimizations the JIT compiler performs is inlining methods, i.e. inserting the code of a called method directly into the method calling it. This means that some methods may simply disappear from the stack trace you're seeing.
What you can do instead is add parameters to the Info
method together with the caller information attributes which are made for scenarios like this. The C# compiler will automatically fill in the information for those parameters at compile time, so they're not affected by runtime optimizations.
Upvotes: 1
Reputation: 94
It seems that class CallerMemberNameAttribute is what you actually needs.
Upvotes: 0