前言
Asp.Net Core中有一个不受人重视的属性HttpContext.TraceIdentifier
,它在链路追踪中非常有用,下面是官方的定义:
在项目中一般会将该字段输出到每一条日志中,也可以将此Id作为通用响应字段返回前端,后续可以根据该属性和日志匹配,快速定位整个链路日志。在本地开发时我通常观察到该值的格式大概如下长这个样子0HLEACIU86PT6:0000000D
,在生产环境中查看日志时,却不是这种格式,而是Guid
格式,虽然都是唯一标识,都能满足我的需要,但是为什么会产生这一差异令我困惑,最初以为是第三方日志组件对该字段进行了赋值,在我的不懈努力下,最终确定该差异的原因是部署方式差异导致,分享给各位。
差异对比
创建一个Asp.Net Core新项目,在示例代码中添加一行日志,打印该属性
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
_logger.LogInformation(Request.HttpContext.TraceIdentifier);
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
Kestre部署时的表现
在VS
中使用控制台启动项目,调用几次接口,输出如下
可见TraceIdentifier
有下面两部分组成{ConnectionId}:{Request number}
,第一部分ConnectionId
标识同一次连接,第二部分Request number
标识,当前是该连接的第n次请求起到计数的作用。
而两者的组成{ConnectionId}:{Request number}
就可以标识唯一一次请求。
IIS部署时的表现
用上面的的程序,不做任何更改,在VS
中IIS Express
启动,调用几次接口,输出如下,由于没使用文件日志,IIS Express启动时没有窗口可以看输出,只能通过VS
的调试窗口查看输出如下:
这里可以很明显的看出,我们打印的TraceIdentifier
是GUID
格式。
差异的原因
本地开发时我一般不会选择IIS Express
启动,因为它速度慢,也不能方便的查看日志输出。所以正如上文测试的那样,我在本地使用只看到一种格式:{ConnectionId}:{Request number}
。
我们生产环境是部署在Windows Server
中,而在windows下部署,使用IIS托管则比使用控制台更加安全稳定,所以这中部署方式输出的TraceIdentifier
和上文中使用IIS Express
表现是一致的。
那为何不同的部署方式产生该差异呢?
其实这是Asp.Net Core设计使然。在IIS
上,它(TraceIdentifier
)来自HTTP.sys
(内核驱动程序)并暴露给应用程序,以便您可以跟踪该ID,从内核到应用程序并返回到内核,它的值来自IIS生成并传递给我们的应用。使用Kestrel
,请求不需要iis对其进行转发,我们的应用程序就是链路的第一个程序,没有程序会传给它一个链路ID,所以程序自己需要生成一个,也就是我们看到的这种{ConnectionId}:{Request number}
。
总结
在不同的部署方式下,Asp.Net Core对TraceIdentifier
有不同的表现,是技术上的一种严谨。在IIS部署时,该属性值来自IIS内核的传递,以便我们可以跟踪IIS内核到我们的程序,也保持了IIS式的风格。