ASP.NET Core SignalR 中的日志记录和诊断信息

作者:Andrew Stanton-Nurse

本文提供从 ASP.NET Core SignalR 应用收集诊断信息以帮助解决问题的指南。

服务器端日志记录

警告

服务器端日志可能包含来自应用的敏感信息。 切勿将来自生产应用的原始日志发布到 GitHub 等公共论坛。

SignalR 是 ASP.NET Core 的一部分,因此它使用 ASP.NET Core 日志记录系统。 在默认配置中, SignalR 记录最少的信息,但可以配置日志记录级别。 有关配置 ASP.NET Core 日志记录的详细信息,请参阅 ASP.NET Core 日志记录上的文档。

SignalR 使用两个记录器类别:

  • Microsoft.AspNetCore.SignalR:用于与中心协议、激活中心、调用方法和其他与中心相关活动的日志。
  • Microsoft.AspNetCore.Http.Connections:用于与传输相关的日志,例如 WebSocket、长轮询、服务器发送的事件以及低级 SignalR 基础结构。

若要启用详细日志,请在文件中将上述两个前缀配置为Debug级别,并将以下项添加到Logging中的LogLevel子节:SignalR

{
    "Logging": {
        "LogLevel": {
            "Default": "Debug",
            "System": "Information",
            "Microsoft": "Information",
            "Microsoft.AspNetCore.SignalR": "Debug",
            "Microsoft.AspNetCore.Http.Connections": "Debug"
        }
    }
}

在代码中,可以在CreateWebHostBuilder方法中配置用于SignalR记录器类别的日志级别。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .ConfigureLogging(logging =>
        {
            logging.AddFilter("Microsoft.AspNetCore.SignalR", LogLevel.Debug);
            logging.AddFilter("Microsoft.AspNetCore.Http.Connections", LogLevel.Debug);
        })
        .UseStartup<Startup>();

如果未使用基于 JSON 的配置,请在配置系统中设置以下配置值:

  • Logging:LogLevel:Microsoft.AspNetCore.SignalR = Debug
  • Logging:LogLevel:Microsoft.AspNetCore.Http.Connections = Debug

查看配置系统的文档以确定如何指定嵌套的配置值。 例如,使用环境变量时,使用两个 _ 而不是 : 字符(例如 Logging__LogLevel__Microsoft.AspNetCore.SignalR)。

建议在为应用收集更详细的诊断时使用 Debug 级别。 该 Trace 级别产生低级诊断信息,很少会用到以诊断应用中的问题。

访问服务器端日志

访问服务器端日志的方式取决于运行应用的环境。

作为 IIS 外部的控制台应用

如果在控制台应用中运行,则默认情况下应启用控制台记录器。 SignalR 日志显示在控制台中。

在 Visual Studio 的 IIS Express 中

Visual Studio 在“输出”窗口中显示日志输出。 选择“ASP.NET Core Web 服务器”下拉选项。

Azure 应用服务

在 Azure 应用服务门户的“诊断日志”部分中启用“应用程序日志记录(Filesystem)”选项,并将级别配置为 。 日志应可从日志流式处理服务和应用服务文件系统的日志中获取。 有关详细信息,请参阅 Azure 日志流式处理

其他环境

有关配置适用于不同部署环境的日志记录提供程序(如 Docker、Kubernetes 或 Windows 服务)的详细信息,请参阅 .NET Core 中的日志记录和 ASP.NET Core

JavaScript 客户端日志记录

警告

客户端日志可能包含来自应用的敏感信息。 切勿将来自生产应用的原始日志发布到 GitHub 等公共论坛。

使用 JavaScript 客户端时,可以使用 configureLogging 上的 HubConnectionBuilder 方法配置日志记录选项:

let connection = new signalR.HubConnectionBuilder()
    .withUrl("/my/hub/url")
    .configureLogging(signalR.LogLevel.Debug)
    .build();

configureLogging方法中指定signalR.LogLevel.None以禁用框架日志记录。 请注意,某些日志记录由浏览器直接发出,无法通过设置日志级别禁用。

