stellar-plus
Version:
beta version of stellar-plus, an all-in-one sdk for the Stellar blockchain
200 lines (199 loc) • 9.87 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProfilingHandler = void 0;
const tslib_1 = require("tslib");
class ProfilingHandler {
constructor() {
this.log = [];
this.resourceHandler = (methodName, costs, elapsedTime, feeCharged) => {
const entry = {
methodName,
costs,
feeCharged,
elapsedTime,
};
this.log.push(entry);
};
this.getLog = (options) => {
const filteredLog = (options === null || options === void 0 ? void 0 : options.filter) ? this.filterLog(this.log, options.filter) : this.log;
const aggregatedLog = (options === null || options === void 0 ? void 0 : options.aggregate) ? this.aggregateLog(filteredLog, options.aggregate) : filteredLog;
if (options === null || options === void 0 ? void 0 : options.clear) {
this.clearLog();
}
if ((options === null || options === void 0 ? void 0 : options.formatOutput) && options.formatOutput === 'csv') {
return this.formatAsCsv(aggregatedLog);
}
if ((options === null || options === void 0 ? void 0 : options.formatOutput) && options.formatOutput === 'text-table') {
return this.formatAsTable(aggregatedLog);
}
return aggregatedLog;
};
this.clearLog = () => {
this.log = [];
};
// apply filters to log and return filtered log
// filter by methods, resources and values
this.filterLog = (log, filters) => {
const { methods } = filters, resources = tslib_1.__rest(filters, ["methods"]);
const filteredLogByMethods = methods ? this.filterLogByMethods(log, methods) : log;
const filteredLogByResources = resources
? this.filterLogByResources(filteredLogByMethods, resources)
: filteredLogByMethods;
return filteredLogByResources;
};
this.filterLogByMethods = (log, methods) => {
return log.filter((entry) => methods.includes(entry.methodName));
};
this.filterLogByResources = (log, resources) => {
return log.reduce((acc, logEntry) => {
let shouldIncludeLogEntry = true;
const filteredCosts = Object.keys(logEntry.costs).reduce((costsAccumulator, resourceKey) => {
const resourceFilter = resources[resourceKey];
const resourceValue = logEntry.costs[resourceKey];
if (!resourceFilter) {
// Rule 1 and 5: Include the resource as is
costsAccumulator[resourceKey] = resourceValue;
}
else if (resourceFilter.include === false) {
// Rule 2: Exclude this resource from costs, but keep the log entry
// No action needed here, as the resource is simply not added to costsAccumulator
}
else {
// Rule 3 and 4: Check min/max only if include is true or undefined
const withinMinRange = resourceFilter.min === undefined || resourceValue >= resourceFilter.min;
const withinMaxRange = resourceFilter.max === undefined || resourceValue <= resourceFilter.max;
if (typeof resourceValue === 'number' && withinMinRange && withinMaxRange) {
costsAccumulator[resourceKey] = resourceValue;
}
else {
shouldIncludeLogEntry = false; // Exclude the entire log entry
}
}
return costsAccumulator;
}, {});
if (shouldIncludeLogEntry) {
acc.push(Object.assign(Object.assign({}, logEntry), { costs: filteredCosts }));
}
return acc;
}, []);
};
this.aggregateLog = (log, aggregateOptions) => {
var _a;
const costs = [];
const resources = Object.keys(((_a = log[0]) === null || _a === void 0 ? void 0 : _a.costs) || {});
resources.forEach((resourceKey) => {
const specificMethod = aggregateOptions[resourceKey];
const defaultMethod = aggregateOptions.all;
const aggregationMethod = specificMethod || defaultMethod;
if (aggregationMethod) {
const aggregatedValue = this.performAggregation(log, resourceKey, aggregationMethod);
costs.push([resourceKey, aggregatedValue]);
}
});
let elapsedTime;
if (aggregateOptions.elapsedTime) {
elapsedTime = this.performAggregation(log, 'elapsedTime', aggregateOptions.elapsedTime);
}
let feeCharged;
if (aggregateOptions.feeCharged) {
feeCharged = this.performAggregation(log, 'feeCharged', aggregateOptions.feeCharged);
}
return [
Object.assign({ methodName: 'aggregated', costs: Object.fromEntries(costs) }, { elapsedTime, feeCharged }),
];
};
this.performAggregation = (logEntries, resourceKey, aggregationMethod) => {
const values = logEntries.map((entry) => {
const value = resourceKey === 'elapsedTime'
? entry.elapsedTime
: resourceKey === 'feeCharged'
? entry.feeCharged
: entry.costs[resourceKey];
return typeof value === 'number' ? value : 0;
});
switch (aggregationMethod.method) {
case 'sum':
return values.reduce((acc, value) => acc + value, 0);
case 'average': {
const total = values.reduce((acc, value) => acc + value, 0);
return total / values.length;
}
case 'standardDeviation': {
const mean = values.reduce((acc, value) => acc + value, 0) / values.length;
const squaredDifferences = values.map((value) => (value - mean) ** 2);
const variance = squaredDifferences.reduce((acc, value) => acc + value, 0) / values.length;
const standardDeviation = Math.sqrt(variance);
return standardDeviation;
}
// Implement other aggregation methods like median, etc.
default:
throw new Error(`Unknown aggregation method: ${aggregationMethod.method}`);
}
};
}
formatAsTable(log) {
var _a;
if (log.length === 0) {
return ''; // Return an empty string if the log is empty
}
// Determine the column widths
const columnWidths = [];
const headers = ['Method Name', 'Elapsed Time', 'Fee Charged', ...Object.keys(((_a = log[0]) === null || _a === void 0 ? void 0 : _a.costs) || {})];
headers.forEach((header, index) => {
const maxLength = Math.max(header.length, ...log.map((entry) => {
var _a, _b;
return Math.max(entry.methodName.length, ((_a = entry.elapsedTime) === null || _a === void 0 ? void 0 : _a.toString()).length, entry.feeCharged.toString().length, (((_b = entry.costs[header]) === null || _b === void 0 ? void 0 : _b.toString()) || '').length);
}));
columnWidths[index] = maxLength;
});
// Generate the table header
const headerRow = headers.map((header, index) => header.padEnd(columnWidths[index])).join(' | ');
// Generate the separator row
const separatorRow = columnWidths.map((width) => '-'.repeat(width)).join('-+-');
// Generate the data rows
const dataRows = log
.map((entry) => {
var _a;
const row = [
entry.methodName,
(_a = entry.elapsedTime) === null || _a === void 0 ? void 0 : _a.toString().padEnd(columnWidths[1]),
entry.feeCharged.toString().padEnd(columnWidths[2]),
...headers.slice(3).map((header) => (entry.costs[header] || '')
.toString()
.padEnd(columnWidths[headers.indexOf(header) + 3])),
];
return row.join(' | ');
})
.join('\n');
// Combine the header, separator, and data rows
const formattedTable = [headerRow, separatorRow, dataRows].join('\n');
return formattedTable;
}
formatAsCsv(log) {
var _a;
if (log.length === 0) {
return ''; // Return an empty string if the log is empty
}
// Generate the header row
const headers = ['Method Name', 'Elapsed Time', 'Fee Charged', ...Object.keys(((_a = log[0]) === null || _a === void 0 ? void 0 : _a.costs) || {})];
const headerRow = headers.join(',');
// Generate the data rows
const dataRows = log
.map((entry) => {
const row = [
entry.methodName,
entry.elapsedTime,
entry.feeCharged,
...headers
.slice(3)
.map((header) => (entry.costs[header] || '').toString()),
];
return row.join(',');
})
.join('\n');
// Combine the header and data rows
const formattedCsv = [headerRow, dataRows].join('\n');
return formattedCsv;
}
}
exports.ProfilingHandler = ProfilingHandler;