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

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

{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}

例如:

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}) {Message}{NewLine}{Exception}")

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

【问题讨论】:

  • 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, ) 而不是实际的方法调用记录器

标签: c# asp.net logging asp.net-core serilog


【解决方案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}) {Message}{NewLine}{Exception}")
    .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}”将导致:

命名空间.类名.方法名

【讨论】:

【解决方案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}] {SourceContext}{NewLine}Message:{Message}{NewLine}in method {MemberName} at {FilePath}:{LineNumber}{NewLine}{Exception}{NewLine}";

当您调用扩展方法时,方法、文件和行号由各自的属性获取。将属性推送到 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,它只是在其核心中实现了这一点。
    猜你喜欢
    • 2017-02-15
    • 1970-01-01
    • 1970-01-01
    • 2018-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-19
    相关资源
    最近更新 更多