Skip to content

Instantly share code, notes, and snippets.

@isaaclyman
Last active February 7, 2019 17:51
Show Gist options
  • Save isaaclyman/d893f7d2c318882b21d529163a47030d to your computer and use it in GitHub Desktop.
Save isaaclyman/d893f7d2c318882b21d529163a47030d to your computer and use it in GitHub Desktop.
A TypeScript annotation for Angular that tracks the execution time of any class method and logs slow methods to the console
// Created by Anders Lyman, uploaded with permission
// Usage: class MyMethods { @LogPerformance() slowMethod() { ... } }
// If slowMethod exceeds 50 invocations or 30ms total run time, a statement like the following will be logged:
// 35ms | total: 35ms | calls: 1 | MyMethods:slowMethod
import {isDevMode} from '@angular/core';
export function LogPerformance(whenExceedsTimeInMs: number = 30, whenExceedsCalls: number = 50): any {
return function<T>(target: T, key: keyof T, descriptor?): any {
descriptor =
descriptor || Object.getOwnPropertyDescriptor(target, key) || Object.getOwnPropertyDescriptor(Object.getPrototypeOf(target), key);
const originalMethod = descriptor.value;
if (!isDevMode) {
return originalMethod;
}
const log = (message: string) => {
console.log(message, 'color: red;');
};
const debouncedLog = debounce(log, 1000);
const parentName = ((target || {}).constructor || <any>{}).name || 'UNKNOWN';
const caller = `${parentName}:${key}`;
let calls = 0;
let totalElapsed = 0;
descriptor.value = function() {
calls++;
const t0 = performance.now();
const result = originalMethod.apply(this, arguments);
const t1 = performance.now();
const elapsed = Math.round((t1 - t0) * 10) / 10;
totalElapsed += elapsed;
if (elapsed > whenExceedsTimeInMs || totalElapsed > whenExceedsTimeInMs || calls > whenExceedsCalls) {
const elapsedStr = padStart(Math.round(elapsed).toLocaleString(), ' ', 10);
const totalStr = padEnd(Math.round(totalElapsed).toLocaleString() + 'ms', ' ', 10);
const callsStr = padEnd(calls.toLocaleString(), ' ', 10);
const message = `%c ${elapsedStr}ms | total: ${totalStr} | calls: ${callsStr} | ${caller}`;
debouncedLog(message);
}
return result;
};
return descriptor;
};
}
function padStart(val: string, padStr: string, len: number): string {
return pad(val, padStr, len, false);
}
function padEnd(val: string, padStr: string, len: number): string {
return pad(val, padStr, len, true);
}
function pad(val: string, padStr: string, len: number, toEnd: boolean): string {
if (val.length > len) {
return val;
} else {
len -= val.length;
while (len > padStr.length) {
padStr += padStr;
}
return toEnd ? val + padStr.slice(0, len) : padStr.slice(0, len) + val;
}
}
function debounce(func: (...args: any[]) => void, wait: number): (...args: any[]) => void {
let timeout: any;
return function() {
const context = this;
const args = arguments;
const later = function() {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment