How to Use Attributes in ASP.NET Core - Windows ASP.NET Core Hosting 2024 | Review and ComparisonWindows ASP.NET Core Hosting 2024 | Review and Comparison

If you’re a seasoned .NET developer, you most likely know about the importance of logging: when used in an IT context, the term logging defines the process of keeping track of all the events occurring within the application (and their context information) and output them to a dedicated storage channel – be it a text file, a database table, and so on.

As we might easily guess, the primary purpose of logging is to keep track of the various interactions between the software and its users: state changes, access to internal resources, event handlers that trigger in response to user actions, exceptions thrown by internal modules, and so on.

Here at Ryadel we’ve published several posts about how to implement logging in .NET, by either leveraging the built-in ILogger interface or implementing a third-party solution such as Serilog. In this post we’ll further expand that topic introducing three attributes provided by the System.Runtime.CompilerServices namespace that we can use to get useful info about the execution context, thus being very useful for logging purposes: CallerMemberNameCallerFilePath, and CallerLineNumber.

CallerMemberName

Let’s start with the [CallerMemberName] attribute, which can be used to obtain the method or property name of the caller to the method.

Here’s how we can use it:

using System.Diagnostics;
using System.Runtime.CompilerServices;

public void SomeTask()
{
    Log("Logging info.");
}

public void Log(string message,
        [CallerMemberName] string memberName = "")
{
    Trace.WriteLine("message: " + message);
    Trace.WriteLine("member name: " + memberName);
}

As we can see by looking at the above code, the [CallerMemberName] attribute must be applied to an optional string parameter that has a default value: that parameter (memberName) will contain the name of the caller – in our scenario, the SomeTask method.

If the SomeTask method is executed, it will produce the following output:

message: Logging info.
member name: SomeTask

That’s a convenient way to retrieve the name of the caller member.

CallerFilePath

The [CallerFilePath] attribute works in a similar way, but instead of retrieving the caller member’s name, it will retrieve the full path of the source file that contains the caller: in other words, the class file path at the time of compile.

Here’s how we can use it:

using System.Diagnostics;
using System.Runtime.CompilerServices;

public void SomeTask()
{
    Log("Logging info.");
}

public void Log(string message,
        [CallerFilePath] string filePath = "")
{
    Trace.WriteLine("message: " + message);
    Trace.WriteLine("source file: " + filePath);
}

Again, the [CallerFilePath] attribute must be applied to an optional string parameter that has a default value: that parameter (filePath) will contain the source path of the caller.

If the SomeTask method is executed, it will produce the following output:

message: Logging info.
source file: C:\Projects\MySampleProject\Program.cs

CallerLineNumber

Having the member name and the source file can be great for error logging, as it allows us to instantly know the method that is not working as intended and its source file: however, if the method’s implementation has several logging tasks, it might be hard to locate the precise line of code that called the log method which generated the log entry we are analyzing.

The [CallerLineNumber] attribute can be useful to fill that gap since it allows us to obtain the line number in the source file at which the method is called.

Here’s how we can use it:

using System.Diagnostics;
using System.Runtime.CompilerServices;
 
public void SomeTask()
{
    Log("Logging info.");
}
 
public void Log(string message,
        [CallerFilePath] string filePath = "",
        [CallerLineNumber] int lineNumber = 0)
{
    Trace.WriteLine("message: " + message);
    Trace.WriteLine($"method name: {methodName)"); 
    Trace.WriteLine($"source file: {filePath} (line {lineNumber})"); 
}

Notice how, this time, we used the [CallerMethodName],  [CallerFilePath], and [CallerLineNumber] attributes together,  to see how we can precisely locate the name, the source file, and the line number of the caller member.

If the SomeTask method is executed, it will produce the following output:

message: Logging info.
method name: SomeTask
source file: C:\Projects\MySampleProject\Program.cs (line 32)

This information can be very useful to understand what happened and where.

Conclusion

The [CallerMethodName],  [CallerFilePath], and [CallerLineNumber] attributes can be very useful to obtain valuable info regarding our execution context: their ideal usage is for logging and monitoring purposes, but they can definitely come in handy in other scenarios as well.