How do I get CallerFilePath and CallerLineNumber without using the CallerInfo attributes?
Asked Answered
P

2

8

For my log4net solution, I have an API wrapper that uses the CallerInfo attributes, e.g.

    public void Write(string message,
                            [CallerMemberName] string memberName = "",
                            [CallerFilePath] string filePath = "",
                            [CallerLineNumber] int lineNumber = 0)

However, I am also using Unity Interception so that I can perform trace logging of the before/after responses, e.g. using ICallHandler like below in the Invoke method.

public class TraceCallHandler : ICallHandler
{
...

   public IMethodReturn Invoke(IMethodInvocation input, 
                               GetNextHandlerDelegate getNext)
    {
        //---- Trace method inputs
        this.LogInfoBeforeInvoke(input);

        //---- invoking the target method
        InvokeHandlerDelegate next = getNext();
        IMethodReturn methodReturn = next(input, getNext);

        //---- invoking the target method
        this.LogInfoAfterInvoke(methodReturn.ReturnValue); 
    }
}

Note: The above code is in no way complete/correct... but just wanted to show you what I was doing for Unity Interception.

My question / challenge is this: when I eventually call log.Write(...), I want the target's caller info, not my TraceCallHandler info.

e.g. for method name, I can do this:

   string methodName = input.MethodBase.Name;

How do I get the Caller's File Path and Caller's Line Number? Is it even possible to do via reflection?

Thanks!

Priggery answered 23/10, 2014 at 16:13 Comment(2)
didnt you ask this exact same question a couple days ago?Aloisius
kind-of. I deleted that question as someone marked it as duplicate, but it was not. I already know how to get the method name... I wanted the file path and line number.Priggery
P
8

Yes, you can get these using reflection:

var sf = new System.Diagnostics.StackTrace(1).GetFrame(0);
Console.WriteLine(" File: {0}", sf.GetFileName());
Console.WriteLine(" Line Number: {0}", sf.GetFileLineNumber());
// Note that the column number defaults to zero 
// when not initialized.
Console.WriteLine(" Column Number: {0}", sf.GetFileColumnNumber());

However as it says clearly in the documentation:

StackFrame information will be most informative with Debug build configurations. By default, Debug builds include debug symbols, while Release builds do not. The debug symbols contain most of the file, method name, line number, and column information used in constructing StackFrame objects.

So if all you want this for is debugging, then enable it in debug builds and log away. In Release builds though it will be at best unhelpful and at worst downright misleading as apart from the symbol considerations above the compiler will aggressively inline methods and reorder things and generally mess with your stuff.

Petersham answered 23/10, 2014 at 22:55 Comment(3)
Thanks for the great 'answer', but I can't mark it as an answer as the entire point of having log tracing is for troubleshooting problems in a production releases :(Priggery
You're going to have the same problem whatever approach you use, though, unless you include debug info in the release builds.Petersham
That doesn't surprise me - async stack traces are incomprehensible. EnhancedStackTrace.Current() from nuget.org/packages/Ben.Demystifier/0.1.1 might be worth a look.Petersham
C
2

I just ran across this issue and thought I would share what I learned. First, when you include [CallerFilePath] in a method argument a side effect is that the full path of the file, including any user identifiable data, will be included in your .exe. I created a simple program with one method. I created an exe. I then added a [CallerFilePath] attribute to the test function. When I compared the results of strings.exe (from sysinternals), the one with the attribute differed in that it included the full path of my source file.

c:\users\<my name>\documents\visual studio 2015\Projects\TestCallerAttribute\TestCallerAttribute\Program.cs

The answer above by stuartd is correct in that you will not be able to get the data you want from the stack trace in a release build.

There is a solution to getting strong data however: Event Tracing for Windows. From msdn: "Event Tracing for Windows (ETW) is an efficient kernel-level tracing facility that lets you log kernel or application-defined events to a log file. You can consume the events in real time or from a log file and use them to debug an application or to determine where performance issues are occurring in the application."

This is not a quick solution. There is work in setting up the events and the listeners to get the provenance you need. The long term payoff is strong.

Cryometer answered 28/7, 2016 at 14:42 Comment(1)
The user identifiable data can be avoided by simply putting this in the .csproj file: <PathMap>$(SolutionDir)=</PathMap>Eastlake

© 2022 - 2024 — McMap. All rights reserved.