Last active
February 7, 2019 17:51
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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