第二章 基础知识(4) - 日志记录

在默认日志级别,Blazor项目中默认提供如下日志记录提供程序:

在服务器上(Blazor Server),日志记录仅发生LogLevel.Information 或更高级别的 Development 环境中的服务器端 .NET 控制台。

在客户端上(Blazor WebAssembly),日志记录仅发生LogLevel.Information 或更高级别的客户端浏览器开发人员工具控制台。

Razor组件日志记录

要在组件中使用日志记录,可以通过注入ILogger<T>或者ILoggerFactory来实现。

ILogger<T>示例

csharp 复制代码
@page "/"
@inject ILogger<Home> Logger

<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>

Welcome to your new app.
@code{
    protected override void  OnInitialized(){
        //这是服务端的组件
        Logger.LogWarning("OKK");
    }
}

ILoggerFactory示例

csharp 复制代码
@page "/counter"
@rendermode InteractiveAuto
@inject ILoggerFactory LoggerFactory

<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
        //这里用的客户端的组件
        LoggerFactory.CreateLogger<Counter>().LogWarning("OKK");
    }
}

客户端日志

对于客户端,并非支持所有ASP.NET Core的日志记录功能。例如,客户端组件无法访问客户端的文件系统或网络,因此无法将日志写入客户端的物理或网络存储。

一、设置日志等级

对于服务端项目的日志配置,默认情况下,可以在项目的appsettings.jsonappsettings.Development.json文件中进行配置。
而在客户端项目中,要进行日志最低等级的配置,需要在Program.cs中,使用WebAssemblyHostBuilder.Logging属性进行日志的设置。

日志级别

  • Trace:0,包含最详细消息的日志,不建议在生产环境下使用此等级。
  • Debug:1,在开发期间用于交互式调查的日志。这些日志应该主要包含对调试有用的信息,没有长期价值。
  • Information:2,跟踪应用的常规流,可能具有长期值。
  • Warning:3,突出显示应用程序流中异常或意外事件,但不会导致应用程序执行停止的日志。
  • Error:4,突出显示由于故障而停止当前执行流的日志。这些应该表明当前活动中的故障,而不是应用程序范围的故障。
  • Critical:5,描述不可恢复的应用程序或系统崩溃,或需要立即注意的灾难性故障的日志。
  • None:6,不进行任何日志记录。

示例-将日志最低等级设置为Warning-Program.cs

csharp 复制代码
......
//设置日志级别
builder.Logging.SetMinimumLevel(LogLevel.Warning);
......

二、客户端Program.cs中输出日志

要在客户端的Program.cs中输出日志,可以使用WebAssemblyHost的服务容器中获取日志工厂ILoggerFactory从而生成日志对象。

  • 需要注意的是,只有在加载首次加载客户端组件时,才会运行Program中的代码。

Program.cs

csharp 复制代码
var builder = WebAssemblyHostBuilder.CreateDefault(args);

var host = builder.Build();
var logger = host.Services.GetRequiredService<ILoggerFactory>()
    .CreateLogger<Program>();
logger.LogInformation("Logged after the app is built in the Program file.");

await builder.Build().RunAsync();

三、日志类别

客户端同样支持日志类别,在创建 ILogger 对象时,需要指定日志类别,可以以下几种类别指定方式:

  • ILogger<T>:在注入ILogger接口时,通过泛型T来指定,这种方式需要使用已存在的类型,然后会自动使用T类型的完整空间命名来作为日志类别。
  • CreateLogger<T>()ILoggerFactory的方法,创建ILogger<T>日志对象。
  • CreateLogger(className)ILoggerFactory的方法,创建ILogger日志对象,这里的className可以随便指定,不需要使用存在的类型名称。

示例

csharp 复制代码
@page "/counter"
@rendermode InteractiveAuto
@inject ILogger<Counter> Logger
@inject ILoggerFactory LoggerFactory

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
        LoggerFactory.CreateLogger("MyLogger").LogWarning("OKK");
        Logger.LogWarning("OKK");
    }
}

四、日志事件ID

Blazor客户端侧同样支持日志事件ID,每个日志都可以指定一个事件ID,一般会将所有日志事件ID作为常量封装到一个类中来使用。

LogEvent.cs

jsx 复制代码
public class LogEvent
{
    public const int Event1 = 1000;
    public const int Event2 = 1001;
}

Counter.razor

csharp 复制代码
@page "/counter"
@rendermode InteractiveAuto
@inject ILogger<Counter> Logger

