Repository: serverless/serverless (JavaScript)
Date: 2016-12-12 15:07:33+00:00
Task type: Feature

Issue:
metrics command returning metrics for service
# This is a Feature Proposal

## Description

`serverless metrics` (without `-f`) should return aggregated metrics for whole service (all functions in service).

```
$ serverless metrics
December 1, 2016 10:34 AM - December 2, 2016 10:34 AM

Invocations: 19
Throttles: 12
Errors: 1
Duration (avg.): 92ms
```




Issue discussion:
This looks like a good improvement! Maybe we should add an `All functions combined` or something like that where usually the function name appears if you run this command with a `--function` option.
I like it. Since we gathering all the data for all the functions I would propose we show data for functions separately. In addition we can add 99th percentile if it's already exposed to the API:

```
December 5, 2016 7:47 PM - December 6, 2016 7:47 PM

All functions
Invocations: 19
Throttles: 12
Errors: 1
Duration (avg.): 92ms
Duration (99th.): 92ms

serverless-simple-http-endpoint-dev-currentTime
Invocations: 0 
Throttles: 0 
Errors: 0 
Duration (avg.): 0ms
Duration (99th.): 92ms

serverless-simple-http-endpoint-dev-createTodo
Invocations: 0 
Throttles: 0 
Errors: 0 
Duration (avg.): 0ms
Duration (99th.): 92ms
```
hmm, IMHO when `-f` is not specified we should show only 

```
December 5, 2016 7:47 PM - December 6, 2016 7:47 PM

All functions
Invocations: 19
Throttles: 12
Errors: 1
Duration (avg.): 92ms
Duration (99th.): 92ms
```
I like and see the value of showing all functions but IMHO it can get pretty confusing if you have many functions. So showing only the stuff for "All functions" might be easier to grasp.
So should we start with @mthenw proposal?

Would start the implementation if everyone is fine with that and submit a PR where we can discuss the implementation details.

Solution patch:
diff --git a/docs/providers/aws/cli-reference/metrics.md b/docs/providers/aws/cli-reference/metrics.md
index d407d06a4dc..64a3c98bf2d 100644
--- a/docs/providers/aws/cli-reference/metrics.md
+++ b/docs/providers/aws/cli-reference/metrics.md
@@ -15,12 +15,12 @@ layout: Doc
 Lets you watch the metrics of a specific function.
 
 ```bash
-serverless metrics --function hello
+serverless metrics
 ```
 
 ## Options
 
-- `--function` or `-f` The function you want to fetch the metrics for. **Required**
+- `--function` or `-f` The function you want to fetch the metrics for.
 - `--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.
@@ -30,18 +30,34 @@ serverless metrics --function hello
 
 **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
+### See service wide metrics for the last 24h
+
+```bash
+serverless metrics
+```
+
+Displays service wide metrics for the last 24h.
+
+### See service wide metrics for a specific timespan
+
+```bash
+serverless metrics --startTime 2016-01-01 --endTime 2016-01-02
+```
+
+Displays service wide metrics for the time between January 1, 2016 and January 2, 2016.
+
+### See all metrics for the function `hello` of the last 24h
 
 ```bash
 serverless metrics --function hello
 ```
 
-Displays all metrics for the last 24h.
+Displays all `hello` function metrics for the last 24h.
 
-### See metrics for a specific timespan
+### See metrics for the function `hello` of a specific timespan
 
 ```bash
