serverless
Version:
Serverless Framework - Build web, mobile and IoT applications with serverless architectures using AWS Lambda, Azure Functions, Google CloudFunctions & more
141 lines (117 loc) • 4.52 kB
JavaScript
;
const BbPromise = require('bluebird');
const chalk = require('chalk');
const _ = require('lodash');
const dayjs = require('dayjs');
const validate = require('./lib/validate');
const LocalizedFormat = require('dayjs/plugin/localizedFormat');
dayjs.extend(LocalizedFormat);
class AwsMetrics {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.provider = this.serverless.getProvider('aws');
Object.assign(this, validate);
this.hooks = {
'metrics:metrics': async () => {
this.extendedValidate();
const metrics = await this.getMetrics();
this.showMetrics(metrics);
},
};
}
extendedValidate() {
this.validate();
const today = new Date();
const yesterday = dayjs().subtract(1, 'day').toDate();
if (this.options.startTime) {
const sinceDateMatch = this.options.startTime.match(/(\d+)(m|h|d)/);
if (sinceDateMatch) {
this.options.startTime = dayjs().subtract(sinceDateMatch[1], sinceDateMatch[2]).valueOf();
}
}
// finally create a new date object
this.options.startTime = new Date(this.options.startTime || yesterday);
this.options.endTime = new Date(this.options.endTime || today);
}
async getMetrics() {
const StartTime = this.options.startTime;
const EndTime = this.options.endTime;
const hoursDiff = Math.abs(EndTime - StartTime) / 36e5;
const Period = hoursDiff > 24 ? 3600 * 24 : 3600;
const functions = this.options.function
? [this.serverless.service.getFunction(this.options.function).name]
: this.serverless.service.getAllFunctionsNames();
return Promise.all(
functions.map(async (functionName) => {
const commonParams = {
StartTime,
EndTime,
Namespace: 'AWS/Lambda',
Period,
Dimensions: [{ Name: 'FunctionName', Value: functionName }],
};
const invocationsParams = _.merge({}, commonParams, {
MetricName: 'Invocations',
Statistics: ['Sum'],
Unit: 'Count',
});
const throttlesParams = _.merge({}, commonParams, {
MetricName: 'Throttles',
Statistics: ['Sum'],
Unit: 'Count',
});
const errorsParams = _.merge({}, commonParams, {
MetricName: 'Errors',
Statistics: ['Sum'],
Unit: 'Count',
});
const averageDurationParams = _.merge({}, commonParams, {
MetricName: 'Duration',
Statistics: ['Average'],
Unit: 'Milliseconds',
});
const getMetrics = (params) =>
this.provider.request('CloudWatch', 'getMetricStatistics', params);
return BbPromise.all([
getMetrics(invocationsParams),
getMetrics(throttlesParams),
getMetrics(errorsParams),
getMetrics(averageDurationParams),
]);
})
);
}
showMetrics(metrics) {
let message = '';
if (this.options.function) {
message += `${chalk.yellow.underline(this.options.function)}\n`;
} else {
message += `${chalk.yellow.underline('Service wide metrics')}\n`;
}
const formattedStartTime = dayjs(this.options.startTime).format('LLL');
const formattedEndTime = dayjs(this.options.endTime).format('LLL');
message += `${formattedStartTime} - ${formattedEndTime}\n\n`;
if (metrics && metrics.length > 0) {
const getDatapointsByLabel = (Label) =>
_.flatten(
_.flatten(metrics)
.filter((metric) => metric.Label === Label)
.map((metric) => metric.Datapoints)
);
const invocationsCount = _.sumBy(getDatapointsByLabel('Invocations'), 'Sum');
const throttlesCount = _.sumBy(getDatapointsByLabel('Throttles'), 'Sum');
const errorsCount = _.sumBy(getDatapointsByLabel('Errors'), 'Sum');
const durationAverage = _.meanBy(getDatapointsByLabel('Duration'), 'Average') || 0;
// display the data
message += `${chalk.yellow('Invocations:', invocationsCount, '\n')}`;
message += `${chalk.yellow('Throttles:', throttlesCount, '\n')}`;
message += `${chalk.yellow('Errors:', errorsCount, '\n')}`;
message += `${chalk.yellow('Duration (avg.):', `${Number(durationAverage.toFixed(2))}ms`)}`;
} else {
message += `${chalk.yellow('There are no metrics to show for these options')}`;
}
this.serverless.cli.consoleLog(message);
}
}
module.exports = AwsMetrics;