......

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
        Logger.LogInformation(LogEvent.Event1, "Someone has clicked me!");
        Logger.LogWarning(LogEvent.Event2, "Someone has clicked me!");
    }
}

五、日志异常参数

Blazor客户端侧支持日志异常参数的使用。

Counter.razor

csharp 复制代码
@page "/counter"
@rendermode InteractiveAuto
@inject ILogger<Counter> Logger

......

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        try
        {
            currentCount++;
            throw new OperationCanceledException("Skip 3");
        }
        catch (Exception ex)
        {
            Logger.LogInformation(ex, "Exception (currentCount: {Count})!", currentCount);
        }
    }
}

六、日志筛选函数

指定日志类别与日志级别

在客户端如果希望只输出指定的日志类别和日志级别,可以通过在Program.cs中使用builder.Logging.AddFilter方法来添加过滤器。

Program.cs

csharp 复制代码
......

//表示只记录日志类别为MyLoggerClass且日志级别为LogLevel.Information的日志
builder.Logging.AddFilter((provider, category, logLevel)=> category!.Equals("MyLoggerClass") && logLevel == LogLevel.Information);

......

Counter.razor

csharp 复制代码
@page "/counter"
@rendermode InteractiveAuto
@inject ILoggerFactory LoggerFactory

......

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
        ILogger _logger = LoggerFactory.CreateLogger("MyLoggerClass");
        _logger.LogInformation("Information");
        _logger.LogWarning("Warning");
    }
}

七、身份验证日志记录

要开启身份验证的日志记录,可以有如下两种方法:

通过配置文件设置

csharp 复制代码
"Logging": {
  "LogLevel": {
    "Microsoft.AspNetCore.Components.WebAssembly.Authentication": "Debug"
  }
}

通过在Program.cs中添加过滤器

csharp 复制代码
builder.Logging.AddFilter("Microsoft.AspNetCore.Components.WebAssembly.Authentication", LogLevel.Debug);

八、自定义记录器提供程序

有时可能会遇到需要自己定义日志提供程序的需求,要实现自定义的日志提供程序,具体需要进行如下步骤:

创建日志级别字典

csharp 复制代码
using Microsoft.Extensions.Logging;

public class CustomLoggerConfiguration
{
    public int EventId { get; set; }

    public Dictionary<LogLevel, LogFormat> LogLevels { get; set; } = 
        new()
        {
            [LogLevel.Information] = LogFormat.Short,
            [LogLevel.Warning] = LogFormat.Short,
            [LogLevel.Error] = LogFormat.Long
        };

    public enum LogFormat
    {
        Short,
        Long
    }
}

实现ILogger接口,自定义日志提供程序

csharp 复制代码
public sealed class CustomLogger : ILogger
{
    private readonly string name;
    private readonly Func<CustomLoggerConfiguration> getCurrentConfig;

    public CustomLogger(
        string name,
        Func<CustomLoggerConfiguration> getCurrentConfig) =>
        (this.name, this.getCurrentConfig) = (name, getCurrentConfig);

    public IDisposable BeginScope<TState>(TState state) => default!;

    public bool IsEnabled(LogLevel logLevel) =>
        getCurrentConfig().LogLevels.ContainsKey(logLevel);

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

        CustomLoggerConfiguration config = getCurrentConfig();

        if (config.EventId == 0 || config.EventId == eventId.Id)
        {
            switch (config.LogLevels[logLevel])
            {
                case LogFormat.Short:
                    Console.WriteLine($"{name}: {formatter(state, exception)}");
                    break;
                case LogFormat.Long:
                    Console.WriteLine($"[{eventId.Id, 2}: {logLevel, -12}] {name} - {formatter(state, exception)}");
                    break;
                default:
                    // No-op
                    break;
            }
        }
    }
}

实现自定义日志提供程序的配置类

csharp 复制代码
using System.Collections.Concurrent;
using Microsoft.Extensions.Options;

[ProviderAlias("CustomLog")]
public sealed class CustomLoggerProvider : ILoggerProvider
{
    private readonly IDisposable onChangeToken;
    private CustomLoggerConfiguration config;
    private readonly ConcurrentDictionary<string, CustomLogger> loggers =
        new(StringComparer.OrdinalIgnoreCase);

    public CustomLoggerProvider(
        IOptionsMonitor<CustomLoggerConfiguration> config)
    {
        this.config = config.CurrentValue;
        onChangeToken = config.OnChange(updatedConfig => this.config = updatedConfig);
    }