-serverless metrics --function hello --startTime 1970-01-01 --endTime 1970-01-02
+serverless metrics --function hello --startTime 2016-01-01 --endTime 2016-01-02
 ```
 
-Displays all metrics for the time between January 1, 1970 and January 2, 1970.
+Displays all `hello` function metrics for the time between January 1, 2016 and January 2, 2016.
diff --git a/lib/plugins/aws/metrics/awsMetrics.js b/lib/plugins/aws/metrics/awsMetrics.js
index d32835bcce1..6d0e29aa3da 100644
--- a/lib/plugins/aws/metrics/awsMetrics.js
+++ b/lib/plugins/aws/metrics/awsMetrics.js
@@ -6,6 +6,13 @@ const _ = require('lodash');
 const moment = require('moment');
 const validate = require('../lib/validate');
 
+// helper functions
+const getRoundedAvgDuration = (duration, functionsCount) =>
+  (Math.round(duration * 100) / 100) / functionsCount;
+
+const reduceDatapoints = (datapoints, statistic) => datapoints
+  .reduce((previous, datapoint) => previous + datapoint[statistic], 0);
+
 class AwsMetrics {
   constructor(serverless, options) {
     this.serverless = serverless;
@@ -25,9 +32,6 @@ class AwsMetrics {
   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);
@@ -55,160 +59,178 @@ class AwsMetrics {
   }
 
   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);
+    // get all the function names in the service
+    let functions = this.serverless.service.getAllFunctions()
+      .map((func) => this.serverless.service.getFunction(func).name);
+
+    if (this.options.function) {
+      // validate if function can be found in service
+      this.options.function = this.serverless.service.getFunction(this.options.function).name;
+
+      // filter out the one function the user has specified through an option
+      functions = functions.filter((func) => func === this.options.function);
+    }
+
+    return BbPromise.map(functions, (func) => {
+      const FunctionName = func;
+      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`;
+    if (this.options.function) {
+      message += `${chalk.yellow.underline(this.options.function)}\n`;
+    } else {
+      message += `${chalk.yellow.underline('Service wide metrics')}\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) {
+      let invocations = 0;
+      let throttles = 0;
+      let errors = 0;
+      let duration = 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)}`;
-        }
+        _.forEach(metric, (funcMetric) => {
+          if (funcMetric.Label === 'Invocations') {
+            invocations += reduceDatapoints(funcMetric.Datapoints, 'Sum');
+          } else if (funcMetric.Label === 'Throttles') {
+            throttles += reduceDatapoints(funcMetric.Datapoints, 'Sum');
+          } else if (funcMetric.Label === 'Errors') {
+            errors += reduceDatapoints(funcMetric.Datapoints, 'Sum');
+          } else {
+            duration += reduceDatapoints(funcMetric.Datapoints, 'Average');
+          }
+        });
       });
+      const formattedDuration = `${getRoundedAvgDuration(duration, metrics.length)}ms`;
+      // display the data
+      message += `${chalk.yellow('Invocations:', invocations, '\n')}`;
+      message += `${chalk.yellow('Throttles:', throttles, '\n')}`;
+      message += `${chalk.yellow('Errors:', errors, '\n')}`;
+      message += `${chalk.yellow('Duration (avg.):', formattedDuration)}`;
     } else {
       message += `${chalk.yellow('There are no metrics to show for these options')}`;
     }
