65.9K
CodeProject 正在变化。 阅读更多。
Home

Azure Functions 中的遥测相关性

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2019 年 4 月 24 日

CPOL

2分钟阅读

viewsIcon

3633

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 中的遥测关联

 

 

© . All rights reserved.