    public ILogger CreateLogger(string categoryName) =>
        loggers.GetOrAdd(categoryName, name => new CustomLogger(name, GetCurrentConfig));

    private CustomLoggerConfiguration GetCurrentConfig() => config;

    public void Dispose()
    {
        loggers.Clear();
        onChangeToken.Dispose();
    }
}

ILoggingBuilder添加扩展方法

csharp 复制代码
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Configuration;

public static class CustomLoggerExtensions
{
    public static ILoggingBuilder AddCustomLogger(
        this ILoggingBuilder builder)
    {
        builder.AddConfiguration();

        builder.Services.TryAddEnumerable(
            ServiceDescriptor.Singleton<ILoggerProvider, CustomLoggerProvider>());

        LoggerProviderOptions.RegisterProviderOptions
            <CustomLoggerConfiguration, CustomLoggerProvider>(builder.Services);

        return builder;
    }
}

Program.cs中,添加日志提供程序。

csharp 复制代码
builder.Logging.ClearProviders().AddCustomLogger();

使用第三方日志提供程序

这里以NLog的使用为例,由于浏览器存在访问权限等原因,这里只在服务端(Blazor Server)上使用。

一、安装NLog包

要在 ASP.NET Core 6 中使用 NLog,首先要安装 NLog 的扩展包,然后再进行一些基本的配置,就可以实现 NLog 与.NET Core 6 的集成了。

首先到Nuget中,下载对应的依赖库:

  • 选择NLog.Extensions.Logging程序包,这个是微软参与开发的插件,也是微软官方网站上推荐的,兼容性也最好。

二、添加配置文件

NLog包安装完成之后,还需要配置才可使用,NLog使用的是NLog.config配置文件。

在项目中填加新项,选择Web配置文件,命名为NLog.config。
NLog.config

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<nlog
	xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	autoReload="true"
	internalLogLevel="Warn"
	internalLogFile="nlog.txt">
	<targets>
		<target xsi:type="File" name="allfile" fileName="nlog-all-${shortdate}.log" layout="${longdate}|${logger}|${uppercase:${level}}|${message} ${exception}" />
		<target xsi:type="File" name="ownFile-web" fileName="nlog-own-${shortdate}.log" layout="${longdate}|${logger}|${uppercase:${level}}| ${message} ${exception}"/>
	</targets>
	<rules>
		<logger name="*" minlevel="Trace" writeTo="allfile" />
		<logger name="Microsoft.*" minlevel="Trace" final="true" />
		<logger name="*" minlevel="Trace" writeTo="ownFile-web" />
	</rules>
</nlog>

三、注册NLog

NLog的包和配置文件完成之后,可以在Program类中注册NLog日志提供程序。

NLog要使用ILoggerFactory来提供注入服务,这里我们在统一的Program类中注册NLog。
AddNLog([string path])ILoggingBuilder的扩展方法,为Blzor框架添加NLog日志提供程序。

Program.cs

csharp 复制代码
......

//清除掉所有的日志服务
builder.Logging.ClearProviders();
//添加NLog日志服务
builder.Logging.AddNLog();

......

这里试了一下,如果不清除掉所有的日志服务,直接添加NLog服务,可以保留其他日志提供程序的日志记录。

四、使用NLog

Home.razor

csharp 复制代码
@page "/"
@inject ILoggerFactory LoggerFactory

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

@code{
    protected override void OnInitialized(){
        ILogger _logger = LoggerFactory.CreateLogger<Home>();
        _logger.LogInformation("Information");
        _logger.LogWarning("Warning");
    }
}
相关推荐
疯一样的码农2 分钟前
Python 正则表达式(RegEx)
开发语言·python·正则表达式
&岁月不待人&24 分钟前
Kotlin by lazy和lateinit的使用及区别
android·开发语言·kotlin
StayInLove28 分钟前
G1垃圾回收器日志详解
java·开发语言
无尽的大道36 分钟前
Java字符串深度解析:String的实现、常量池与性能优化
java·开发语言·性能优化
爱吃生蚝的于勒39 分钟前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
binishuaio1 小时前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git
zz.YE1 小时前
【Java SE】StringBuffer
java·开发语言
就是有点傻1 小时前
WPF中的依赖属性
开发语言·wpf
洋2401 小时前
C语言常用标准库函数
c语言·开发语言
进击的六角龙1 小时前
Python中处理Excel的基本概念(如工作簿、工作表等)
开发语言·python·excel