question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Create telemetry properties inside a IServiceScope

See original GitHub issue

I have a WebJob running an infinite loop. For each iteration of this loop, I want to log some informations (traces, exceptions and one request) to AppInsights. And I need that each of those informations, for a same iteration loop, share the same correlationId (to be able to group all logs in the azure portal).

Here the code I use to declare AppInsights :

this.hostBuilder = new HostBuilder()
    .ConfigureLogging((context, builder) =>
    {
        builder.AddConfiguration(context.Configuration.GetSection("Logging"))
            AddApplicationInsights(options => options.InstrumentationKey = context.Configuration["ApplicationInsightsInstrumentationKey"]);
    });

Then I have a hosted service which run multiples tasks, and an infinite loop in each task :

internal class MyHostedService : IHostedService
{
    public Task StartAsync(CancellationToken cancellationToken)
    {
        this.taskCancellationToken = new CancellationTokenSource();

        foreach (var xxx in yyy)
        {
            this.tasks.Add(Task.Run(async () =>
            {
                while (!this.taskCancellationToken.IsCancellationRequested)
                {
                    using (var scope = this.serviceScopeFactory.CreateScope())
                    {
                        [...]
                    }
                }
            }
        }
    }
}

So each iteration has its own scope.

Then, to be able to log a request, I create an operation based on the TelemetryClient:

using(var operationHolder = this.telemetryClient.StartOperation<RequestTelemetry>("myoperation"))
{
    try
    {
        [...]

        operationHolder.Telemetry.Success = true;
    }
    catch(Exception e)
    {
        operationHolder.Telemetry.Success = false;
    }
}

And I also add some logs:

var logger = scope.ServiceProvider.GetRequiredService<ILogger<MyHostedService>>();
logger.LogInformation("Process ended successfully");

I want that both my logger and operationHolder share the same properties (mainly the correlationId which is a Guid).

My operationHolder properties are initialized in that way and it’s working correctly :

operationHolder.Telemetry.Properties["correlationId"] = correlationId.ToString();

The ApplicationInsightsLogger, which is used when I call logger.LogInformation, creates a TraceTelemetry which use the TelemetryClient (but not the same telemetry as the operationholder) to get properties to add to AppInsights. My problem here is that the TelemetryClient is declared as Singleton. So I can declare GlobalProperties but not Properties specific to each iteration of my loop.

My only workaround is to create LoggerScope and add my properties in the state :

var loggerScope = logger.BeginScope(new Dictionary<string, object>
{
    ["correlationId"] = correlationId
});

The only problem here is that my correlationId appears as prop___correlationId, I think to avoid collision between state and telemetry properties.

Expected behavior

Be able to declare scoped properties inside a TelemetryClient.

Actual behavior

TelemetryClient is a Singleton so I can’t declare properties for a scope.

Known workarounds

As exposed previously, use a BeginScope in addition of StartOperation.

Related information

Provide any related information

  • Microsoft.Azure.WebJobs.Logging.ApplicationInsights : 3.0.6
  • Microsoft.Azure.WebJobs.Extensions : 3.0.2

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:3
  • Comments:13 (1 by maintainers)

github_iconTop GitHub Comments

3reactions
brettsamcommented, May 23, 2019

Ultimately – you want that correllationId to be attached to all telemetry inside that operation, but without the prop__ prefix?

One thing you may be able to do is take advantage of Activity and use an ITelemetryInitializer to stick your correllation id onto your telemetry.

If I’m undnerstanding your scenario correctly, I wrote an IHostedService like this:

internal class MyHostedService : IHostedService
{
    private readonly TelemetryClient _client;
    private readonly IServiceScopeFactory _serviceScopeFactory;
    private readonly CancellationTokenSource _cts = new CancellationTokenSource();

    public MyHostedService(IServiceScopeFactory serviceScopeFactory, TelemetryClient client)
    {
        _serviceScopeFactory = serviceScopeFactory;
        _client = client;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        Task.Run(async () =>
        {
            while (!_cts.IsCancellationRequested)
            {
                using (var scope = _serviceScopeFactory.CreateScope())
                {
                    using (var operationHolder = _client.StartOperation<RequestTelemetry>("myoperation"))
                    {
                        Guid correllationId = Guid.NewGuid();
                        Activity.Current.AddTag(nameof(correllationId), correllationId.ToString());

                        var logger = scope.ServiceProvider.GetService<ILogger<MyHostedService>>();
                        logger.LogInformation("Something!");

                        operationHolder.Telemetry.Success = true;
                    }
                }

                await Task.Delay(2000, _cts.Token);
            }
        }, _cts.Token);

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _cts.Cancel();
        return Task.CompletedTask;
    }
}

Notice the Activity.Current line.

Then you can write an ITelemetryInitializer that looks at Activity.Current, pulls out the relevant details, and applies them directly to the telemetry however you want.

1reaction
dcarr42commented, May 16, 2019

Nice one…I will not harp on about it but I still think using RequestTelemetry inside the IHostedService is a little bit of a misuse. But I totally see your predicament. 😃

Read more comments on GitHub >

github_iconTop Results From Across the Web

Add Custom Properties to Application Insights Request ...
In the OnTraceResponse method, I added my custom property to the request telemetry by extracting value from OperationContext (which is ...
Read more >
Application Insights for ASP.NET Core applications
Get an instance of TelemetryClient by using constructor injection and call the required TrackXXX() method on it. We don't recommend creating new ...
Read more >
Enhancing Application Insights Request Telemetry
Once you've added custom properties to Request Telemetry, you can use those custom properties to filter data in the Application Insights portal.
Read more >
Customizing Application Insights using Telemetry Initializers
This week, we continue our mini series exploring Application Insights. Monster Dave shows us the importance of setting a Cloud Role Name and ......
Read more >
ASP.NET Core: Telemetry and Application Insights
Allocate your Application Insights resource in Azure, whichever way ... ability to attach custom properties to any telemetry in real-time.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found