How to log the classPath, methodName and lineNumber with Serilog in C#

490 views Asked by At

I want to print log to file from my system (.NET Framework 4.6.1) I want to print this information: className, methodName, lineNumber from which log entry was Called.

I've a Serilog class: `

    public class SerilogClass
    {
        public static readonly ILogger _log;
        static SerilogClass()
        {
            string logsFolder = ConfigurationManager.AppSettings["logsFolder"];
            string environment = ConfigurationManager.AppSettings["environment"];
            _log = new LoggerConfiguration()
                    .MinimumLevel.Debug()
                    .WriteTo.File(logsFolder + "//" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".log", outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:Ij}{NewLine}{Exception}")
                    .CreateLogger();
        }
    }

I'm trying to document the system with logs:

 public class MyClass
    {
        private readonly ILogger _log = SerilogClass._log;

        public void GetData()
        {
             _log.information("run GetData, **here I want to print by default the class path and the line number**")
        }
    }

What should I add in SerilogClass? I would be happy to advices. Thanks.

2

There are 2 answers

1
Terentiy Gatsukov On BEST ANSWER

To automatically log the class name, method name, and line number in Serilog, you can use the Serilog.Enrichers.StackTrace NuGet package.

public class SerilogClass
{
    public static readonly ILogger _log;
    static SerilogClass()
    {
        string logsFolder = ConfigurationManager.AppSettings["logsFolder"];
        string environment = ConfigurationManager.AppSettings["environment"];
        _log = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .Enrich.WithDemystifiedStackTraces() // Add this to enrich with stack trace information
                .WriteTo.File(
                    path: Path.Combine(logsFolder, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log"),
                    outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext}.{Method}({LineNumber}): {Message:lj}{NewLine}{Exception}")
                .CreateLogger();
    }
}

public class MyClass
{
    private readonly ILogger _log = SerilogClass._log.ForContext<MyClass>();

    public void GetData()
    {
        _log.Information("Run GetData");
    }
}

Below is an example of how a log entry might look when using Serilog configured with the Serilog.Enrichers.StackTrace:

[15:42:01 INF] MyNamespace.MyClass.GetData(42): Run GetData

To automatically log the class name, method name, and line number without the need for external packages like Serilog.Enrichers.StackTrace as Blindy said, you can use the compiler services features provided by C# such as CallerMemberName, CallerFilePath, and CallerLineNumber.

Example:

public static class SerilogExtensions
{
    public static void LogWithDetailedInfo(
        this ILogger logger,
        string message,
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string sourceFilePath = "",
        [CallerLineNumber] int sourceLineNumber = 0)
    {
        var className = Path.GetFileNameWithoutExtension(sourceFilePath);
        logger.Information("{ClassName}.{MemberName}({LineNumber}): {Message}", className, memberName, sourceLineNumber, message);
    }
}

public static class SerilogClass
{
    public static readonly ILogger Log;

    static SerilogClass()
    {
        string logsFolder = "path_to_logs_folder"; // Update with your configuration method
        string environment = "development"; // Update with your configuration method
        Log = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .WriteTo.File(
                    path: Path.Combine(logsFolder, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log"),
                    outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
                .CreateLogger();
    }
}

public class MyClass
{
    private readonly ILogger _log = SerilogClass.Log.ForContext<MyClass>();

    public void GetData()
    {
        _log.LogWithDetailedInfo("Run GetData");
    }
}
4
Blindy On

You can write an extension method with default arguments decorated with CallerMemberName, CallerLineNumber and CallerFilePath to build whatever message you need and forward it to your logger.

As a bonus over Terntiy's answer above, this approach has zero run-time impact since the compiler will inject the parameters at compile time as plain strings, whereas the stack trace enrichment needs to inspect the stack trace at runtime to extract information (fairly expensive). This approach also doesn't require debug symbols to decode the stack trace.