下表显示了可用于 JavaScript 客户端的日志级别。 将日志级别设置为其中一个值,便可以在该表的该级别及其以上所有级别进行日志记录。

级别 说明
None 不记录任何消息。
Critical 指示整个应用失败的消息。
Error 指示当前操作失败的消息。
Warning 指示非严重问题的消息。
Information 信息性消息。
Debug 对调试有帮助的诊断消息。
Trace 用于诊断特定问题的非常详细的诊断消息。

配置详细程度后,日志将写入浏览器控制台(或 NodeJS 应用中的标准输出)。

如果要将日志发送到自定义日志记录系统,可以提供实现 ILogger 接口的 JavaScript 对象。 唯一需要实现的方法是 log,它采用事件的级别和与事件关联的消息。 例如:

import { ILogger, LogLevel, HubConnectionBuilder } from "@microsoft/signalr";

export class MyLogger implements ILogger {
    log(logLevel: LogLevel, message: string) {
        // Use `message` and `logLevel` to record the log message to your own system
    }
}

// later on, when configuring your connection...

let connection = new HubConnectionBuilder()
    .withUrl("/my/hub/url")
    .configureLogging(new MyLogger())
    .build();
import { ILogger, LogLevel, HubConnectionBuilder } from "@aspnet/signalr";

export class MyLogger implements ILogger {
    log(logLevel: LogLevel, message: string) {
        // Use `message` and `logLevel` to record the log message to your own system
    }
}

// later on, when configuring your connection...

let connection = new HubConnectionBuilder()
    .withUrl("/my/hub/url")
    .configureLogging(new MyLogger())
    .build();

.NET 客户端日志记录

警告

客户端日志可能包含来自应用的敏感信息。 切勿将来自生产应用的原始日志发布到 GitHub 等公共论坛。

若要从 .NET 客户端获取日志,可以对 ConfigureLogging 使用 HubConnectionBuilder 方法。 其工作方式与对 ConfigureLoggingWebHostBuilder 使用 HostBuilder 方法相同。 可以配置与在 ASP.NET Core 中使用的相同日志记录提供程序。 但是,必须为单个日志记录提供程序手动安装和启用 NuGet 包。

若要将 .NET 客户端日志记录添加到 Blazor WebAssembly 应用,请参阅 ASP.NET Core Blazor 日志记录

控制台日志记录

若要启用控制台日志记录,请添加 包。 然后,使用 AddConsole 方法配置控制台记录器:

var connection = new HubConnectionBuilder()
    .WithUrl("https://example.com/my/hub/url")
    .ConfigureLogging(logging =>
    {
        // Log to the Console
        logging.AddConsole();

        // This will set ALL logging to Debug level
        logging.SetMinimumLevel(LogLevel.Debug);
    })
    .Build();

调试输出窗口日志记录

日志可配置为转到 Visual Studio 中的 “输出 ”窗口。 安装 Microsoft.Extensions.Logging.Debug 包并使用 AddDebug 方法:

var connection = new HubConnectionBuilder()
    .WithUrl("https://example.com/my/hub/url")
    .ConfigureLogging(logging =>
    {
        // Log to the Output Window
        logging.AddDebug();

        // This will set ALL logging to Debug level
        logging.SetMinimumLevel(LogLevel.Debug)
    })
    .Build();

其他日志记录提供程序

SignalR 支持其他日志记录提供程序,例如 Serilog、Seq、NLog 或与 Microsoft.Extensions.Logging 集成的任何其他日志记录系统。 如果日志记录系统提供 ILoggerProvider,可以使用 AddProvider 注册它:

var connection = new HubConnectionBuilder()
    .WithUrl("https://example.com/my/hub/url")
    .ConfigureLogging(logging =>
    {
        // Log to your custom provider
        logging.AddProvider(new MyCustomLoggingProvider());

        // This will set ALL logging to Debug level
        logging.SetMinimumLevel(LogLevel.Debug)
    })
    .Build();

控制详细程度

