覆写 默认的 ASP.NET Core 日志配置

988 阅读3分钟

摘录自:Link

为什么需要 覆写 默认的日志配置?

默认的,日志 仅写入 控制台 或者 调试窗口

  • 有时 需要写入文件或数据库
  • 有时 希望扩展日志记录的其他信息

更改 默认的日志配置 属于哪个层次?

ASP.NET Core 框架的层次,自底而上:

  • Host
  • Middleware
  • Routing
  • MVC\SignalR\gRPC\Blazor\Other
  • WebAPI\Razor Pages

修改 日志的 配置 属于Host层

在哪里 更改 日志的默认配置?

ASP.NET Core 2.0 之前,日志 在Startup.cs中配置

ASP.NET Core 2.0 之后,Startup.cs慢慢简化许多配置 被移动Program.csWebHostBuilder

ASP.NET Core 2.0 之后,日志的配置 也被移动到Program.csWebHostBuilder

Program.cs 变得 更通用

ASP.NET Core 3.1 之后, Program.cs变得 更加通用

  • IHostBuilder最先创建 (引导启动的关键)
  • 通过IHostBuilder,可以创建IWebHostBuilder
  • IWebHostBuilder 配置
public class Program {
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
    
    public static IHostBuilder CreateHostBuilder(string[] args) => 
        Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => {
            webBuilder.UseStartup<Startup>();
    });
}

ASP.NET Core 几乎允许 覆盖和自定义 所有内容

IWebHostBuilder 有很多扩展方法,允许我们覆盖 各种 默认行为

如何修改 日志的默认配置?

覆盖 日志的默认设置,需要使用ConfigureLogging方法

public class Program {
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
    
    public static IHostBuilder CreateHostBuilder(string[] args) => 
        Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => {
            webBuilder
            .ConfigureLogging( (hostingContext, logging) => {
                logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                logging.AddConsole();
                logging.AddDebug();
            })
            .UseStartup<Startup>();
    });
}

什么是 迷你API?

ASP.NET Core 6.0中,微软引入了 简化配置的 迷你API方法 (minimal API)

  • 不使用Startup.cs文件
  • 而是将 所有配置 添加到 Program.cs文件
  • WebApplication.CreateBuilder() 创建一个 builder
  • builder上搞配置
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();
var app = builder.Build();

使用 迷你API方式 配置日志:

builder.Logging

  • AddConfiguration()
    • builder.Configuration 可以得到那个叫 Logging 的 section
  • AddConsole()
  • AddDebug() 即:在builder上,再加:
builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging"));
builder.Logging.AddConsole();
builder.Logging.AddDebug();

如何 使用 自定义的 日志框架

这里,还是用 迷你API的方式 配置日志: builder.Logging

  • ClearProviders()
    • 清除 之前添加的 所有日志框架 提供程序
  • AddProvider()
    • 参数是 一个自定义类 ColoredConsoleLoggerProvider
      • 实现了接口 ILoggerProvider
      • 可以返回ILogger
      • 构造函数的参数: 一个自定义类 ColoredConsoleLoggerConfiguration
using LoggingSample;

builder.Logging.ClearProviders();

var config = new ColoredConsoleLoggerConfiguration
{
    LogLevel = LogLevel.Information,
    Color = ConsoleColor.Red
};
builder.Logging.AddProvider(new ColoredConsoleLoggerProvider(config));
public class ColoredConsoleLoggerConfiguration
{
    public LogLevel LogLevel { get; set; } = LogLevel.Warning;
    public int EventId { get; set; } = 0;
    public ConsoleColor Color { get; set; } = ConsoleColor.Yellow;
}
public class ColoredConsoleLoggerProvider : ILoggerProvider
{
    private readonly ColoredConsoleLoggerConfiguration _config;
    private readonly ConcurrentDictionary<string, ColoredConsoleLogger> _loggers = new ConcurrentDictionary<string,ColoredConsoleLogger>();
    public ColoredConsoleLoggerProvider (ColoredConsoleLoggerConfiguration config)
    {
        _config = config;
    }
    public ILogger CreateLogger(string categoryName)
    {
        return _loggers.GetOrAdd(categoryName,name => new ColoredConsoleLogger(name, _config));
    }
    
    public void Dispose()
    {
        _loggers.Clear();
    }
}
public class ColoredConsoleLogger : ILogger
{
    private static readonly object _lock = new Object();
    private readonly string _name;
    private readonly ColoredConsoleLoggerConfiguration _config;

    public ColoredConsoleLogger(string name, ColoredConsoleLoggerConfiguration config)
    {
        _name = name;
        _config = config;
    }

    public IDisposable BeginScope<TState>(TState state)
    {
        return null;
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        return logLevel == _config.LogLevel;
    }

    public void Log<TState>(
        LogLevel logLevel,
        EventId eventId,
        TState state,
        Exception exception,
        Func<TState, Exception, string> formatter
    )
    {
        if (!IsEnabled(logLevel))
        {
            return;
        }

        lock (_lock)
        {
            if (_config.EventId == 0 || _config.EventId == eventId.Id)
            {
                var color = Console.ForegroundColor;
                Console.ForegroundColor = _config.Color;
                Console.Write($"{logLevel} - ");
                Console.Write($"{eventId.Id} - {_name} - ");
                Console.Write($"{formatter(state, exception)}n");
                Console.ForegroundColor = color;
            }
        }
    }
}

  • 使用lock是因为:控制台本身 并不是线程安全的,可能出现错误的着色

其实,用不着自己写日志框架,已经有许多优秀的第三方日志框架可用:

  • ELMAH
  • log4net
  • NLog

如何 使用第三方日志框架 NLog

NLog是,最早的一款 可以用于ASP.NET Core的日志框架

NLog 提供了 一个日志记录 提供程序 插件。可以方便地插入ASP.NET Core

  1. 配置NLog: 添加一个NLog.Config文件
    • 一般,将标准消息记录在一个日志文件中
    • 自定义消息记录在另一个文件中
  2. 引入NuGet
dotnet add package NLog.Web.AspNetCore

3.将NLogIWebHostBuilder结合

  • 方式一:
    • .UseNLog()
Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => {
    webBuilder.ConfigureLogging((hostingContext, logging) => {
        logging.ClearProviders();
        logging.SetMinimumLevel(LogLevel.Trace);
    })
    .UseNLog()
    .UseStartup<Startup>();
});
  • 方式二:使用迷你API
    • builder.WebHost.UseNLog()
using NLog.Web;

var builder = WebApplication.CreateBuilder(args);

builder.Logging.ClearProviders();
builder.Logging.SetMinimumLevel(LogLevel.Trace);
builder.WebHost.UseNLog();