Home > Mobile >  C# streamlined way to get Exception line number into Serilog
C# streamlined way to get Exception line number into Serilog

Time:06-24

I am using this code using serilog to get write the line number of the exception into my logs. The problem here is the line number is where the exception is thrown in the catch block and not the actual line where the exception occurred.

The Problem

try
{
  //line: 2
     throw new Exception();

}
catch(Exception ex)
{
    //line: 15
       Log.Here().Error(ex, "Hello, world!");
   //Serilog will log the exception line as line 15 instead of line 2 
   //which doesn't help me at all if I have 100s of lines of code 
   //that could throw an actual exception
}

This is my current Serilog code

public static class LoggerExtensions
{
    public static ILogger Here(this ILogger logger,
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string sourceFilePath = "",
        [CallerLineNumber] int sourceLineNumber = 0) {
        return logger
            .ForContext("MemberName", memberName)
            .ForContext("FilePath", sourceFilePath)
            .ForContext("LineNumber", sourceLineNumber);
    }
}

I use like this in each of my classes which also needs to be streamlined to accept any type T but that is another question for another day:

     Serilog.Log.ForContext<T>()

// at the beginning of the class

private static Serilog.ILogger Log => Serilog.Log.ForContext<MyClass>();

// in the method
Log.Here().Information("Hello, world!");


var outputTemplate = "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message}{NewLine}in method {MemberName} at {FilePath}:{LineNumber}{NewLine}{Exception}{NewLine}";

Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Warning()
            .Enrich.FromLogContext()
            .WriteTo.RollingFile("log/{Date}.log", outputTemplate, LogEventLevel.Warning)
            .WriteTo.Console(LogEventLevel.Warning, outputTemplate, theme: AnsiConsoleTheme.Literate)
            .CreateLogger();

The Partial Solution

Here is the code that I use to find the actual line where the exception originally occurred. Instead of using these 3 lines of code(stacktrace,frame,line) over and over and over in each catch block I want to find a way to use it once and automatically append it to the serilog output template correctly.

catch(Exception ex)
{
   var stackTrace = new StackTrace(ex, true);
   var frame = stackTrace.GetFrame(0);
   var line = frame.GetFileLineNumber();// This is what I need as the actual line number
   Log.Here().Error(ex, $"Exception occured on line # {line}"); //This works but doesn't follow the output template pattern
}

CodePudding user response:

Caller Information has been added to .NET 4.5. This will be compiled, a big improvement over having to examine the stack trace manually.

    public void Log(string message,
            [CallerFilePath] string filePath = "",
            [CallerLineNumber] int lineNumber = 0)
    {
        // Do logging
    }

CodePudding user response:

Just move the code from the catch block into your "Here()" method as such

public static class LoggerExtensions
{
    public static ILogger Here(this ILogger logger,
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string sourceFilePath = "",
        [CallerLineNumber] int sourceLineNumber = 0) {
      
       var stackTrace = new StackTrace(ex, true);
       var frame = stackTrace.GetFrame(0);
       var line = frame.GetFileLineNumber();

        return logger
            .ForContext("MemberName", memberName)
            .ForContext("FilePath", sourceFilePath)
            .ForContext("LineNumber", **line**);
    }
}

then simple just call the method as the same

Log.Here(ex).Error(ex, "Hello, world!");

or you can just write the log directly in the method as such

public static class LoggerExtensions
    {
        public static ILogger Here(this ILogger logger, Exception ex, string message,
            [CallerMemberName] string memberName = "",
            [CallerFilePath] string sourceFilePath = "",
            [CallerLineNumber] int sourceLineNumber = 0) {
          
           var stackTrace = new StackTrace(ex, true);
           var frame = stackTrace.GetFrame(0);
           var line = frame.GetFileLineNumber();
            
                LogContext.PushProperty("MemberName", memberName)
                LogContext.PushProperty("FilePath", sourceFilePath)
                LogContext.PushProperty("LineNumber", **line**);
          Log.Error(ex, message);
        }
    }

and simply call as so

Log.Here(ex, "Hello, world!");
  • Related