How do I set the applicaiton insights operationID to my custom correlationID?

889 views Asked by At

I have a .net6 worker service and I need the Request Telemetry OperationID set to a custom value. This value is my CorrelationID that is read from a message queue, and it's format is a guid with dashes.

TelemetryClient.StartOperation has an overload that takes an operationId, but it only works with a specific format. It will not work with my guid.

I have tried the code below, which appears to work in the debugger. However, the value that shows up in applicaiton insights is not what I set it to.

var client = new TelemetryClient();
client.Context.Operation.Id = internalId;

I have tried creating an ITelemetryInitializer. If I set the operationID in the initialize method will work. The problem here is getting the correlationID to the initializer in the correct dependency injection scope.

It seems as though this is a common scenario. I have seen where others ask the question but I have not found a suitable solution.

Has anyone solved this problem?

1

There are 1 answers

1
Peter Bons On BEST ANSWER

The problem is that since .Net 5 the default Id format is set to W3C standard instead of the Hierarchical Id format, see the docs:

Parent-Child relationships between Activities in the distributed trace tree are established using unique IDs. .NET's implementation of distributed tracing supports two ID schemes: the W3C standard TraceContext, which is the default in .NET 5+, and an older .NET convention called 'Hierarchical' that's available for backwards compatibility. Activity.DefaultIdFormat controls which ID scheme is used. In the W3C TraceContext standard, every trace is assigned a globally unique 16-byte trace-id (Activity.TraceId), and every Activity within the trace is assigned a unique 8-byte span-id (Activity.SpanId). Each Activity records the trace-id, its own span-id, and the span-id of its parent (Activity.ParentSpanId). Because distributed traces can track work across process boundaries, parent and child Activities may not be in the same process. The combination of a trace-id and parent span-id can uniquely identify the parent Activity globally, regardless of what process it resides in.

Activity.DefaultIdFormat controls which ID format is used for starting new traces, but by default adding a new Activity to an existing trace uses whatever format the parent Activity is using. Setting Activity.ForceDefaultIdFormat to true overrides this behavior and creates all new Activities with the DefaultIdFormat, even when the parent uses a different ID format.

When you set the Activity.DefaultIdFormat to ActivityIdFormat.Hierarchical you can specify any string as an operation Id as it does not have to conform to the W3C standard.

So the following code works like a charm:

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    Activity.DefaultIdFormat = ActivityIdFormat.Hierarchical;

    int index = 0;
    while (!stoppingToken.IsCancellationRequested)
    {
        ++index;
        using var operation = _telemetryClient.StartOperation<RequestTelemetry>($"op{index}", $"a-b-c-{index}");
                
        _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
        await Task.Delay(1000, stoppingToken);
    }
}

but it might break the distributed trace flow for your api controllers if you want to trace the end-to-end flow between multiple seperate applications.

Another way is to just include your own correlation Id as a custom propery:

operation.Telemetry.Properties["MessageCorrelationId"] = "xxx";