diff --git a/lib/plugins/metrics/metrics.js b/lib/plugins/metrics/metrics.js
index 3cc54bf4b61..393dc8b86da 100644
--- a/lib/plugins/metrics/metrics.js
+++ b/lib/plugins/metrics/metrics.js
@@ -14,7 +14,6 @@ class Metrics {
         options: {
           function: {
             usage: 'The function name',
-            required: true,
             shortcut: 'f',
           },
           stage: {


Test patch:
diff --git a/lib/plugins/aws/metrics/awsMetrics.test.js b/lib/plugins/aws/metrics/awsMetrics.test.js
index d6a30f08458..cb6df66bfff 100644
--- a/lib/plugins/aws/metrics/awsMetrics.test.js
+++ b/lib/plugins/aws/metrics/awsMetrics.test.js
@@ -175,7 +175,14 @@ describe('AwsMetrics', () => {
     let requestStub;
 
     beforeEach(() => {
-      awsMetrics.options.function = 'function1';
+      awsMetrics.serverless.service.functions = {
+        function1: {
+          name: 'func1',
+        },
+        function2: {
+          name: 'func2',
+        },
+      };
       awsMetrics.options.startTime = '1970-01-01';
       awsMetrics.options.endTime = '1970-01-02';
       requestStub = sinon.stub(awsMetrics.provider, 'request');
@@ -185,11 +192,12 @@ describe('AwsMetrics', () => {
       awsMetrics.provider.request.restore();
     });
 
-    it('should should gather metrics for the function', () => {
+    it('should gather service wide function metrics if no function option is specified', () => {
+      // stubs for function1
       // invocations
       requestStub.onCall(0).returns(
         BbPromise.resolve({
-          ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755' },
+          ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func1' },
           Label: 'Invocations',
           Datapoints: [],
         })
@@ -197,7 +205,7 @@ describe('AwsMetrics', () => {
       // throttles
       requestStub.onCall(1).returns(
         BbPromise.resolve({
-          ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2' },
+          ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func1' },
           Label: 'Throttles',
           Datapoints: [],
         })
@@ -205,7 +213,7 @@ describe('AwsMetrics', () => {
       // errors
       requestStub.onCall(2).returns(
         BbPromise.resolve({
-          ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b' },
+          ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func1' },
           Label: 'Errors',
           Datapoints: [],
         })
@@ -213,29 +221,146 @@ describe('AwsMetrics', () => {
       // duration
       requestStub.onCall(3).returns(
         BbPromise.resolve({
-          ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164' },
+          ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func1' },
+          Label: 'Duration',
+          Datapoints: [],
+        })
+      );
+      // stubs for function2
+      // invocations
+      requestStub.onCall(4).returns(
+        BbPromise.resolve({
+          ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func2' },
+          Label: 'Invocations',
+          Datapoints: [],
+        })
+      );
+      // throttles
+      requestStub.onCall(5).returns(
+        BbPromise.resolve({
+          ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func2' },
+          Label: 'Throttles',
+          Datapoints: [],
+        })
+      );
+      // errors
+      requestStub.onCall(6).returns(
+        BbPromise.resolve({
+          ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func2' },
+          Label: 'Errors',
+          Datapoints: [],
+        })
+      );
+      // duration
+      requestStub.onCall(7).returns(
+        BbPromise.resolve({
+          ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func2' },
           Label: 'Duration',
           Datapoints: [],
         })
       );
 
       const expectedResult = [
-        { ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755' },
+        [
+          { ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func1' },
+            Label: 'Invocations',
+            Datapoints: [],
+          },
+          { ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func1' },
+            Label: 'Throttles',
+            Datapoints: [],
+          },
+          { ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func1' },
+            Label: 'Errors',
+            Datapoints: [],
+          },
+          { ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func1' },
+            Label: 'Duration',
+            Datapoints: [],
+          },
+        ],
+        [
+          { ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func2' },
+            Label: 'Invocations',
+            Datapoints: [],
+          },
+          { ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func2' },
+            Label: 'Throttles',
+            Datapoints: [],
+          },
+          { ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func2' },
+            Label: 'Errors',
+            Datapoints: [],
+          },
+          { ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func2' },
+            Label: 'Duration',
+            Datapoints: [],
+          },
+        ],
+      ];
+
+      return awsMetrics.getMetrics().then((result) => {
+        expect(result).to.deep.equal(expectedResult);
+      });
+    });
+
+    it('should gather function metrics if function option is specified', () => {
+      // only display metrics for function1
+      awsMetrics.options.function = 'function1';
+
+      // stubs for function1
+      // invocations
+      requestStub.onCall(0).returns(
+        BbPromise.resolve({
+          ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func1' },
           Label: 'Invocations',
           Datapoints: [],
-        },
-        { ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2' },
+        })
+      );
+      // throttles
+      requestStub.onCall(1).returns(
+        BbPromise.resolve({
+          ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func1' },
           Label: 'Throttles',
           Datapoints: [],
-        },
-        { ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b' },
+        })
+      );
+      // errors
+      requestStub.onCall(2).returns(
+        BbPromise.resolve({
+          ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func1' },
           Label: 'Errors',
           Datapoints: [],
-        },
-        { ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164' },
+        })
+      );
+      // duration
+      requestStub.onCall(3).returns(
+        BbPromise.resolve({
+          ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func1' },
           Label: 'Duration',
           Datapoints: [],
-        },
+        })
+      );
+
+      const expectedResult = [
+        [
+          { ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func1' },
+            Label: 'Invocations',
+            Datapoints: [],
+          },
+          { ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func1' },
+            Label: 'Throttles',
+            Datapoints: [],
+          },
+          { ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func1' },
+            Label: 'Errors',
+            Datapoints: [],
+          },
+          { ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func1' },
+            Label: 'Duration',
+            Datapoints: [],
+          },
+        ],
       ];
 
       return awsMetrics.getMetrics().then((result) => {
@@ -248,7 +373,14 @@ describe('AwsMetrics', () => {
     let consoleLogStub;
 
     beforeEach(() => {
-      awsMetrics.options.function = 'function1';
+      awsMetrics.serverless.service.functions = {
+        function1: {
+          name: 'func1',
+        },
+        function2: {
+          name: 'func2',
+        },
+      };
       awsMetrics.options.startTime = '1970-01-01';
       awsMetrics.options.endTime = '1970-01-02';
       consoleLogStub = sinon.stub(serverless.cli, 'consoleLog').returns();
@@ -258,36 +390,118 @@ describe('AwsMetrics', () => {
       serverless.cli.consoleLog.restore();
     });
 
-    it('should display all metrics for the given function', () => {
+    it('should display service wide metrics if no function option is specified', () => {
       const metrics = [
-        {
-          ResponseMetadata: {
-            RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755',
+        [
+          {
+            ResponseMetadata: {
+              RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func1',
+            },
+            Label: 'Invocations',
+            Datapoints: [{ Sum: 12 }, { Sum: 8 }],
           },
-          Label: 'Invocations',
-          Datapoints: [{ Sum: 12 }, { Sum: 8 }],
-        },
-        {
-          ResponseMetadata: {
-            RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2',
+          {
+            ResponseMetadata: {
+              RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func1',
+            },
+            Label: 'Throttles',
+            Datapoints: [{ Sum: 15 }, { Sum: 15 }],
           },
-          Label: 'Throttles',
-          Datapoints: [{ Sum: 15 }, { Sum: 15 }],
-        },
-        {
-          ResponseMetadata: {
-            RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b',
+          {
+            ResponseMetadata: {
+              RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func1',
+            },
+            Label: 'Errors',
+            Datapoints: [{ Sum: 0 }],
           },
-          Label: 'Errors',
-          Datapoints: [{ Sum: 0 }],
-        },
-        {
-          ResponseMetadata: {
-            RequestId: '1f63db14-b569-11e6-8501-d98a275ce164',
+          {
+            ResponseMetadata: {
+              RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func1',
+            },
+            Label: 'Duration',
+            Datapoints: [{ Average: 1000 }],
           },
-          Label: 'Duration',
-          Datapoints: [{ Average: 1000 }],
-        },
+        ],
+        [
+          {
+            ResponseMetadata: {
+              RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func2',
+            },
+            Label: 'Invocations',
+            Datapoints: [{ Sum: 12 }, { Sum: 8 }],
+          },
+          {
+            ResponseMetadata: {
+              RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func2',
+            },
+            Label: 'Throttles',
+            Datapoints: [{ Sum: 15 }, { Sum: 15 }],
+          },
+          {
+            ResponseMetadata: {
+              RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func2',
+            },
+            Label: 'Errors',
+            Datapoints: [{ Sum: 0 }],
+          },
+          {
+            ResponseMetadata: {
+              RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func2',
+            },
+            Label: 'Duration',
+            Datapoints: [{ Average: 1000 }],
+          },
+        ],
+      ];
+
+      let expectedMessage = '';
+      expectedMessage += `${chalk.yellow.underline('Service wide metrics')}\n`;
+      expectedMessage += 'January 1, 1970 12:00 AM - January 2, 1970 12:00 AM\n\n';
+      expectedMessage += `${chalk.yellow('Invocations: 40 \n')}`;
+      expectedMessage += `${chalk.yellow('Throttles: 60 \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 display function metrics if function option is specified', () => {
+      awsMetrics.options.function = 'function1';
+
+      const metrics = [
+        [
+          {
+            ResponseMetadata: {
+              RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func1',
+            },
+            Label: 'Invocations',
+            Datapoints: [{ Sum: 12 }, { Sum: 8 }],
+          },
+          {
+            ResponseMetadata: {
+              RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func1',
+            },
+            Label: 'Throttles',
+            Datapoints: [{ Sum: 15 }, { Sum: 15 }],
+          },
+          {
+            ResponseMetadata: {
+              RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func1',
+            },
+            Label: 'Errors',
+            Datapoints: [{ Sum: 0 }],
+          },
+          {
+            ResponseMetadata: {
+              RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func1',
+            },
+            Label: 'Duration',
+            Datapoints: [{ Average: 1000 }],
+          },
+        ],
       ];
 
       let expectedMessage = '';
@@ -305,6 +519,8 @@ describe('AwsMetrics', () => {
     });
 
     it('should resolve with an error message if no metrics are available', () => {
+      awsMetrics.options.function = 'function1';
+
       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';
diff --git a/lib/plugins/metrics/metrics.test.js b/lib/plugins/metrics/metrics.test.js
index 7ad7afb97ad..f96cd08d100 100644
--- a/lib/plugins/metrics/metrics.test.js
+++ b/lib/plugins/metrics/metrics.test.js
@@ -25,10 +25,5 @@ describe('Metrics', () => {
         '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;
-    });
   });
 });