import type { URL } from 'node:url';
import { parentPort } from 'node:worker_threads';
+import { secondsToMilliseconds } from 'date-fns';
+
import {
+ ConfigurationSection,
type IncomingRequestCommand,
+ type LogConfiguration,
MessageType,
type RequestCommand,
type Statistics,
- type TimeSeries,
+ type StorageConfiguration,
+ type TimestampedData,
} from '../types';
import {
CircularArray,
Configuration,
Constants,
- Utils,
+ JSONStringifyWithMapSupport,
buildPerformanceStatisticsMessage,
+ extractTimeSeriesValues,
+ formatDurationSeconds,
+ generateUUID,
+ logPrefix,
logger,
median,
nthPercentile,
private readonly objName: string;
private performanceObserver!: PerformanceObserver;
private readonly statistics: Statistics;
- private displayInterval!: NodeJS.Timeout;
+ private displayInterval?: NodeJS.Timeout;
private constructor(objId: string, objName: string, uri: URL) {
this.objId = objId;
public static getInstance(
objId: string,
objName: string,
- uri: URL
+ uri: URL,
): PerformanceStatistics | undefined {
if (!PerformanceStatistics.instances.has(objId)) {
PerformanceStatistics.instances.set(objId, new PerformanceStatistics(objId, objName, uri));
}
public static beginMeasure(id: string): string {
- const markId = `${id.charAt(0).toUpperCase()}${id.slice(1)}~${Utils.generateUUID()}`;
+ const markId = `${id.charAt(0).toUpperCase()}${id.slice(1)}~${generateUUID()}`;
performance.mark(markId);
return markId;
}
public addRequestStatistic(
command: RequestCommand | IncomingRequestCommand,
- messageType: MessageType
+ messageType: MessageType,
): void {
switch (messageType) {
case MessageType.CALL_MESSAGE:
if (
this.statistics.statisticsData.has(command) &&
- this.statistics.statisticsData.get(command)?.countRequest
+ this.statistics.statisticsData.get(command)?.requestCount
) {
- ++this.statistics.statisticsData.get(command).countRequest;
+ ++this.statistics.statisticsData.get(command)!.requestCount!;
} else {
this.statistics.statisticsData.set(command, {
...this.statistics.statisticsData.get(command),
- countRequest: 1,
+ requestCount: 1,
});
}
break;
case MessageType.CALL_RESULT_MESSAGE:
if (
this.statistics.statisticsData.has(command) &&
- this.statistics.statisticsData.get(command)?.countResponse
+ this.statistics.statisticsData.get(command)?.responseCount
) {
- ++this.statistics.statisticsData.get(command).countResponse;
+ ++this.statistics.statisticsData.get(command)!.responseCount!;
} else {
this.statistics.statisticsData.set(command, {
...this.statistics.statisticsData.get(command),
- countResponse: 1,
+ responseCount: 1,
});
}
break;
case MessageType.CALL_ERROR_MESSAGE:
if (
this.statistics.statisticsData.has(command) &&
- this.statistics.statisticsData.get(command)?.countError
+ this.statistics.statisticsData.get(command)?.errorCount
) {
- ++this.statistics.statisticsData.get(command).countError;
+ ++this.statistics.statisticsData.get(command)!.errorCount!;
} else {
this.statistics.statisticsData.set(command, {
...this.statistics.statisticsData.get(command),
- countError: 1,
+ errorCount: 1,
});
}
break;
public start(): void {
this.startLogStatisticsInterval();
- if (Configuration.getPerformanceStorage().enabled) {
+ const performanceStorageConfiguration =
+ Configuration.getConfigurationSection<StorageConfiguration>(
+ ConfigurationSection.performanceStorage,
+ );
+ if (performanceStorageConfiguration.enabled) {
logger.info(
- `${this.logPrefix()} storage enabled: type ${
- Configuration.getPerformanceStorage().type
- }, uri: ${Configuration.getPerformanceStorage().uri}`
+ `${this.logPrefix()} storage enabled: type ${performanceStorageConfiguration.type}, uri: ${
+ performanceStorageConfiguration.uri
+ }`,
);
}
}
const lastPerformanceEntry = performanceObserverList.getEntries()[0];
// logger.debug(
// `${this.logPrefix()} '${lastPerformanceEntry.name}' performance entry: %j`,
- // lastPerformanceEntry
+ // lastPerformanceEntry,
// );
this.addPerformanceEntryToStatistics(lastPerformanceEntry);
});
private logStatistics(): void {
logger.info(`${this.logPrefix()}`, {
...this.statistics,
- statisticsData: Utils.JSONStringifyWithMapSupport(this.statistics.statisticsData),
+ statisticsData: JSONStringifyWithMapSupport(this.statistics.statisticsData),
});
}
private startLogStatisticsInterval(): void {
- const logStatisticsInterval = Configuration.getLog().enabled
- ? Configuration.getLog().statisticsInterval
+ const logConfiguration = Configuration.getConfigurationSection<LogConfiguration>(
+ ConfigurationSection.log,
+ );
+ const logStatisticsInterval = logConfiguration.enabled
+ ? logConfiguration.statisticsInterval!
: 0;
if (logStatisticsInterval > 0 && !this.displayInterval) {
this.displayInterval = setInterval(() => {
this.logStatistics();
- }, logStatisticsInterval * 1000);
+ }, secondsToMilliseconds(logStatisticsInterval));
logger.info(
- `${this.logPrefix()} logged every ${Utils.formatDurationSeconds(logStatisticsInterval)}`
+ `${this.logPrefix()} logged every ${formatDurationSeconds(logStatisticsInterval)}`,
);
} else if (this.displayInterval) {
logger.info(
- `${this.logPrefix()} already logged every ${Utils.formatDurationSeconds(
- logStatisticsInterval
- )}`
+ `${this.logPrefix()} already logged every ${formatDurationSeconds(logStatisticsInterval)}`,
);
- } else if (Configuration.getLog().enabled) {
+ } else if (logConfiguration.enabled) {
logger.info(
- `${this.logPrefix()} log interval is set to ${logStatisticsInterval?.toString()}. Not logging statistics`
+ `${this.logPrefix()} log interval is set to ${logStatisticsInterval}. Not logging statistics`,
);
}
}
}
// Update current statistics
this.statistics.updatedAt = new Date();
- this.statistics.statisticsData.get(entryName).countTimeMeasurement =
- this.statistics.statisticsData.get(entryName)?.countTimeMeasurement
- ? this.statistics.statisticsData.get(entryName).countTimeMeasurement + 1
- : 1;
- this.statistics.statisticsData.get(entryName).currentTimeMeasurement = entry.duration;
- this.statistics.statisticsData.get(entryName).minTimeMeasurement =
- this.statistics.statisticsData.get(entryName)?.minTimeMeasurement
- ? this.statistics.statisticsData.get(entryName).minTimeMeasurement > entry.duration
- ? entry.duration
- : this.statistics.statisticsData.get(entryName).minTimeMeasurement
- : entry.duration;
- this.statistics.statisticsData.get(entryName).maxTimeMeasurement =
- this.statistics.statisticsData.get(entryName)?.maxTimeMeasurement
- ? this.statistics.statisticsData.get(entryName).maxTimeMeasurement < entry.duration
- ? entry.duration
- : this.statistics.statisticsData.get(entryName).maxTimeMeasurement
- : entry.duration;
- this.statistics.statisticsData.get(entryName).totalTimeMeasurement =
- this.statistics.statisticsData.get(entryName)?.totalTimeMeasurement
- ? this.statistics.statisticsData.get(entryName).totalTimeMeasurement + entry.duration
- : entry.duration;
- this.statistics.statisticsData.get(entryName).avgTimeMeasurement =
- this.statistics.statisticsData.get(entryName).totalTimeMeasurement /
- this.statistics.statisticsData.get(entryName).countTimeMeasurement;
- this.statistics.statisticsData.get(entryName)?.timeMeasurementSeries instanceof CircularArray
+ this.statistics.statisticsData.get(entryName)!.timeMeasurementCount =
+ (this.statistics.statisticsData.get(entryName)?.timeMeasurementCount ?? 0) + 1;
+ this.statistics.statisticsData.get(entryName)!.currentTimeMeasurement = entry.duration;
+ this.statistics.statisticsData.get(entryName)!.minTimeMeasurement = Math.min(
+ entry.duration,
+ this.statistics.statisticsData.get(entryName)?.minTimeMeasurement ?? Infinity,
+ );
+ this.statistics.statisticsData.get(entryName)!.maxTimeMeasurement = Math.max(
+ entry.duration,
+ this.statistics.statisticsData.get(entryName)?.maxTimeMeasurement ?? -Infinity,
+ );
+ this.statistics.statisticsData.get(entryName)!.totalTimeMeasurement =
+ (this.statistics.statisticsData.get(entryName)?.totalTimeMeasurement ?? 0) + entry.duration;
+ this.statistics.statisticsData.get(entryName)!.avgTimeMeasurement =
+ this.statistics.statisticsData.get(entryName)!.totalTimeMeasurement! /
+ this.statistics.statisticsData.get(entryName)!.timeMeasurementCount!;
+ this.statistics.statisticsData.get(entryName)?.measurementTimeSeries instanceof CircularArray
? this.statistics.statisticsData
.get(entryName)
- ?.timeMeasurementSeries?.push({ timestamp: entry.startTime, value: entry.duration })
- : (this.statistics.statisticsData.get(entryName).timeMeasurementSeries =
- new CircularArray<TimeSeries>(Constants.DEFAULT_CIRCULAR_BUFFER_CAPACITY, {
+ ?.measurementTimeSeries?.push({ timestamp: entry.startTime, value: entry.duration })
+ : (this.statistics.statisticsData.get(entryName)!.measurementTimeSeries =
+ new CircularArray<TimestampedData>(Constants.DEFAULT_CIRCULAR_BUFFER_CAPACITY, {
timestamp: entry.startTime,
value: entry.duration,
}));
- this.statistics.statisticsData.get(entryName).medTimeMeasurement = median(
- this.extractTimeSeriesValues(
- this.statistics.statisticsData.get(entryName).timeMeasurementSeries
- )
+ this.statistics.statisticsData.get(entryName)!.medTimeMeasurement = median(
+ extractTimeSeriesValues(
+ this.statistics.statisticsData.get(entryName)!.measurementTimeSeries as TimestampedData[],
+ ),
);
- this.statistics.statisticsData.get(entryName).ninetyFiveThPercentileTimeMeasurement =
+ this.statistics.statisticsData.get(entryName)!.ninetyFiveThPercentileTimeMeasurement =
nthPercentile(
- this.extractTimeSeriesValues(
- this.statistics.statisticsData.get(entryName).timeMeasurementSeries
+ extractTimeSeriesValues(
+ this.statistics.statisticsData.get(entryName)!.measurementTimeSeries as TimestampedData[],
),
- 95
+ 95,
);
- this.statistics.statisticsData.get(entryName).stdDevTimeMeasurement = stdDeviation(
- this.extractTimeSeriesValues(
- this.statistics.statisticsData.get(entryName).timeMeasurementSeries
- )
+ this.statistics.statisticsData.get(entryName)!.stdDevTimeMeasurement = stdDeviation(
+ extractTimeSeriesValues(
+ this.statistics.statisticsData.get(entryName)!.measurementTimeSeries as TimestampedData[],
+ ),
);
- if (Configuration.getPerformanceStorage().enabled) {
+ if (
+ Configuration.getConfigurationSection<StorageConfiguration>(
+ ConfigurationSection.performanceStorage,
+ ).enabled
+ ) {
parentPort?.postMessage(buildPerformanceStatisticsMessage(this.statistics));
}
}
- private extractTimeSeriesValues(timeSeries: CircularArray<TimeSeries>): number[] {
- return timeSeries.map((timeSeriesItem) => timeSeriesItem.value);
- }
-
private logPrefix = (): string => {
- return Utils.logPrefix(` ${this.objName} | Performance statistics`);
+ return logPrefix(` ${this.objName} | Performance statistics`);
};
}