我正在维护几个用 ASP.NET 4.8(完整框架)和 ASP.NET Core 编写的应用程序。
.NET Core 实现了https://www.w3.org/TR/trace-context/,因此我可以看到我的日志(我使用 Serilog 进行结构化日志记录),其中包含 ParentId、SpanId 以及最重要的 TraceId。
有没有办法在旧的 ASP.NET 4.8 应用程序中读取和传播 TraceId?
我的应用程序之间正在执行大量请求,这将极大地改善调试体验。不幸的是,大多数请求源自旧的 ASP.NET 4.8 应用程序,并转到较新的 .NET Core 应用程序。
理想情况下,我希望达到与 ASP.NET Core 应用程序相同的状态 - 如果 Request-Id 来自 HTTP 标头,则使用它并将其填充到 ParentId 和 TraceId 中,并基于此生成 SpanId。此外,它还会进一步传播到源自 .NET Core 应用程序的其他 HTTP 请求。
谢谢!
好吧,我自己设法解决了这个问题。
首先,需要添加System.Diagnostics.DiagnosticSourcenugget包。
然后,创建ActivityManager类:
public static class ActivityManager
{
public static void StartActivity()
{
if (Activity.Current == null)
{
var activity = new Activity("Default Activity");
string parentIdFromHeaders = HttpContext.Current?.Request.Headers[GetRequestIdHeaderName()];
if (!string.IsNullOrEmpty(parentIdFromHeaders))
{
activity.SetParentId(parentIdFromHeaders);
}
activity.Start();
Activity.Current = activity;
// Sometimes I had issues with Activity.Current being empty even though I set it
// So just to be sure, I add it also to HttpContext Items.
HttpContext.Current?.Items.Add("Activity", activity);
}
}
public static void StopActivity()
{
GetActivity()?.Stop();
}
public static Activity GetActivity()
{
Activity activity = Activity.Current ?? (Activity)HttpContext.Current.Items["Activity"];
return activity;
}
public static string GetRequestIdHeaderName()
{
return "Request-Id";
}
public static string GetRequestId()
{
Activity activity = GetActivity();
if (activity != null)
{
string activityId = activity.Id;
return activityId;
}
// For the rare cases when something happens and activity is not set
// Try to read Request-Id first, if none, then create new GUID
return HttpContext.Current?.Request.Headers.Get(GetRequestIdHeaderName())
?? Guid.NewGuid().ToString().Replace("-", "");
}
}
Run Code Online (Sandbox Code Playgroud)
并配置Global.asax.cs处理程序
protected void Application_Start()
{
Activity.DefaultIdFormat = ActivityIdFormat.Hierarchical;
}
protected void Application_BeginRequest()
{
ActivityManager.StartActivity();
}
protected void Application_EndRequest()
{
ActivityManager.StopActivity();
}
Run Code Online (Sandbox Code Playgroud)
现在,所有传入请求都会创建新的Activity并正确设置其ParentId.
为了添加SpanId,TraceId和ParentId到日志记录上下文,我创建了自定义日志丰富器:
public class TraceLogEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
Activity activity = ActivityManager.GetActivity();
if (activity != null)
{
string parentId = activity.ParentId;
string rootId = activity.RootId;
string activityId = activity.Id;
logEvent.AddPropertyIfAbsent(new LogEventProperty("SpanId", new ScalarValue(activityId)));
logEvent.AddPropertyIfAbsent(new LogEventProperty("ParentId", new ScalarValue(parentId)));
logEvent.AddPropertyIfAbsent(new LogEventProperty("TraceId", new ScalarValue(rootId))); }
}
}
Run Code Online (Sandbox Code Playgroud)
并将其添加到 Serilog 配置中.Enrich.With<TraceLogEnricher>()。
最后一步是配置传出请求。正在HttpClient使用DefaultRequestHeaders。(为了更方便,也可以在IHttpClientFactory配置中配置)。
var requestId = ActivityManager.GetRequestId();
client.DefaultRequestHeaders.Add("Request-Id", requestId);
Run Code Online (Sandbox Code Playgroud)
如果使用 Web 服务,最好Request-Id也向 Web 服务请求添加标头。为此,请覆盖GetWebRequestWeb 服务客户端的方法。(这些通常生成为分部类,因此请在您自己的分部类文件中覆盖它)。
protected override WebRequest GetWebRequest(Uri uri)
{
var request = base.GetWebRequest(uri);
request.Headers.Add("Request-Id", ActivityManager.GetRequestId());
return request;
}
Run Code Online (Sandbox Code Playgroud)