从应用中的其他地方进行日志记录时,将默认级别更改为Debug时可能会过于详细。 筛选器可用于配置 SignalR 的记录级别。 这可以通过代码完成,其方式与在服务器上相同:

var connection = new HubConnectionBuilder()
    .WithUrl("https://example.com/my/hub/url")
    .ConfigureLogging(logging =>
    {
        // Register your providers

        // Set the default log level to Information, but to Debug for SignalR-related loggers.
        logging.SetMinimumLevel(LogLevel.Information);
        logging.AddFilter("Microsoft.AspNetCore.SignalR", LogLevel.Debug);
        logging.AddFilter("Microsoft.AspNetCore.Http.Connections", LogLevel.Debug);
    })
    .Build();

跟踪 SignalR

SignalR中心服务器和SignalR客户端提供关于连接和消息的信息SignalR,使用DiagnosticSourceActivity。 SignalR 具有中心服务器和客户端的 ActivitySource,从 .NET 9 开始提供。

ActivitySource 是用于分布式跟踪的组件,用于创建和管理活动或跨度段,这些活动或跨度段代表应用程序中的操作。 这些活动可用于:

  • 跟踪跨不同组件和服务的请求和操作流向。
  • 提供有关应用程序性能和行为的宝贵见解。

.NET SignalR 服务器 ActivitySource

名为 SignalR 的 Microsoft.AspNetCore.SignalR.Server ActivitySource 为 hub 方法调用发出事件:

  • 每个方法都是其自己的活动,因此在中心方法调用期间发出活动的任何内容都位于中心方法活动下。
  • 中心方法活动没有父级。 这意味着它们不会捆绑在长时间运行的 SignalR 连接下。

以下示例使用 .NET Aspire仪表板OpenTelemetry 包:

<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />

将以下启动代码添加到 Program.cs 文件:

using OpenTelemetry.Trace;
using SignalRChat.Hubs;

// Set OTEL_EXPORTER_OTLP_ENDPOINT environment variable depending on where your OTEL endpoint is.
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddSignalR();

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        if (builder.Environment.IsDevelopment())
        {
            // View all traces only in development environment.
            tracing.SetSampler(new AlwaysOnSampler());
        }

        tracing.AddAspNetCoreInstrumentation();
        tracing.AddSource("Microsoft.AspNetCore.SignalR.Server");
    });

builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());

var app = builder.Build();

以下示例输出来自 Aspire 仪表板:

SignalR 中心方法调用事件的活动列表

ASP.NET Core 还会通过Microsoft.AspNetCore.Hosting事件源提供其自身的指标。

.NET SignalR 客户端 ActivitySource

SignalR ActivitySource名为Microsoft.AspNetCore.SignalR.Client的对象会为SignalR客户端发出事件:

  • 集线器调用会创建客户端跨度。 其他 SignalR 客户端(如 JavaScript 客户端)不支持跟踪。 在将来的版本中,此功能将添加到更多客户端中。
  • 客户端和服务器上的 Hub 调用支持上下文传播。 传播跟踪上下文可实现真正的分布式跟踪。 现在,我们可以看到调用从客户端流向服务器,然后再流回来。

以下是这些新活动在 .NET Aspire 仪表板中的显示方式:

Aspire 仪表板中的 Aspire 仪表板中的 SignalR 分布式跟踪

网络跟踪

警告

网络跟踪包含应用发送的每条消息的全部内容。 切勿将来自生产应用的原始网络跟踪发布到 GitHub 等公共论坛。

如果遇到问题,网络跟踪有时可以提供有价值的信息。 在向问题跟踪器提交问题时,这特别有用。

使用 Fiddler 收集网络跟踪(首选选项)

此方法适用于所有应用程序。

Fiddler 是一个功能强大的工具,用于收集 HTTP 跟踪。 从 telerik.com/fiddler 安装并启动该工具,然后运行你的应用程序,重现此问题。 Fiddler 适用于 Windows,并且提供适用于 macOS 和 Linux 的 beta 版本。

