import { performance , PerformanceObserver , PerformanceEntry } from 'perf_hooks' ; import fs from 'fs' ; import Knex from 'knex' ; export function setupMetrics ( metricsOutputFile ) { if ( metricsOutputFile ) { fs . writeFileSync ( metricsOutputFile , '' , { flag : 'w' } ) ; } const log = ( entry ) : void => { if ( metricsOutputFile ) { fs . appendFileSync ( metricsOutputFile , JSON . stringify ( entry ) + '\n' ) ; } else { console . log ( JSON . stringify ( entry ) ) ; } } ; const obs = new PerformanceObserver ( list => { for ( const entry of list . getEntries ( ) ) { log ( entry ) ; } } ) ; obs . observe ( { entryTypes : [ 'node' , 'measure' , 'gc' , 'function' , 'http2' , 'http' ] , buffered : false , } ) ; } export function setupDBMetrics ( knex ) { knex . on ( 'query' , query => { const uid = query . __knexQueryUid ; performance . mark ( `${ uid }-start` ) ; } ) . on ( 'query-response' , ( response , query ) => { const uid = query . __knexQueryUid ; performance . mark ( `${ uid }-end` ) ; performance . measure ( `query-${ query . sql }` , `${ uid }-start` , `${ uid }-end` ) ; } ) ; } export const timerFactory = ( timingMetrics , prefix ) => async < T > ( label , cb ) : Promise < T > => timeAsync ( timingMetrics , `${ prefix }: ${ label }` , cb ) ; export const timerFactorySync = ( timingMetrics , prefix ) => < T > ( label , cb ) : T => timeSync ( timingMetrics , `${ prefix }: ${ label }` , cb ) ; async function timeAsync < T > ( timingMetrics , label , cb ) { if ( timingMetrics ) { performance . mark ( `${ label }-start` ) ; const result = await cb ( ) ; performance . mark ( `${ label }-end` ) ; performance . measure ( label , `${ label }-start` , `${ label }-end` ) ; return result ; } else { return await cb ( ) ; } } function timeSync < T > ( timingMetrics , label , cb ) { if ( timingMetrics ) { performance . mark ( `${ label }-start` ) ; const result = cb ( ) ; performance . mark ( `${ label }-end` ) ; performance . measure ( label , `${ label }-start` , `${ label }-end` ) ; return result ; } else { return cb ( ) ; } } param objectOrFunction export function recordFunctionMetrics < T > ( objectOrFunction , timingMetrics = true ) { if ( timingMetrics ) { if ( typeof objectOrFunction === 'object' ) { const functionKeys = Object . keys ( objectOrFunction ) . concat ( Object . getOwnPropertyNames ( Object . getPrototypeOf ( objectOrFunction ) ) ) . filter ( k => typeof ( < any > objectOrFunction ) [ k ] === 'function' ) ; for ( const fk of functionKeys ) { ( < any > objectOrFunction ) [ fk ] = performance . timerify ( ( < any > objectOrFunction ) [ fk ] ) ; } } else if ( typeof objectOrFunction === 'function' ) { return ( performance . timerify ( objectOrFunction as any ) as unknown ) as T ; } } return objectOrFunction ; }