import { IRequestResponse , IThriftClientFilterConfig , IThriftRequest , NextFunction , } from '@creditkarma/thrift-client' ; import { LogFunction } from '@creditkarma/thrift-server-core' ; import { defaultLogger } from './logger' ; function getStartTime ( ) { return process . hrtime ( ) ; } function getTimings ( startTime ) { const duration = process . hrtime ( startTime ) ; return duration [ 0 ] * 1e3 + duration [ 1 ] * 1e-6 ; } interface ITimingUpdate { methodName : string ; duration : number ; status : 'error' | 'success' ; } export interface IStatusCount { error : number ; success : number ; } export interface ITimingFilterOptions { remoteServiceName : string ; interval ? : number ; logger ? : LogFunction ; tags ? : Array < string > ; } export interface IRequestsPerMethod { [ name ] : number ; } export interface ITimingEvent { status : IStatusCount ; maxDuration : number ; averageDuration : number ; requestsPerMethod : IRequestsPerMethod ; } export function ThriftClientTimingFilter < RequestContext > ( { remoteServiceName , interval = 5000 , logger = defaultLogger , tags = [ ] , } ) { let maxTime = 0 ; let totalTime = 0 ; let count = 0 ; const statusCount = { success : 0 , error : 0 , } ; const requestsPerMethod = { } ; function logTimings ( ) { const timingEvent = { status : statusCount , maxDuration : maxTime , averageDuration : totalTime / count , requestsPerMethod , } ; logger ( [ 'info' , 'metrics' , 'RequestDuration' , remoteServiceName , ... tags ] , timingEvent ) ; statusCount . error = 0 ; statusCount . success = 0 ; maxTime = 0 ; totalTime = 0 ; count = 0 ; for ( const key in requestsPerMethod ) { if ( requestsPerMethod . hasOwnProperty ( key ) ) { requestsPerMethod [ key ] = 0 ; } } } function updateData ( { methodName , duration , status } ) { count += 1 ; statusCount [ status ] += 1 ; totalTime += duration ; if ( requestsPerMethod [ methodName ] === undefined ) { requestsPerMethod [ methodName ] = 0 ; } requestsPerMethod [ methodName ] += 1 ; if ( duration > maxTime ) { maxTime = duration ; } } function scheduleUpdate ( ) { const timer = setTimeout ( ( ) => { logTimings ( ) ; scheduleUpdate ( ) ; } , interval ) ; timer . unref ( ) ; } scheduleUpdate ( ) ; return { handler ( request , next ) { const startTime = getStartTime ( ) ; return next ( ) . then ( ( res ) => { updateData ( { methodName : request . methodName , duration : getTimings ( startTime ) , status : 'success' , } ) ; return res ; } ) . catch ( ( err ) => { updateData ( { methodName : request . methodName , duration : getTimings ( startTime ) , status : 'error' , } ) ; return Promise . reject ( err ) ; } ) ; } , } ; }