Using Application Insights with a .NET Core WebApp running in a Container

Using Application Insights with a .NET Core WebApp running in a Container

Adding Azure Application Insight monitoring to .NET applications has always been fairly easy and straight forward. There are a few extra considerations for running it in a container, but nothing crazy...

However, because so little time is spent with that configuration code (it's a 'set and forget' operation), there have been many times when I'm left scratching my head forgetting the basics for each new project!

So here it is... until it's out of date again!!

Get your Instrumentation Key

First off, make sure you have an Application Insights instance setup in Azure. And you'll need a copy of your Instrumentation Key.

image.png

Configure your Web Application

Next, in your WebApp's .csproj file add the following NuGet package references:

<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.16.0" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.1.3" />
<PackageReference Include="Microsoft.AspNetCore.ApplicationInsights.HostingStartup" Version="2.2.0" />

Then add the instrumentation key to your appSettings.json file:

"ApplicationInsights": {
    "InstrumentationKey": "f75c8503-<use your own key>-863348"
  }

Now in our Startup.cs file we want to:

  1. Add the Application Insights Telemetry service
  2. Add the Kubernetes Telemetry enricher
  3. Add a Singleton to resolve our dependency injection when we need it
public void ConfigureServices(IServiceCollection services)
{
    services.AddApplicationInsightsTelemetry();
    services.AddApplicationInsightsKubernetesEnricher();
    services.AddSingleton<TelemetryClient>();
}

Hurrah!!! At this point, if you build and run your application, telemetry will start streaming to your Application Insights instance in Azure, showing CPU, Memory, Requests and uncaught Exceptions. Excellent! :)

Bonus... Report caught Exceptions at your discretion

But, if like me you already have a Catch All error handler in your API - you may still want to report these Exceptions to Application Insights without having to remove you're existing handler.

Your example may differ from mine below, but the approach is generally the same. Get a hold of a TelemetryClient with dependency injection, and call the .TrackException() method.

[Route("api/[controller]")]
public class ErrorController : WebApiErrorController
{
    private readonly TelemetryClient telemetryClient;

    // This is where we resolve our dependency to get the Singleton scoped TelemetryClient
    public ErrorController(TelemetryClient telemetryClient)
    {
        this.telemetryClient = telemetryClient;
    }
    [AllowAnonymous]
    [HttpGet]
    [HttpPut]
    [HttpPost]
    [HttpDelete]
    public async Task<IActionResult> ErrorHandler()
    {
        var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
        telemetryClient.TrackException(exceptionHandlerPathFeature?.Error);
        #... the rest of your custom handler logic goes here...
    }
}

There are other tracking methods available if you really want to get smart with Application Insights. You don't need to limit yourself to just Exception monitoring. See the Azure Monitor API for more information on the Telemetry Client capabilities.

Getting Telemetry working from a Container

When it comes to the Container runtime, you need to add some Environment Variables to get the telemetry working correctly.

The Instrumentation Key environment variable is optional here, as it is already set, but you are best to not rely on the the value set in the appSettings.json file. In general it's not good practice to rely on configuration settings stored in your code files unless they are forever constants.

To setup the container environment variables...

For docker run : docker run -e "ApplicationInsights:InstrumentationKey"="[instrumentation key]" -e ASPNETCORE_HOSTINGSTARTUPASSEMBLIES="Microsoft.AspNetCore.ApplicationInsights.HostingStartup" <your docker image>

In a docker-compose file:

version: '3'
services:
  myWebApp:
    image: myWebAppImage:latest
    ports:
      - "80:5000"
    container_name: myWebAppContainer
    environment:
      ASPNETCORE_HOSTINGSTARTUPASSEMBLIES: "Microsoft.AspNetCore.ApplicationInsights.HostingStartup"
      "ApplicationInsights:InstrumentationKey": "[instrumentation key]"

In a Kubernetes Deployment file (note swapping the : for __ - which is portable when using Linux base container images):

spec:
      containers:
      - name: myWebAppContainer
        image: myWebAppImage:latest
        ports:
        - containerPort: 80
          name: http-tcp-80
        env:
        - name: ASPNETCORE_HOSTINGSTARTUPASSEMBLIES
          value: "Microsoft.AspNetCore.ApplicationInsights.HostingStartup"
        - name: "ApplicationInsights__ConnectionString"
          value: "[instrumentation key]"

Happy hacking!!!