Get end-to-end traceability with Application Insights and Angular

Yannick Haas
medialesson
Published in
4 min readFeb 6, 2023

Application Insights allows us to monitor what and how our application is doing. Apart from collecting metrics and telemetry it even allows us to store trace logging data, allowing for any application to have a true end-to-end traceability.

You can read more about it here: Microsoft Learn — Application Insights

What is end-to-end traceability?

End-to-end traceability allows us to see how a page view has performed. In a monolithic application this is usually done by logging the call stack. But since microservices and modern cloud architectures have taken over, this has become more or less impossible, since the typical frontend is executed on the client side. This is where distributed tracing can help us and I’ll show you how to implement the frontend part.

So how do I implement it?

Package installation

We need to install the following packages (for Angular 14):

"@microsoft/applicationinsights-angularplugin-js": "^3.0.0",
"@microsoft/applicationinsights-web": "^2.8.9",

The Microsoft Docs don’t mention @microsoft/applicationinsights-web but it is required for the types and to initialize the service (at least for me it didn’t install when I added @microsoft/applicationinsights-angularplugin-js ).

Setup

There are two different ways we could use Application Insights. One is to have everything in app.component.ts , as described in the Microsoft Docs. Or we could create a dedicated service (like in the example), which we could also inject in other components to allow for further custom logging/tracing. Since the latter should be more suitable for complex projects, we’re going to take a look on how to implement it.

Adding the service

First of all we’re going to create a service by executing ng generate service ApplicationInsights (or the shorthand ng g s ApplicationInsights) in the command line. Remember: Service will be automatically added to the name resulting in the name ApplicationInsightsService (you can of course name your service however you want). Then we’ll have to add the following code inside our class:

// Ensure only one instance exists in the application
@Injectable({
providedIn: 'root',
})
export class ApplicationInsightsService {
private readonly angularPlugin = new AngularPlugin();
private readonly appInsights = new ApplicationInsights({
config: {
connectionString: 'YOUR_CONNECTION_STRING',
extensions: [this.angularPlugin],
// Required for end-to-end traces
enableCorsCorrelation: true,
enableRequestHeaderTracking: true,
enableResponseHeaderTracking: true,
// Domains where we don't want to send tracing headers, since it could lead to errors
correlationHeaderExcludedDomains: ['*.queue.core.windows.net'],
},
});

constructor() {
this.appInsights.loadAppInsights();
this.appInsights.addTelemetryInitializer((envelope: ITelemetryItem) => {
envelope.tags = envelope.tags || [];
});
}

public init(): Observable<void> {
return of(undefined);
}
}

Next we’ll have to ensure the service is at least used once. That’s why we added the init method, which just returns an empty Observable. That way we can add it in our APP_INITIALIZER . To do this we need to add the following code to our app.module.ts :

// ...
providers: [
{
provide: APP_INITIALIZER,
deps: [ApplicationInsightsService],
useFactory: (appInsights: ApplicationInsightsService): Observable<void> => (appInsights.init()),
multi: true,
},
ApplicationInsightsService,
// ...
]

That’s the basic setup. Our application should now properly track requests and send them to Azure Application Insights. But there’s even more we can do.

Catching application errors and sending them to Application Insights

To catch errors occurring in your application during runtime, we’ll have to add the following provider to your app.module.ts :

providers: [
// ...
{
provide: ErrorHandler,
useClass: ApplicationinsightsAngularpluginErrorService,
},
// ...
]

Logging how long a user spends on a page

To achieve this, we have to modify our service a bit. First we need to change the constructor:

// ApplicationInsightsService
constructor(private readonly router: Router) {
this.appInsights.loadAppInsights();
this.appInsights.addTelemetryInitializer((envelope: ITelemetryItem) => {
envelope.tags = envelope.tags || [];
});

// Subscribe to route changes to log how long a user spends on a page
this.router.events
.pipe(filter((event: unknown): event is ResolveEnd => event instanceof ResolveEnd))
.subscribe((event: ResolveEnd) => {
const activatedComponent = this.getActivatedComponent(event.state.root);
if (activatedComponent)
this.appInsights.trackPageView({
name: activatedComponent.name,
uri: event.urlAfterRedirects,
});
});
}

Then we’ll have to add the following method:

private getActivatedComponent(snapshot: ActivatedRouteSnapshot): IPageViewTelemetry | null {
if (snapshot.firstChild) {
return this.getActivatedComponent(snapshot.firstChild);
}
return snapshot.component;
}

That way we can track how long a user spends on a page and which component he accessed. We actually get the component name and not just the URL, which really helps with debugging.

And that’s all we have to do to get end-to-end traceability in your application!

How can this help us?

Combined with backend logs we can now trace a request from the component all the way down to the database. Furthermore we now have all logs in a single place and don’t need to ask a client to show us the error he’s getting or correlate frontend and backend logs ourselves. In the long run this can save time and money especially when dealing with critical bugs, which need to be fixed as soon as possible.

Troubleshooting

I can’t see traces from the backend in my view

Sometimes it can take some time for the traces to show up. Usually somewhere between 20 seconds and 3 minutes.

More information: Microsoft Learn

Even after waiting I still can’t see them

Try enabling the legacy mode by adding:

// ...
new ApplicationInsights({
config: {
// ...
// Ensure compatibility with older AI packages
distributedTracingMode: eDistributedTracingModes.AI_AND_W3C,
}
});

More information here: Microsoft Learn

Enabling it didn’t help

Enable logging to console by setting loggingLevelConsole to 2, which should log internal errors to your console.

More information here: Microsoft Learn

--

--