如果使用 HTTPS 进行连接,则需执行一些额外的步骤来确保 Fiddler 可以解密 HTTPS 流量。 有关详细信息,请参阅 Fiddler 文档

收集跟踪后,通过从菜单栏中选择 “文件>保存>所有会话 ”来导出它

从 Fiddler 导出所有会话

使用 tcpdump 收集网络跟踪(仅限 macOS 和 Linux)

此方法适用于所有应用程序。

可以通过从命令行界面运行以下命令,使用 tcpdump 收集原始 TCP 跟踪。 如果出现权限错误,你可能需要 root 权限或为命令添加前缀 sudo

tcpdump -i [interface] -w trace.pcap

[interface] 替换为要捕获的网络接口。 通常,这类似于 /dev/eth0 (对于标准以太网接口)或 /dev/lo0 (对于 localhost 流量)。 有关详细信息,请参阅主机系统上的 tcpdump 手动页。

在浏览器中收集网络跟踪

此方法仅适用于基于浏览器的应用。

大多数浏览器开发人员工具控制台都有一个“网络”选项卡,允许在浏览器和服务器之间捕获网络活动。 但是,这些跟踪不包括 WebSocket 和服务器发送的事件消息。 使用这些传输时,使用 Fiddler 或 TcpDump 等工具是一种更好的方法,如本文后面所述。

Microsoft Edge 和 Internet Explorer

(Microsoft Edge 和 Internet Explorer 的说明相同)

  1. 按 F12 打开开发工具
  2. 选择“网络”选项卡
  3. 如果需要,请刷新页面,并重现此问题
  4. 选择工具栏中的“保存”图标,将跟踪导出为“HAR”文件:

Microsoft Edge 开发人员工具“网络”选项卡上的“保存”图标

Google Chrome

  1. 按 F12 打开开发工具
  2. 选择“网络”选项卡
  3. 如果需要,请刷新页面,并重现此问题
  4. 右键单击请求列表中的任意位置,然后选择“另存为带内容的 HAR”:

Google Chrome 开发人员工具“网络”选项卡上的“另存为包含内容的 HAR”选项

Mozilla Firefox

  1. 按 F12 打开开发工具
  2. 选择“网络”选项卡
  3. 如果需要,请刷新页面,并重现此问题
  4. 右键单击请求列表中的任意位置,然后选择“全部保存为 HAR”

Mozilla Firefox 开发人员工具“网络”选项卡上的“全部另存为 HAR”选项

将诊断文件附加到 GitHub 问题

可以通过重命名诊断文件并将其附加到 GitHub 问题,使其具有 .txt 扩展,然后将其拖放到问题中。

注意

请不要将日志文件或网络跟踪的内容粘贴到 GitHub 问题。 这些日志和追踪可能很大,GitHub 常常会截断它们。

将日志文件拖放到 GitHub 问题上

指标

指标是时间间隔内数据度量值的表示形式。 例如每秒的请求数。 使用指标数据可以在高级别观察应用的状态。 .NET gRPC 指标是使用 EventCounter 发出的。

SignalR 服务器指标

SignalR 服务器指标在 Microsoft.AspNetCore.Http.Connections 事件源上报告。

名称 说明
connections-started 已启动的连接总数
connections-stopped 已停止的连接总数
connections-timed-out 已超时的连接总数
current-connections 当前连接数
connections-duration 平均连接持续时间

观察指标

dotnet-counters 是一个性能监视工具,用于临时运行状况监视和初级性能调查。 使用 Microsoft.AspNetCore.Http.Connections 作为提供程序名称监视 .NET 应用。 例如:

> dotnet-counters monitor --process-id 37016 Microsoft.AspNetCore.Http.Connections

Press p to pause, r to resume, q to quit.
    Status: Running
[Microsoft.AspNetCore.Http.Connections]
    Average Connection Duration (ms)       16,040.56
    Current Connections                         1
    Total Connections Started                   8
    Total Connections Stopped                   7
    Total Connections Timed Out                 0

其他资源