mirror of
https://github.com/serverless/serverless.git
synced 2026-01-25 15:07:39 +00:00
Merge pull request #2819 from serverless/add-metrics-plugin
Add metrics plugin
This commit is contained in:
commit
b708f66f4e
@ -53,6 +53,7 @@ The Serverless Framework allows you to deploy auto-scaling, pay-per-execution, e
|
||||
<li><a href="./providers/aws/cli-reference/deploy.md">Deploy</a></li>
|
||||
<li><a href="./providers/aws/cli-reference/invoke.md">Invoke</a></li>
|
||||
<li><a href="./providers/aws/cli-reference/logs.md">Logs</a></li>
|
||||
<li><a href="./providers/aws/cli-reference/metrics.md">Metrics</a></li>
|
||||
<li><a href="./providers/aws/cli-reference/info.md">Info</a></li>
|
||||
<li><a href="./providers/aws/cli-reference/rollback.md">Rollback</a></li>
|
||||
<li><a href="./providers/aws/cli-reference/remove.md">Remove</a></li>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<!--
|
||||
title: Serverless Framework Commands - AWS Lambda - Info
|
||||
menuText: Info
|
||||
menuOrder: 6
|
||||
menuOrder: 7
|
||||
description: Display information about your deployed service and the AWS Lambda Functions, Events and AWS Resources it contains.
|
||||
layout: Doc
|
||||
-->
|
||||
@ -76,4 +76,4 @@ CreateThumbnailsLambdaFunctionArn: arn:aws:lambda:us-east-1:377024778620:functio
|
||||
TakeScreenshotLambdaFunctionArn: arn:aws:lambda:us-east-1:377024778620:function:lambda-screenshots-dev-takeScreenshot
|
||||
ServiceEndpoint: https://12341jc801.execute-api.us-east-1.amazonaws.com/dev
|
||||
ServerlessDeploymentBucketName: lambda-screenshots-dev-serverlessdeploymentbucket-15b7pkc04f98a
|
||||
```
|
||||
```
|
||||
|
||||
47
docs/providers/aws/cli-reference/metrics.md
Normal file
47
docs/providers/aws/cli-reference/metrics.md
Normal file
@ -0,0 +1,47 @@
|
||||
<!--
|
||||
title: Serverless Framework Commands - AWS Lambda - Metrics
|
||||
menuText: Metrics
|
||||
menuOrder: 6
|
||||
description: View metrics of your AWS Lambda Function within your terminal using the Serverless Framework
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
<!-- DOCS-SITE-LINK:START automatically generated -->
|
||||
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/cli-reference/metrics)
|
||||
<!-- DOCS-SITE-LINK:END -->
|
||||
|
||||
# Metrics
|
||||
|
||||
Lets you watch the metrics of a specific function.
|
||||
|
||||
```bash
|
||||
serverless metrics --function hello
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
- `--function` or `-f` The function you want to fetch the metrics for. **Required**
|
||||
- `--stage` or `-s` The stage you want to view the function metrics for. If not provided, the plugin will use the default stage listed in `serverless.yml`. If that doesn't exist either it'll just fetch the metrics from the `dev` stage.
|
||||
- `--region` or `-r` The region you want to view the function metrics for. If not provided, the plugin will use the default region listed in `serverless.yml`. If that doesn't exist either it'll just fetch the metrics from the `us-east-1` region.
|
||||
- `--startTime` A specific unit in time to start fetching metrics from (ie: `2010-10-20`, `1469705761`, `30m` (30 minutes ago), `2h` (2 days ago) or `3d` (3 days ago)). Date formats should be written in ISO 8601. Defaults to 24h ago.
|
||||
- `--endTime` A specific unit in time to end fetching metrics from (ie: `2010-10-21` or `1469705761`). Date formats should be written in ISO 8601. Defaults to now.
|
||||
|
||||
## Examples
|
||||
|
||||
**Note:** There's a small lag between invoking the function and actually having access to the metrics. It takes a few seconds for the metrics to show up right after invoking the function.
|
||||
|
||||
### See all metrics of the last 24h
|
||||
|
||||
```bash
|
||||
serverless metrics --function hello
|
||||
```
|
||||
|
||||
Displays all metrics for the last 24h.
|
||||
|
||||
### See metrics for a specific timespan
|
||||
|
||||
```bash
|
||||
serverless metrics --function hello --startTime 1970-01-01 --endTime 1970-01-02
|
||||
```
|
||||
|
||||
Displays all metrics for the time between January 1, 1970 and January 2, 1970.
|
||||
@ -1,7 +1,7 @@
|
||||
<!--
|
||||
title: Serverless Framework Commands - AWS Lambda - Remove
|
||||
menuText: Remove
|
||||
menuOrder: 7
|
||||
menuOrder: 8
|
||||
description: Remove a deployed Service and all of its AWS Lambda Functions, Events and Resources
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<!--
|
||||
title: Serverless Rollback CLI Command
|
||||
menuText: Rollback
|
||||
menuOrder: 9
|
||||
menuOrder: 10
|
||||
description: Rollback the Serverless service to a specific deployment
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<!--
|
||||
title: Serverless Framework Commands - AWS Lambda - Serverless Stats
|
||||
menuText: Serverless Stats
|
||||
menuOrder: 8
|
||||
menuOrder: 9
|
||||
description: Enables or disables Serverless Statistic logging within the Serverless Framework.
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
"./invoke/invoke.js",
|
||||
"./info/info.js",
|
||||
"./logs/logs.js",
|
||||
"./metrics/metrics.js",
|
||||
"./remove/remove.js",
|
||||
"./rollback/index.js",
|
||||
"./slstats/slstats.js",
|
||||
@ -17,6 +18,7 @@
|
||||
"./aws/invoke/index.js",
|
||||
"./aws/info/index.js",
|
||||
"./aws/logs/index.js",
|
||||
"./aws/metrics/awsMetrics.js",
|
||||
"./aws/remove/index.js",
|
||||
"./aws/rollback/index.js",
|
||||
"./aws/deploy/compile/functions/index.js",
|
||||
|
||||
212
lib/plugins/aws/metrics/awsMetrics.js
Normal file
212
lib/plugins/aws/metrics/awsMetrics.js
Normal file
@ -0,0 +1,212 @@
|
||||
'use strict';
|
||||
|
||||
const BbPromise = require('bluebird');
|
||||
const chalk = require('chalk');
|
||||
const _ = require('lodash');
|
||||
const moment = require('moment');
|
||||
const validate = require('../lib/validate');
|
||||
|
||||
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': () => BbPromise.bind(this)
|
||||
.then(this.extendedValidate)
|
||||
.then(this.getMetrics)
|
||||
.then(this.showMetrics),
|
||||
};
|
||||
}
|
||||
|
||||
extendedValidate() {
|
||||
this.validate();
|
||||
|
||||
// validate function exists in service
|
||||
this.options.function = this.serverless.service.getFunction(this.options.function).name;
|
||||
|
||||
const today = new Date();
|
||||
let yesterday = new Date();
|
||||
yesterday = yesterday.setDate(yesterday.getDate() - 1);
|
||||
yesterday = new Date(yesterday);
|
||||
|
||||
if (this.options.startTime) {
|
||||
const since = (['m', 'h', 'd']
|
||||
.indexOf(this.options.startTime[this.options.startTime.length - 1]) !== -1);
|
||||
if (since) {
|
||||
this.options.startTime = moment().subtract(this.options
|
||||
.startTime.replace(/\D/g, ''), this.options
|
||||
.startTime.replace(/\d/g, '')).valueOf();
|
||||
}
|
||||
} else {
|
||||
this.options.startTime = yesterday;
|
||||
}
|
||||
|
||||
this.options.endTime = this.options.endTime || today;
|
||||
|
||||
// finally create a new date object
|
||||
this.options.startTime = new Date(this.options.startTime);
|
||||
this.options.endTime = new Date(this.options.endTime);
|
||||
|
||||
return BbPromise.resolve();
|
||||
}
|
||||
|
||||
getMetrics() {
|
||||
const FunctionName = this.options.function;
|
||||
const StartTime = this.options.startTime;
|
||||
const EndTime = this.options.endTime;
|
||||
const Namespace = 'AWS/Lambda';
|
||||
|
||||
const hoursDiff = Math.abs(EndTime - StartTime) / 36e5;
|
||||
const Period = (hoursDiff > 24) ? 3600 * 24 : 3600;
|
||||
|
||||
const promises = [];
|
||||
|
||||
// get invocations
|
||||
const invocationsPromise = this.provider.request('CloudWatch',
|
||||
'getMetricStatistics',
|
||||
{
|
||||
StartTime,
|
||||
EndTime,
|
||||
MetricName: 'Invocations',
|
||||
Namespace,
|
||||
Period,
|
||||
Dimensions: [
|
||||
{
|
||||
Name: 'FunctionName',
|
||||
Value: FunctionName,
|
||||
},
|
||||
],
|
||||
Statistics: [
|
||||
'Sum',
|
||||
],
|
||||
Unit: 'Count',
|
||||
},
|
||||
this.options.stage,
|
||||
this.options.region
|
||||
);
|
||||
// get throttles
|
||||
const throttlesPromise = this.provider.request('CloudWatch',
|
||||
'getMetricStatistics',
|
||||
{
|
||||
StartTime,
|
||||
EndTime,
|
||||
MetricName: 'Throttles',
|
||||
Namespace,
|
||||
Period,
|
||||
Dimensions: [
|
||||
{
|
||||
Name: 'FunctionName',
|
||||
Value: FunctionName,
|
||||
},
|
||||
],
|
||||
Statistics: [
|
||||
'Sum',
|
||||
],
|
||||
Unit: 'Count',
|
||||
},
|
||||
this.options.stage,
|
||||
this.options.region
|
||||
);
|
||||
// get errors
|
||||
const errorsPromise = this.provider.request('CloudWatch',
|
||||
'getMetricStatistics',
|
||||
{
|
||||
StartTime,
|
||||
EndTime,
|
||||
MetricName: 'Errors',
|
||||
Namespace,
|
||||
Period,
|
||||
Dimensions: [
|
||||
{
|
||||
Name: 'FunctionName',
|
||||
Value: FunctionName,
|
||||
},
|
||||
],
|
||||
Statistics: [
|
||||
'Sum',
|
||||
],
|
||||
Unit: 'Count',
|
||||
},
|
||||
this.options.stage,
|
||||
this.options.region
|
||||
);
|
||||
// get avg. duration
|
||||
const avgDurationPromise = this.provider.request('CloudWatch',
|
||||
'getMetricStatistics',
|
||||
{
|
||||
StartTime,
|
||||
EndTime,
|
||||
MetricName: 'Duration',
|
||||
Namespace,
|
||||
Period,
|
||||
Dimensions: [
|
||||
{
|
||||
Name: 'FunctionName',
|
||||
Value: FunctionName,
|
||||
},
|
||||
],
|
||||
Statistics: [
|
||||
'Average',
|
||||
],
|
||||
Unit: 'Milliseconds',
|
||||
},
|
||||
this.options.stage,
|
||||
this.options.region
|
||||
);
|
||||
|
||||
// push all promises to the array which will be used to resolve those
|
||||
promises.push(invocationsPromise);
|
||||
promises.push(throttlesPromise);
|
||||
promises.push(errorsPromise);
|
||||
promises.push(avgDurationPromise);
|
||||
|
||||
return BbPromise.all(promises).then((metrics) => metrics);
|
||||
}
|
||||
|
||||
showMetrics(metrics) {
|
||||
let message = '';
|
||||
|
||||
message += `${chalk.yellow.underline(this.options.function)}\n`;
|
||||
|
||||
const formattedStartTime = moment(this.options.startTime).format('LLL');
|
||||
const formattedEndTime = moment(this.options.endTime).format('LLL');
|
||||
message += `${formattedStartTime} - ${formattedEndTime}\n\n`;
|
||||
|
||||
if (metrics && metrics.length > 0) {
|
||||
_.forEach(metrics, (metric) => {
|
||||
if (metric.Label === 'Invocations') {
|
||||
const datapoints = metric.Datapoints;
|
||||
const invocations = datapoints
|
||||
.reduce((previous, datapoint) => previous + datapoint.Sum, 0);
|
||||
message += `${chalk.yellow('Invocations:', invocations, '\n')}`;
|
||||
} else if (metric.Label === 'Throttles') {
|
||||
const datapoints = metric.Datapoints;
|
||||
const throttles = datapoints
|
||||
.reduce((previous, datapoint) => previous + datapoint.Sum, 0);
|
||||
message += `${chalk.yellow('Throttles:', throttles, '\n')}`;
|
||||
} else if (metric.Label === 'Errors') {
|
||||
const datapoints = metric.Datapoints;
|
||||
const errors = datapoints
|
||||
.reduce((previous, datapoint) => previous + datapoint.Sum, 0);
|
||||
message += `${chalk.yellow('Errors:', errors, '\n')}`;
|
||||
} else {
|
||||
const datapoints = metric.Datapoints;
|
||||
const duration = datapoints
|
||||
.reduce((previous, datapoint) => previous + datapoint.Average, 0);
|
||||
const formattedRoundedAvgDuration = `${Math.round(duration * 100) / 100}ms`;
|
||||
message += `${chalk.yellow('Duration (avg.):', formattedRoundedAvgDuration)}`;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
message += `${chalk.yellow('There are no metrics to show for these options')}`;
|
||||
}
|
||||
this.serverless.cli.consoleLog(message);
|
||||
return BbPromise.resolve(message);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AwsMetrics;
|
||||
319
lib/plugins/aws/metrics/awsMetrics.test.js
Normal file
319
lib/plugins/aws/metrics/awsMetrics.test.js
Normal file
@ -0,0 +1,319 @@
|
||||
'use strict';
|
||||
|
||||
const expect = require('chai').expect;
|
||||
const sinon = require('sinon');
|
||||
const BbPromise = require('bluebird');
|
||||
const AwsProvider = require('../provider/awsProvider');
|
||||
const AwsMetrics = require('./awsMetrics');
|
||||
const Serverless = require('../../../Serverless');
|
||||
const CLI = require('../../../classes/CLI');
|
||||
const chalk = require('chalk');
|
||||
|
||||
describe('AwsMetrics', () => {
|
||||
let awsMetrics;
|
||||
let serverless;
|
||||
|
||||
beforeEach(() => {
|
||||
serverless = new Serverless();
|
||||
serverless.cli = new CLI(serverless);
|
||||
serverless.setProvider('aws', new AwsProvider(serverless));
|
||||
const options = {
|
||||
stage: 'dev',
|
||||
region: 'us-east-1',
|
||||
};
|
||||
awsMetrics = new AwsMetrics(serverless, options);
|
||||
});
|
||||
|
||||
describe('#constructor()', () => {
|
||||
it('should set the serverless instance to this.serverless', () => {
|
||||
expect(awsMetrics.serverless).to.deep.equal(serverless);
|
||||
});
|
||||
|
||||
it('should set the passed in options to this.options', () => {
|
||||
expect(awsMetrics.options).to.deep.equal({ stage: 'dev', region: 'us-east-1' });
|
||||
});
|
||||
|
||||
it('should set the provider variable to the AwsProvider instance', () =>
|
||||
expect(awsMetrics.provider).to.be.instanceof(AwsProvider));
|
||||
|
||||
it('should have a "metrics:metrics" hook', () => {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
expect(awsMetrics.hooks['metrics:metrics']).to.not.be.undefined;
|
||||
});
|
||||
|
||||
it('should run promise chain in order for "metrics:metrics" hook', () => {
|
||||
const extendedValidateStub = sinon
|
||||
.stub(awsMetrics, 'extendedValidate').returns(BbPromise.resolve());
|
||||
const getMetricsStub = sinon
|
||||
.stub(awsMetrics, 'getMetrics').returns(BbPromise.resolve());
|
||||
const showMetricsStub = sinon
|
||||
.stub(awsMetrics, 'showMetrics').returns(BbPromise.resolve());
|
||||
|
||||
return awsMetrics.hooks['metrics:metrics']().then(() => {
|
||||
expect(extendedValidateStub.calledOnce).to.equal(true);
|
||||
expect(getMetricsStub.calledAfter(extendedValidateStub)).to.equal(true);
|
||||
expect(showMetricsStub.calledAfter(getMetricsStub)).to.equal(true);
|
||||
|
||||
awsMetrics.extendedValidate.restore();
|
||||
awsMetrics.getMetrics.restore();
|
||||
awsMetrics.showMetrics.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#extendedValidate()', () => {
|
||||
let validateStub;
|
||||
|
||||
beforeEach(() => {
|
||||
awsMetrics.serverless.service.functions = {
|
||||
function1: {},
|
||||
};
|
||||
awsMetrics.serverless.service.service = 'my-service';
|
||||
awsMetrics.options.function = 'function1';
|
||||
validateStub = sinon
|
||||
.stub(awsMetrics, 'validate').returns(BbPromise.resolve);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
awsMetrics.validate.restore();
|
||||
});
|
||||
|
||||
it('should call the shared validate() function', () =>
|
||||
awsMetrics.extendedValidate().then(() => {
|
||||
expect(validateStub.calledOnce).to.equal(true);
|
||||
})
|
||||
);
|
||||
|
||||
it('should set the startTime to yesterday as the default value if not provided', () => {
|
||||
awsMetrics.options.startTime = null;
|
||||
|
||||
let yesterday = new Date();
|
||||
yesterday = yesterday.setDate(yesterday.getDate() - 1);
|
||||
yesterday = new Date(yesterday);
|
||||
const yesterdaysYear = yesterday.getFullYear();
|
||||
const yesterdaysMonth = yesterday.getMonth() + 1;
|
||||
const yesterdaysDay = yesterday.getDate();
|
||||
const yesterdaysDate = `${yesterdaysYear}-${yesterdaysMonth}-${yesterdaysDay}`;
|
||||
|
||||
return awsMetrics.extendedValidate().then(() => {
|
||||
const defaultsStartTime = awsMetrics.options.startTime;
|
||||
const defaultsYear = defaultsStartTime.getFullYear();
|
||||
const defaultsMonth = defaultsStartTime.getMonth() + 1;
|
||||
const defaultsDay = defaultsStartTime.getDate();
|
||||
const defaultsDate = `${defaultsYear}-${defaultsMonth}-${defaultsDay}`;
|
||||
|
||||
expect(defaultsDate).to.equal(yesterdaysDate);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set the startTime to the provided value', () => {
|
||||
awsMetrics.options.startTime = '1970-01-01';
|
||||
|
||||
return awsMetrics.extendedValidate().then(() => {
|
||||
const startTime = awsMetrics.options.startTime.toISOString();
|
||||
const expectedStartTime = new Date('1970-01-01').toISOString();
|
||||
|
||||
expect(startTime).to.equal(expectedStartTime);
|
||||
});
|
||||
});
|
||||
|
||||
it('should translate human friendly syntax (e.g. 24h) for startTime', () => {
|
||||
awsMetrics.options.startTime = '24h'; // 24 hours ago
|
||||
|
||||
let yesterday = new Date();
|
||||
yesterday = yesterday.setDate(yesterday.getDate() - 1);
|
||||
yesterday = new Date(yesterday);
|
||||
const yesterdaysYear = yesterday.getFullYear();
|
||||
const yesterdaysMonth = yesterday.getMonth() + 1;
|
||||
const yesterdaysDay = yesterday.getDate();
|
||||
const yesterdaysDate = `${yesterdaysYear}-${yesterdaysMonth}-${yesterdaysDay}`;
|
||||
|
||||
return awsMetrics.extendedValidate().then(() => {
|
||||
const translatedStartTime = awsMetrics.options.startTime;
|
||||
const translatedYear = translatedStartTime.getFullYear();
|
||||
const translatedMonth = translatedStartTime.getMonth() + 1;
|
||||
const translatedDay = translatedStartTime.getDate();
|
||||
const translatedDate = `${translatedYear}-${translatedMonth}-${translatedDay}`;
|
||||
|
||||
expect(translatedDate).to.equal(yesterdaysDate);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set the endTime to today as the default value if not provided', () => {
|
||||
awsMetrics.options.endTime = null;
|
||||
|
||||
const today = new Date();
|
||||
const todaysYear = today.getFullYear();
|
||||
const todaysMonth = today.getMonth() + 1;
|
||||
const todaysDay = today.getDate();
|
||||
const todaysDate = `${todaysYear}-${todaysMonth}-${todaysDay}`;
|
||||
|
||||
return awsMetrics.extendedValidate().then(() => {
|
||||
const defaultsStartTime = awsMetrics.options.endTime;
|
||||
const defaultsYear = defaultsStartTime.getFullYear();
|
||||
const defaultsMonth = defaultsStartTime.getMonth() + 1;
|
||||
const defaultsDay = defaultsStartTime.getDate();
|
||||
const defaultsDate = `${defaultsYear}-${defaultsMonth}-${defaultsDay}`;
|
||||
|
||||
expect(defaultsDate).to.equal(todaysDate);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set the endTime to the provided value', () => {
|
||||
awsMetrics.options.endTime = '1970-01-01';
|
||||
|
||||
return awsMetrics.extendedValidate().then(() => {
|
||||
const endTime = awsMetrics.options.endTime.toISOString();
|
||||
const expectedEndTime = new Date('1970-01-01').toISOString();
|
||||
|
||||
expect(endTime).to.equal(expectedEndTime);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getMetrics()', () => {
|
||||
let requestStub;
|
||||
|
||||
beforeEach(() => {
|
||||
awsMetrics.options.function = 'function1';
|
||||
awsMetrics.options.startTime = '1970-01-01';
|
||||
awsMetrics.options.endTime = '1970-01-02';
|
||||
requestStub = sinon.stub(awsMetrics.provider, 'request');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
awsMetrics.provider.request.restore();
|
||||
});
|
||||
|
||||
it('should should gather metrics for the function', () => {
|
||||
// invocations
|
||||
requestStub.onCall(0).returns(
|
||||
BbPromise.resolve({
|
||||
ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755' },
|
||||
Label: 'Invocations',
|
||||
Datapoints: [],
|
||||
})
|
||||
);
|
||||
// throttles
|
||||
requestStub.onCall(1).returns(
|
||||
BbPromise.resolve({
|
||||
ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2' },
|
||||
Label: 'Throttles',
|
||||
Datapoints: [],
|
||||
})
|
||||
);
|
||||
// errors
|
||||
requestStub.onCall(2).returns(
|
||||
BbPromise.resolve({
|
||||
ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b' },
|
||||
Label: 'Errors',
|
||||
Datapoints: [],
|
||||
})
|
||||
);
|
||||
// duration
|
||||
requestStub.onCall(3).returns(
|
||||
BbPromise.resolve({
|
||||
ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164' },
|
||||
Label: 'Duration',
|
||||
Datapoints: [],
|
||||
})
|
||||
);
|
||||
|
||||
const expectedResult = [
|
||||
{ ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755' },
|
||||
Label: 'Invocations',
|
||||
Datapoints: [],
|
||||
},
|
||||
{ ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2' },
|
||||
Label: 'Throttles',
|
||||
Datapoints: [],
|
||||
},
|
||||
{ ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b' },
|
||||
Label: 'Errors',
|
||||
Datapoints: [],
|
||||
},
|
||||
{ ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164' },
|
||||
Label: 'Duration',
|
||||
Datapoints: [],
|
||||
},
|
||||
];
|
||||
|
||||
return awsMetrics.getMetrics().then((result) => {
|
||||
expect(result).to.deep.equal(expectedResult);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#showMetrics()', () => {
|
||||
let consoleLogStub;
|
||||
|
||||
beforeEach(() => {
|
||||
awsMetrics.options.function = 'function1';
|
||||
awsMetrics.options.startTime = '1970-01-01';
|
||||
awsMetrics.options.endTime = '1970-01-02';
|
||||
consoleLogStub = sinon.stub(serverless.cli, 'consoleLog').returns();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
serverless.cli.consoleLog.restore();
|
||||
});
|
||||
|
||||
it('should display all metrics for the given function', () => {
|
||||
const metrics = [
|
||||
{
|
||||
ResponseMetadata: {
|
||||
RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755',
|
||||
},
|
||||
Label: 'Invocations',
|
||||
Datapoints: [{ Sum: 12 }, { Sum: 8 }],
|
||||
},
|
||||
{
|
||||
ResponseMetadata: {
|
||||
RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2',
|
||||
},
|
||||
Label: 'Throttles',
|
||||
Datapoints: [{ Sum: 15 }, { Sum: 15 }],
|
||||
},
|
||||
{
|
||||
ResponseMetadata: {
|
||||
RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b',
|
||||
},
|
||||
Label: 'Errors',
|
||||
Datapoints: [{ Sum: 0 }],
|
||||
},
|
||||
{
|
||||
ResponseMetadata: {
|
||||
RequestId: '1f63db14-b569-11e6-8501-d98a275ce164',
|
||||
},
|
||||
Label: 'Duration',
|
||||
Datapoints: [{ Average: 1000 }],
|
||||
},
|
||||
];
|
||||
|
||||
let expectedMessage = '';
|
||||
expectedMessage += `${chalk.yellow.underline(awsMetrics.options.function)}\n`;
|
||||
expectedMessage += 'January 1, 1970 12:00 AM - January 2, 1970 12:00 AM\n\n';
|
||||
expectedMessage += `${chalk.yellow('Invocations: 20 \n')}`;
|
||||
expectedMessage += `${chalk.yellow('Throttles: 30 \n')}`;
|
||||
expectedMessage += `${chalk.yellow('Errors: 0 \n')}`;
|
||||
expectedMessage += `${chalk.yellow('Duration (avg.): 1000ms')}`;
|
||||
|
||||
return awsMetrics.showMetrics(metrics).then((message) => {
|
||||
expect(consoleLogStub.calledOnce).to.equal(true);
|
||||
expect(message).to.equal(expectedMessage);
|
||||
});
|
||||
});
|
||||
|
||||
it('should resolve with an error message if no metrics are available', () => {
|
||||
let expectedMessage = '';
|
||||
expectedMessage += `${chalk.yellow.underline(awsMetrics.options.function)}\n`;
|
||||
expectedMessage += 'January 1, 1970 12:00 AM - January 2, 1970 12:00 AM\n\n';
|
||||
expectedMessage += `${chalk.yellow('There are no metrics to show for these options')}`;
|
||||
|
||||
return awsMetrics.showMetrics().then((message) => {
|
||||
expect(consoleLogStub.calledOnce).to.equal(true);
|
||||
expect(message).to.equal(expectedMessage);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
40
lib/plugins/metrics/metrics.js
Normal file
40
lib/plugins/metrics/metrics.js
Normal file
@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
class Metrics {
|
||||
constructor(serverless, options) {
|
||||
this.serverless = serverless;
|
||||
this.options = options;
|
||||
|
||||
this.commands = {
|
||||
metrics: {
|
||||
usage: 'Show metrics for a specific function',
|
||||
lifecycleEvents: [
|
||||
'metrics',
|
||||
],
|
||||
options: {
|
||||
function: {
|
||||
usage: 'The function name',
|
||||
required: true,
|
||||
shortcut: 'f',
|
||||
},
|
||||
stage: {
|
||||
usage: 'Stage of the service',
|
||||
shortcut: 's',
|
||||
},
|
||||
region: {
|
||||
usage: 'Region of the service',
|
||||
shortcut: 'r',
|
||||
},
|
||||
startTime: {
|
||||
usage: 'Start time for the metrics retrieval (e.g. 1970-01-01)',
|
||||
},
|
||||
endTime: {
|
||||
usage: 'End time for the metrics retrieval (e.g. 1970-01-01)',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Metrics;
|
||||
34
lib/plugins/metrics/metrics.test.js
Normal file
34
lib/plugins/metrics/metrics.test.js
Normal file
@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
const expect = require('chai').expect;
|
||||
const Metrics = require('./metrics');
|
||||
const Serverless = require('../../Serverless');
|
||||
|
||||
describe('Metrics', () => {
|
||||
let metrics;
|
||||
let serverless;
|
||||
|
||||
beforeEach(() => {
|
||||
serverless = new Serverless();
|
||||
const options = {};
|
||||
metrics = new Metrics(serverless, options);
|
||||
});
|
||||
|
||||
describe('#constructor()', () => {
|
||||
it('should have the command "metrics"', () => {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
expect(metrics.commands.metrics).to.not.be.undefined;
|
||||
});
|
||||
|
||||
it('should have a lifecycle event "metrics"', () => {
|
||||
expect(metrics.commands.metrics.lifecycleEvents).to.deep.equal([
|
||||
'metrics',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should have a required option "function"', () => {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
expect(metrics.commands.metrics.options.function.required).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user