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

38

我最近为我的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 [Information] Item created in database.

虽然这很好,但我想要在.txt文件中看到记录日志的类名和正在执行的方法名称。例如,当A类使用B方法向数据库写入内容并记录时,我想看到像这样的东西:

ClassA.MethodB:Item created in database

所有记录日志的类都在它们的构造函数中注入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 记录器? - mason
Microsoft.Extensions.Logging.ILogger - Jeroen
1
现在 https://github.com/serilog/serilog/issues/1084#issuecomment-358117004 上有一个代码示例可供使用。 - Nicholas Blumhardt
1
@NicholasBlumhardt 那段代码打印的是 Serilog.Extensions.Logging.SerilogLogger.Log(Microsoft.Extensions.Logging.LogLevel, Microsoft.Extensions.Logging.EventId, , System.Exception, ),而不是实际调用记录器的方法。 - Raj
3个回答

23

使用Jordan的答案这个答案的组合解决了该问题。

我通过添加日志上下文来改变我的Loggerconfiguration,使用增强,并将“方法(method)”属性添加到我的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();

通过使用LogContext.PushProperty()方法,Enrich.FromLogContext可以将属性推送到outputTemplate中。在这种情况下,针对“method”属性进行操作(请注意输出模板中的{Method})。

异步方法的示例:

using (LogContext.PushProperty("Method", new LogAsyncMethods().GetActualAsyncMethodName()))
{
    _log.LogInformation("Log message.");
}

GetActualAsyncMethodName()的写法如下:

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

对于异步方法,这很好用。

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

using (LogContext.PushProperty("Method", System.Reflection.MethodBase.GetCurrentMethod().Name))
{
    _log.LogInformation("Changing of customer name succeeded");
}

现在日志中显示了方法名。SourceContext 添加了命名空间 + 类名,并通过添加“.{Method}”得到以下结果:

命名空间.类名.方法名


14
请注意,通过反射在运行时获取当前方法可能会很昂贵;这里讨论了一种高效的替代方案,您可能可以根据自己的情况进行调整:https://dev59.com/bV0b5IYBdhLWcg3wM-4c 希望对您有所帮助! - Nicholas Blumhardt

10

如果你使用内置的日志工厂,那么在ASP.NET Core中使用SeriLog会比完整的.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");
}

假设您有一个类似于以下输出模板的模板:
```html

这假定您有一个类似于此的输出模板:

```
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,该对象将删除该属性以及在其后添加的任何属性。因此,通过将调用包装在using语句中,一旦在记录器上调用LogError方法,这些属性就会从上下文中删除,并且不会污染任何其他日志记录调用。

2
在您的日志记录器配置中,您需要使用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();

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

1
浏览文档后,不幸的是我找不到获取方法名称的任何内容。 - Jeroen
所以我刚刚注意到你的编辑,你提到它记录了命名空间,但没有记录方法,所以我的答案可能是不正确的。 - Jordan S. Jones
你可能需要像这样明确地写出来:{ // Your log stuff } - Jordan S. Jones
这将适用于普通方法。但是对于异步方法,这样做就不正确了。它将使用如此解释的“MoveNext”方法(在此处)。 - Jeroen
使用这个解决方案可以用于异步方法。然而我不喜欢它。因为log4net已经在其核心中实现了这个功能。 - Jeroen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接