C# ASP.NET Core Serilog 添加类名和方法到日志

Posted

技术标签:

【中文标题】C# ASP.NET Core Serilog 添加类名和方法到日志【英文标题】:C# ASP.NET Core Serilog add class name and method to log 【发布时间】:2018-05-14 13:49:06 【问题描述】:

我最近在我的 ASP.Net Core 项目中添加了日志记录。目前日志以这种格式写入 .txt 文件:

Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz [Level] MessageNewLineException

例如:

2017-11-30 13:58:22.229 +01:00 [信息] 在数据库中创建的项目。

这很好,但我想知道记录的类的名称和正在执行到这个 .txt 文件的方法。例如,当 A 类使用方法 B 将某些内容写入数据库并记录此内容时,我希望看到类似

ClassA.MethodB:在数据库中创建的项目

所有记录日志的类都将它们的 Logger 注入到它们的构造函数中,例如

public class ClassA

    private readonly ILogger _log;

    public ClassA(ILogger<ClassA> log)
        _log = log;
    

    public void AddItemToDb(Item item)
        //Add item
        //On success: 
        _log.LogInfo("Added item to db.");
    

我目前正在使用 Serilog 并使用以下 LoggerConfiguration:

var logger = new LoggerConfiguration()
    .MinimumLevel.Verbose()
    .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
    .WriteTo.RollingFile(Configuration.GetValue<string>("LogFilePath") + "-Date.txt", LogEventLevel.Information)
    .CreateLogger();

如何将类和方法添加到我的日志中?

编辑

我向 .WriteTo.Rollingfile() 方法添加了一个自定义 outputTemplate,如下所示:

"Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz [Level] (SourceContext) MessageNewLineException")

导致命名空间加上类要添加到日志中,现在只缺少方法

【问题讨论】:

ILogger 是什么类型?是 Serilog 记录器吗?还是 ASP.NET Core 记录器? Microsoft.Extensions.Logging.ILogger 现在github.com/serilog/serilog/issues/1084#issuecomment-358117004 有一个代码示例可以适应这个。 @NicholasBlumhardt 代码打印 Serilog.Extensions.Logging.SerilogLogger.Log(Microsoft.Extensions.Logging.LogLevel, Microsoft.Extensions.Logging.EventId, , System.Exception, ) 而不是实际的方法调用记录器 【参考方案1】:

我通过结合使用Jordan's 答案和this 答案解决了这个问题。

我通过enrichment 添加日志上下文来更改我的 Logger 配置,并将属性“方法”添加到我的 outputTemplate:

var logger = new LoggerConfiguration()
    .MinimumLevel.Verbose()
    .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
    .Enrich.FromLogContext()
    .WriteTo.RollingFile(Configuration.GetValue<string>("LogFilePath") + "-Date.txt", LogEventLevel.Information, 
        outputTemplate: "Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz [Level] (SourceContext.Method) MessageNewLineException")
    .CreateLogger();

Enrich.FromLogContext 允许使用 LogContext.PushProperty() 方法将属性推送到 outputTemplate。在这种情况下,对于“方法”属性(请注意 outputTemplate 中的 Method)。

async 方法示例:

using (LogContext.PushProperty("Method", new LogAsyncMethods().GetActualAsyncMethodName()))

    _log.LogInformation("Log message.");

GetActualAsyncMethodName() 的写法如下:

public static string GetActualAsyncMethodName([CallerMemberName]string name = null) => name;

这适用于 async 方法。

现在对于 非异步 方法可以正常工作:

using (LogContext.PushProperty("Method", System.Reflection.MethodBase.GetCurrentMethod().Name))

    _log.LogInformation("Changing of customer name succeeded");

现在方法名称显示在日志中。 SourceContext 添加命名空间 + 类,通过添加“.Method”将导致:

命名空间.类名.方法名

【讨论】:

请记住,在运行时通过反射获取当前方法会很昂贵;这里讨论了一种您可能能够适应您的场景的有效替代方案:***.com/questions/29470863/…HTH!【参考方案2】:

如果您使用的是内置记录器工厂,则将 SeriLog 与 ASP.NET Core 一起使用会稍微落后于完整的 .NET F/W。你可以通过编写一系列这样的扩展方法来解决这个问题:

public static class LoggerExtensions

    public static void LogAppError<T>(this ILogger<T> logger, EventId eventId, Exception exception, string message,
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string sourceFilePath = "",
        [CallerLineNumber] int sourceLineNumber = 0)
    
        using (var prop = LogContext.PushProperty("MemberName", memberName))
        
            LogContext.PushProperty("FilePath", sourceFilePath);
            LogContext.PushProperty("LineNumber", sourceLineNumber);
            logger.LogError(eventId, exception, message);
        
    

你的调用代码如下:

public PeopleController(ILogger<PeopleController> logger)

    _logger.LogAppError("Ctor");

这假设您有一个类似这样的输出模板:

private static string outputTemplate =
    @"[Timestamp:HH:mm:ss Level] SourceContextNewLineMessage:MessageNewLinein method MemberName at FilePath:LineNumberNewLineExceptionNewLine";

当您调用扩展方法时,方法、文件和行号由各自的属性获取。将属性推送到 LogContext 会返回一个 IDisposable,该 IDisposable 将删除该属性以及在其后添加的任何属性。因此,通过将调用包装在 using 语句中,一旦在记录器上调用 LogError 方法,属性就会从上下文中删除,并且不会污染任何其他记录调用。

【讨论】:

【参考方案3】:

在您的记录器配置中,您需要enrich with the LogContext:

var logger = new LoggerConfiguration()
    .MinimumLevel.Verbose()
    .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
    .Enrich.FromLogContext()
    .WriteTo.RollingFile(
        Configuration.GetValue<string>("LogFilePath") + "-Date.txt", 
        LogEventLevel.Information)
    .CreateLogger();

但是,老实说,我不记得它是否记录了方法名称。

【讨论】:

查看文档我很遗憾找不到任何方法名称 所以我刚刚注意到您的编辑,您提到它正在记录命名空间,而不是方法,所以我的回答是不正确的。 你可能需要明确表达using (LogContext.PushProperty("Method", MethodBase.GetCurrentMethod().Name)) // Your log stuff 这将适用于普通方法。但是,使用异步方法将无法正常工作。如here 所述,它将使用“MoveNext”方法 使用this 解决方案适用于异步方法。不过我不喜欢。对于 log4net,它只是在其核心中实现了这一点。

以上是关于C# ASP.NET Core Serilog 添加类名和方法到日志的主要内容,如果未能解决你的问题,请参考以下文章

在 ASP.NET Core 中使用 Serilog 使用 Fluentd 将日志写入 Elasticsearch

创建返回 Serilog LoggerConfiguration 的方法(Asp.Net Core 2.0)

在带有数据库的 ASP.NET Core 5.0 应用程序中使用 Serilog 实现日志记录

在 ASP.NET Core 2.2 中使用 InProcess 托管模型时,Serilog 不会将日志写入文件

[翻译] ASP.NET Core 利用 DockerElasticSearchKibana 来记录日志

如何在 ASP.NET Core MVC 中添加分页,在 AJAX 调用中动态创建的表上