Azure Functions 中的遥测相关性





0/5 (0投票)
ASP.NET 会自动解析分布式跟踪头,但在 Azure Functions 中,则需要一些自定义代码。
在分布式系统中,分布式跟踪(日志/跟踪/遥测关联)根据系统的复杂程度,可能很好拥有,也可能是绝对必要的。 随着微服务的兴起,它越来越成为后者。
在使用 .NET 中的 HttpClient 时,关联信息会自动通过头传递到其他 Web 应用程序。 这些 Web 应用程序可以解析这些头,并将关联数据包含在日志条目中。 如果接收 Web 应用程序基于 ASP.NET(Core 或 Full),则您需要做的最多是安装一个 NuGet 包,头解析和初始化就会自动完成。 但是,在 Azure Functions 中,缺少此功能(请参阅 GitHub 问题 #1,#2)。
针对 Azure Functions 中缺失关联功能的解决方法通常指向 通过 TelemetryClient 手动记录。 然而,这相当繁琐,需要大量的样板代码(例如,仅针对异常跟踪,您基本上需要包装每个函数...)。 此外,使用 HttpClient 时,关联数据也不会传递到其他系统。
在我看来,更好的选择是用自定义代码实现关联。 幸运的是,ASP.NET 是开源的,因此有几个实现作为蓝图可用,例如在 AspNet.TelemetryCorrelation 中,有一个头解析的实现。
解析数据后,仍然需要使其可用,以便 HttpClient、Application Insights 日志记录器或其他日志记录/跟踪代码可以获取它。 这可以通过创建一个新的 `Activity` 实例、将当前请求 ID 设置为活动体的 parentId,然后在该实例上调用 `Start()` 方法来完成。 这将在内部设置静态(AsyncLocal)Activity.Current 属性,该属性由关联数据使用者访问。
一个非常基本的实现可能如下所示
public static class CorrelationExtensions
{
public static CorrelationData InitializeLogCorrelationFromHeaders(
this ExecutionContext context, HttpRequestMessage request)
{
CorrelationData correlationData = ExtractCorrelationDataFromHeaders(request.Headers);
if (correlationData != null)
{
context.InitializeLogCorrelation(correlationData);
}
return correlationData;
}
public static void InitializeLogCorrelation(this ExecutionContext context, CorrelationData correlationData)
{
Activity activity = new Activity(context.FunctionName);
activity.SetParentId(correlationData.ExternalOperationParentId);
activity.Start();
}
private static CorrelationData ExtractCorrelationDataFromHeaders(HttpRequestHeaders requestHeaders)
{
const string RequestIdHeaderName = "Request-Id";
if (!requestHeaders.TryGetValues(RequestIdHeaderName, out IEnumerable<string> requestIDs)
|| string.IsNullOrEmpty(requestIDs?.FirstOrDefault()))
{
return null;
}
return new CorrelationData { ExternalOperationParentId = requestIDs.First() };
}
}
public class CorrelationData
{
public string ExternalOperationParentId { get; set; }
}
这显然只适用于使用 http 触发器的 azure functions。 当使用不同的触发器时,需要以不同的方式传递关联数据。 特别是对于 Durable Functions,我认为关联是绝对必要的。 您可以使用一个小的包装类来传递函数之间的信息,该类包括关联数据以及实际的有效负载,例如:
[FunctionName("httpFunction")]
public static async Task<HttpResponseMessage> HttpStartImport(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestMessage req,
[OrchestrationClient]DurableOrchestrationClient orchestrationClient,
ExecutionContext context)
{
CorrelationData correlationData = context.InitializeLogCorrelationFromHeaders(req);
string content = await req.Content.ReadAsStringAsync();
Payload payload = JsonConvert.DeserializeObject<Payload>(content);
var dataContainer = new WorkflowDataContainer<ImportData>(correlationData, payload);
string instanceId = await orchestrationClient.StartNewAsync("orchestrateFunction", dataContainer);
return orchestrationClient.CreateCheckStatusResponse(req, instanceId);
}
[FunctionName(orchestrateFunction)]
public static async Task Orchestrator(
[OrchestrationTrigger] DurableOrchestrationContext orchestrationContext,
ExecutionContext context)
{
var dataContainer = orchestrationContext.GetInput<WorkflowDataContainer<ImportData>>();
context.InitializeLogCorrelation(dataContainer.CorrelationData);
...
}
public class WorkflowDataContainer<T>
{
public CorrelationData CorrelationData { get; set; }
public T Content { get; set; }
public WorkflowDataContainer() { }
public WorkflowDataContainer(CorrelationData correlationData, T content)
{
this.CorrelationData = correlationData;
this.Content = content;
}
}
请注意,您需要对每个函数执行此操作!
链接
Application Insights 中的遥测关联