.NET中将请求加入用户背景以实现跟踪

在ASP.NET Core里,咱们可以给请求追踪加点“用户背景user context”信息,这样就能更轻松地找到问题、搞懂用户在干嘛,还能让网站变得更好。

我来一步步教你们,咋在ASP.NET Core网站里给请求追踪加个用户背景。

咋回事?为啥要加用户背景到请求追踪里?

要是网站出了啥问题,你得知道是咋回事,对吧?如果日志里能直接看到用户的ID,那就好办多了!不用在一堆日志里翻来翻去找,你直接按用户ID一搜,就能看到相关的记录。

加了用户背景还有这些好处:

  • 追踪用户在网站里都干了啥
  • 发现用户行为有啥规律
  • 帮某个具体用户解决问题
  • 看看不同用户群的网站性能咋样

咋弄?实现用户背景增强
咱们的核心是个“中间件”,它能从当前用户的“声明”(就是身份信息)里掏出用户ID,然后把这个ID加到当前的“活动”和“日志范围”里。

在ASP.NET Core里,“日志范围”是个啥呢?就是你可以在一个范围内给所有日志加点额外信息。像Serilog或者自带的日志工具都支持这功能。比如,你在日志范围里加了个用户ID,那这个范围里的每条日志都会自动带上这个ID,哪怕日志内容里没提用户。这就能轻松把某个用户在网站不同部分的日志串起来。

还有个“Activity”类,是.NET诊断系统里的东西。它表示一个工作单元或者操作,专门用来跨服务做分布式追踪。你给“Activity.Current”加个标签,这信息就会变成追踪的一部分,方便你在监控系统里过滤和分析请求。

直接上代码吧:
using System.Diagnostics;
using System.Security.Claims;

namespace MyApp.Middleware;

public sealed class UserContextEnrichmentMiddleware(
    RequestDelegate next,
    ILogger logger)
{
    public async Task InvokeAsync(HttpContext context)
    {
        string? userId = context.User?.FindFirstValue(ClaimTypes.NameIdentifier);
        if (userId is not null)
        {
            Activity.Current?.SetTag("user.id", userId);

            var data = new Dictionary
            {
                ["UserId"] = userId
            };

            using (logger.BeginScope(data))
            {
                await next(context);
            }
        }
        else
        {
            await next(context);
        }
    }
}


这段代码都干了啥?咱们拆开来看:

  1. 从登录用户的“声明”里掏出用户ID,用context.User?.FindFirstValue(ClaimTypes.NameIdentifier)
  2. 如果找到用户ID了,就给当前“活动”加个标签:Activity.Current?.SetTag("user.id", userId)
  3. 用ILogger.BeginScope创建个日志范围,把用户ID加进去,这样这个范围里的日志都会带上用户ID
  4. 调用下一个中间件,继续处理请求
  5. 如果没找到用户ID(比如匿名访问),就直接跳到下一个中间件

日志范围和OpenTelemetry咋整?
如果你用OpenTelemetry来导出日志,得设置一下,让它把日志范围也带上。
咋弄?看这儿:

builder.Logging.AddOpenTelemetry(options => { options.IncludeScopes = true; options.IncludeFormattedMessage = true; });


把中间件加到网站里
想用这个中间件,得在Program.cs文件里把中间件加到处理流程里:

app.UseAuthentication();
app.UseAuthorization();

// 在认证之后加用户背景增强中间件
app.UseMiddleware<UserContextEnrichmentMiddleware>();

app.MapControllers();

app.Run();


小心个人信息(PII)!
在日志和追踪里加用户ID的时候,得小心“个人信息”(PII)。有几点要注意:

  • 用户ID最好用不透明的标识(比如GUID),别直接露出啥个人信息
  • 别把邮箱、名字啥的直接记到日志里
  • 确保日志设置不会把个人信息发到不该去的地儿
  • 如果有数据保护法规要遵守,记得搞个日志保留策略,必要时能删掉用户的日志数据

还能加啥背景信息?
光加用户ID还不够,咱们还能加点别的,让日志和追踪更有用。

功能开关(Feature Flags)
功能开关能让咱们慢慢放出新功能,或者给特定用户开权限。把功能开关信息加到背景里,能帮咱们看清楚:

// 在中间件里
if (featureFlagService.IsEnabled(
"NewFeature", userId))
{
    Activity.Current?.SetTag(
"features.newfeature", "enabled");
   
// 也加到日志范围里
}


多租户应用的租户信息
如果你的网站服务多个租户(比如多个公司用同一个系统),加个租户ID可太有用了:

string? tenantId = context.User?.FindFirstValue("TenantId");
if (tenantId is not null)
{
    Activity.Current?.SetTag(
"tenant.id", tenantId);
   
// 加到日志范围
}


总结一下
在ASP.NET Core里给请求追踪加用户背景,是个简单但超有用的招儿。用上咱们讲的中间件,你会发现这些好处:

  • 更快解决问题:用户报问题时,你能很快找到相关日志
  • 更懂使用模式:看看哪些功能被用得多,谁在用
  • 性能监控更强:发现某些用户群的慢请求
  • A/B测试更顺:追踪不同功能开关用户的指标
搞懂日志范围和Activity类,能让你把这招儿发挥到极致。日志范围保证你的日志信息始终带上下文,Activity类则能跨服务做分布式追踪。别忘了小心处理个人信息,确保你的日志做法符合相关法规哦!