如何在 C 中使用 Serilog 记录 classPath、methodName 和 lineNumber#

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

提问人:Miriam F. 提问时间:11/7/2023 最后编辑:Miriam F. 更新时间:11/16/2023 访问量:107

问:

我想将日志从我的系统打印到文件 (.NET Framework 4.6.1) 我想打印以下信息:className、methodName、lineNumber,从中调用了日志条目。

我有一个 Serilog 类: `

    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();
        }
    }

我正在尝试用日志记录系统:

 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**")
        }
    }

我应该在 SerilogClass 中添加什么? 我很乐意为您提供建议。谢谢。

C# serilog serilog-sinks-文件

评论

1赞 Mark Schultheiss 11/7/2023
也许考虑调用记录器的实际代码,而不是日志记录代码 re: stackoverflow.com/q/72258670/125981stackoverflow.com/q/6994852/125981 或其他几个相关问题
1赞 Mark Schultheiss 11/7/2023
有些属性已经存在了一段时间 ref: learn.microsoft.com/en-us/dotnet/api/...

答:

1赞 Terentiy Gatsukov 11/7/2023 #1

若要在 Serilog 中自动记录类名、方法名和行号,可以使用 Serilog.Enrichers.StackTrace NuGet 包。

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");
    }
}

下面是使用配置了 Serilog.Enrichers.StackTrace 的 Serilog 时日志条目的外观示例:

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

要像 Blindy 所说的那样,在不需要外部包的情况下自动记录类名、方法名和行号,可以使用 C# 提供的编译器服务功能,例如 、 和 。Serilog.Enrichers.StackTraceCallerMemberNameCallerFilePathCallerLineNumber

例:

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");
    }
}
2赞 Blindy 11/7/2023 #2

您可以使用 CallerMemberNameCallerLineNumberCallerFilePath 修饰的默认参数编写扩展方法,以生成所需的任何消息并将其转发到记录器。

作为上面 Terntiy 回答的一个好处,这种方法对运行时的影响为零,因为编译器会在编译时将参数注入为纯字符串,而堆栈跟踪扩充需要在运行时检查堆栈跟踪以提取信息(相当昂贵)。此方法也不需要调试符号来解码堆栈跟踪。

评论

0赞 Miriam F. 11/7/2023
首先,谢谢。你有例子吗?
0赞 Terentiy Gatsukov 11/7/2023
@MiriamF。我可以举个例子来补充我的答案。
0赞 Terentiy Gatsukov 11/7/2023
@MiriamF。做!
1赞 Miriam F. 11/7/2023
@TerentiyGatsukov - 谢谢!