import { assoc , flatten , map , mapObjIndexed , values } from 'ramda' ; import { mean , median , percentile } from 'stats-lite' ; import { HttpAgentSingleton } from '../HttpClient/middlewares/request/HttpAgentSingleton' ; import { incomingRequestStats , } from '../service/worker/runtime/http/middlewares/requestStats' ; import { EnvMetric , NamedMetric } from '../service/worker/runtime/statusTrack' ; import { hrToMillis } from '../utils/time' ; export interface Metric { name : string ; [ key ] : number | string | null ; } const production = process . env . VTEX_PRODUCTION === 'true' ; interface Aggregate { count : number ; mean : number ; median : number ; percentile95 : number ; percentile99 : number ; max : number ; } type AggregateMetric = EnvMetric & Aggregate ; interface GetStats { getStats ( ) : { [ key ] : number | boolean | string | undefined ; } ; } let lastCpu = process . cpuUsage ( ) ; function cpuUsage ( ) { const diff = process . cpuUsage ( lastCpu ) ; lastCpu = { system : lastCpu . system + diff . system , user : lastCpu . user + diff . user , } ; return diff ; } function getIncomingRequestStats ( ) { const stats = incomingRequestStats . get ( ) ; incomingRequestStats . clear ( ) ; return stats ; } export class MetricsAccumulator { private metricsMillis : Record < string , number [ ] > ; private extensions : Record < string , Record < string , string | number > > ; private cacheMap : Record < string , GetStats > ; private onFlushMetrics : Array < ( ) => Metric | Metric [ ] > ; constructor ( ) { this . metricsMillis = { } ; this . extensions = { } ; this . onFlushMetrics = [ ] ; this . cacheMap = { } ; } public batchMetric = ( name , timeMillis ? , extensions ? ) => { if ( ! this . metricsMillis [ name ] ) { this . metricsMillis [ name ] = [ ] ; } if ( timeMillis != null ) { this . metricsMillis [ name ] . push ( timeMillis ) ; } if ( extensions ) { if ( ! this . extensions [ name ] ) { this . extensions [ name ] = { } ; } for ( const [ key , value ] of Object . entries ( extensions ) ) { const existing = this . extensions [ name ] [ key ] ; if ( typeof value === 'string' || typeof existing === 'string' ) { this . extensions [ name ] [ key ] = value ; } else if ( typeof value === 'number' ) { this . extensions [ name ] [ key ] = ( existing || 0 ) + value ; } } } } ; param name param diffNs param extensions see https public batch = ( name , diffNs ? , extensions ? ) => { this . batchMetric ( name , diffNs ? hrToMillis ( diffNs ) : undefined , extensions ) ; } ; public addOnFlushMetric = ( metricFn ) => { this . onFlushMetrics . push ( metricFn ) ; } ; public trackCache = ( name , cacheInstance ) => { this . cacheMap [ name ] = cacheInstance ; } ; public statusTrack = ( ) => { return this . flushMetrics ( ) ; } ; private metricToAggregate = ( value , key ) : AggregateMetric => { const aggregate = { name : key , count : value . length , max : Math . max ( ... value ) , mean : mean ( value ) , median : median ( value ) , percentile95 : percentile ( value , 0.95 ) , percentile99 : percentile ( value , 0.99 ) , production , ... this . extensions [ key ] , } ; delete this . metricsMillis [ key ] ; delete this . extensions [ key ] ; return aggregate ; } ; private cacheToMetric = ( value , key ) : EnvMetric => ( { ... value . getStats ( ) , name : `${ key }-cache` , production , } ) ; private flushMetrics = ( ) : EnvMetric [ ] => { const aggregateMetrics = values ( mapObjIndexed ( this . metricToAggregate , this . metricsMillis ) ) ; const systemMetrics = [ { ... cpuUsage ( ) , name : 'cpu' , production , } , { ... process . memoryUsage ( ) , name : 'memory' , production , } , { ... HttpAgentSingleton . httpAgentStats ( ) , name : 'httpAgent' , production , } , { ... getIncomingRequestStats ( ) , name : 'incomingRequest' , production , } , ] ; const onFlushMetrics = flatten ( map ( getMetric => getMetric ( ) , this . onFlushMetrics ) ) as NamedMetric [ ] ; const envFlushMetric = map ( assoc ( 'production' , production ) , onFlushMetrics ) as EnvMetric [ ] ; const cacheMetrics = values ( mapObjIndexed ( this . cacheToMetric , this . cacheMap ) ) ; return [ ... systemMetrics , ... aggregateMetrics , ... envFlushMetric , ... cacheMetrics ] ; } ; }