I'm working on an Angular app and try to analyse the performance of this app. For this I am using Elasticsearch's Kibana and APM server. The APM server is already able to measure the time for the inital page load and routing changes, by mainly getting the times for various HTTP requests. So far, so good.
But now I want to finetune this measurement because not every HTTP request happens during a route change. The situation is that on a site are multiple components, lots of them including PrimeNG tables (<p-table>
) which are updated without a route change, e.g. when the user enters something into a search input.
So here is my approach to measure the lifetime of every component by creating a decorator @TimeTracking()
which extends the Angular lifecycle hooks ngOnInit()
and ngOnDestroy()
. To get some more information about the component's life, I also measured the performance between ngDoCheck()
and ngOnViewChecked()
.
export function TimeTracking(): ClassDecorator {
return function(target: any) {
const lifecycleHooks = ['ngOnInit', 'ngDoCheck', 'ngAfterViewChecked', 'ngOnDestroy'];
lifecycleHooks.forEach(hook => {
const original = target.prototype[hook];
target.prototype[hook] = function ( ...args: any ) {
if (hook === 'ngOnInit') {
// start performance measurement of component
}
else if (hook === 'ngDoCheck') {
// start measurement of checking cycle
}
else if (hook === 'ngAfterViewChecked') {
// end measurement of checking cycle
}
else if (hook === 'ngOnDestroy') {
// end performance measurement for component
}
original && original.apply(this, args);
};
});
};
}
The idea of this approach I got from How to test rendering speed in Angular and https://netbasal.com/inspiration-for-custom-decorators-in-angular-95aeb87f072c.
Additionally, I am also measuring the time for each HTTP request by using a HTTP interceptor:
@Injectable()
export class LoggingInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const t0 = performance.now();
let okString: string;
return next.handle(req)
.pipe(
tap(
(event: HttpEvent<any>) => okString = event instanceof HttpResponse ? 'succeeded' : '',
(error: HttpErrorResponse) => okString = 'failed'
),
finalize(() => {
console.log(`${req.method} "${req.urlWithParams}" ${okString} in ${performance.now() - t0} ms.`);
})
);
}
}
To cut a long story short, here are my questions:
What do you think about this approach? Do you have any further ideas or proposals for improvement?
Is there any possibility to get information about which event has triggered the ngDoCheck()
to get more information?
Is it possible to match the measured HTTP requests (in the interceptor) to the component which triggered it?