3

I'm trying to have an azure function (.net core 6 isolated) where I want to have connected to application insights and user W3C traceparent as my correlation method.

I have installed System.Diagnostics.DiagnosticSource (7.0.1), Microsoft.Azure.Functions.Worker.ApplicationInsights (1.0.0-preview) and added the needed code to my program.cs

.ConfigureFunctionsWorkerDefaults(builder =>
{
    builder.AddApplicationInsights()
        .AddApplicationInsightsLogger();
})

While I see that Activity.Id does hold a traceparent format string, if I send a traceparent header, the activity does not grab it and, as such, I don't see it in application insights. Also, the response does not send a traceparent header back...

I was then forced to create a middleware to attempt this and I'm seriously wondering if this is the proper way to achieve the objective, having in mind this will only work for http triggers (would prefer something that works with all)

public class TraceparentMiddleware : IFunctionsWorkerMiddleware
{
    private const string TraceParentPattern = "^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$";

    public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
    {
        try
        {
            if (await TrySetActivityId(context))
                await next(context);
            else
            {
                var req = await context.GetHttpRequestDataAsync();
                var res = req!.CreateResponse(HttpStatusCode.BadRequest);
                await res.WriteStringAsync("Invalid traceparent format.");
                context.GetInvocationResult().Value = res;
            }

            SetTraceparentHeader(context);
        }
        catch
        {
            SetTraceparentHeader(context);
            throw; //because I don't know where the app insights code to log the exceptions happens.
        }
    }

    private static async Task<bool> TrySetActivityId(FunctionContext context)
    {
        var request = await context.GetHttpRequestDataAsync();

        if (request?.Headers?.TryGetValues("traceparent", out var values) ?? false && !string.IsNullOrWhiteSpace(values?.FirstOrDefault()))
        {
            var regex = new Regex(TraceParentPattern, RegexOptions.IgnoreCase);
            if (regex.IsMatch(values.FirstOrDefault()))
                Activity.Current.GetType()
                    .GetField("_id", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
                    .SetValue(Activity.Current, values.FirstOrDefault());
            else
                return false;
        }
        return true;
    }

    private void SetTraceparentHeader(FunctionContext context)
    {
        var response = context.GetHttpResponseData();
        if (!response?.Headers?.Contains("traceparent") ?? false)
            response.Headers.Add("traceparent", Activity.Current.Id);
    }
}

As you can see... I'm even forced to use reflection in order to set the Activity.Current.Id, which I would like to avoid.

FEST
  • 813
  • 2
  • 14
  • 37

1 Answers1

0

You also have access to TraceParent over HttpRequestData.FunctionContext.TraceContext.

See if that works out for you.

var traceParent = request.FunctionContext.TraceContext.TraceParent

Example of traceparent in watch.

enter image description here