Skip to content

Commit

Permalink
Use different y-axes for metrics of different units
Browse files Browse the repository at this point in the history
- Tidy up the branch
  • Loading branch information
direnakkoc committed Nov 29, 2022
1 parent 9af9274 commit 8afae38
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 11 deletions.
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Automatic, best-practice CloudWatch **Dashboards** and **Alarms** for your SAM, CloudFormation, CDK and Serverless Framework applications.

SLIC Watch supports: _AWS Lambda, API Gateway, DynamoDB, Kinesis Data Streams, SQS Queues, Step Functions, ECS (Fargate or EC2), SNS, EventBridge and Application Load Balancer._
SLIC Watch supports: _AWS Lambda, API Gateway, DynamoDB, Kinesis Data Streams, SQS Queues, Step Functions, ECS (Fargate or EC2), SNS, EventBridge, Application Load Balancer and AppSync._

Supported tools include:
* ⚡️ **Serverless Framework** v2 and v3 via the [_SLIC Watch Serverless Plugin_](#getting-started-with-serverless-framework)
Expand All @@ -33,6 +33,7 @@ Supported tools include:
- [SNS](#sns)
- [EventBridge](#eventbridge)
- [Application Load Balancer](#application-load-balancer)
- [AppSync](#appsync)
- [Configuration](#configuration)
- [Top-level configuration](#top-level-configuration)
- [Function-level configuration](#function-level-configuration)
Expand Down Expand Up @@ -303,6 +304,18 @@ Application Load Balancer dashboard widgets show:
|**UnHealthy Host Count**|**Lambda User Error**|**Lambda Internal Error**|
|![UnHealthyHostCount](https://raw.githubusercontent.com/fourtheorem/slic-watch/main/docs/unHealthyHostCount.png) |![LambdaUserError](https://raw.githubusercontent.com/fourtheorem/slic-watch/main/docs/lambdaUserError.png)| |

### AppSync
AppSync alarms are created for:
1. 5XX Error
2. Latency

AppSync dashboard widgets show:

|5XX Error, Latency, 4XX Error, Request|
|--|
|![API Widget](https://raw.githubusercontent.com/fourtheorem/slic-watch/main/docs/appsyncAPI.png)|
|**Connect Server Error**, **Disconnect Server Error**, **Subscribe Server Error**, **Unsubscribe Server Error**,**PublishDataMessageServerError**|
|![Real-time Subscriptions Widget](https://raw.githubusercontent.com/fourtheorem/slic-watch/main/docs/appsyncRealTimeSubscriptions.png)|
## Configuration

Configuration is entirely optional - SLIC Watch provides defaults that work out of the box.
Expand Down
1 change: 1 addition & 0 deletions core/config-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ const commonWidgetProperties = {
width: { type: ['integer', 'null'], minimum: 1, maximum: 24 },
height: { type: ['integer', 'null'], minimum: 1, maximum: 1000 },
metricPeriod: { type: ['integer', 'null'], minimum: 60, multipleOf: 60 },
yAxis: { type: ['string', 'null'], enum: ['left', 'right'] },
Statistic: {
type: 'array',
items: {
Expand Down
10 changes: 5 additions & 5 deletions core/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,17 +146,16 @@ module.exports = function dashboard (dashboardConfig, functionDashboardConfigs,
*/
function createMetricWidget (title, metricDefs, config) {
const metrics = metricDefs.map(
({ namespace, metric, dimensions, stat }) => [
({ namespace, metric, dimensions, stat, yAxis }) => [
namespace,
metric,
...Object.entries(dimensions).reduce(
(acc, [name, value]) => [...acc, name, value],
[]
),
{ stat }
{ stat, yAxis }
]
)

return {
type: 'metric',
properties: {
Expand Down Expand Up @@ -654,7 +653,7 @@ module.exports = function dashboard (dashboardConfig, functionDashboardConfigs,
function createAppSyncWidgets (appSyncResources) {
const appSyncWidgets = []
const metricGroups = {
CloudWatch: ['5XXError', '4XXError', 'Latency', 'Requests'],
API: ['5XXError', '4XXError', 'Latency', 'Requests'],
'Real-time Subscriptions': ['ConnectServerError', 'DisconnectServerError', 'SubscribeServerError', 'UnsubscribeServerError', 'PublishDataMessageServerError']
}
const metricConfigs = getConfiguredMetrics(appSyncDashConfig)
Expand All @@ -672,7 +671,8 @@ module.exports = function dashboard (dashboardConfig, functionDashboardConfigs,
namespace: 'AWS/AppSync',
metric,
dimensions: { GraphQLAPIId: graphQLAPIId },
stat
stat,
yAxis: metricConfig.yAxis
})
}
}
Expand Down
4 changes: 3 additions & 1 deletion core/default-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ const defaultConfig = {
metricPeriod: 300,
width: 8,
height: 6,
yAxis: 'left',
Lambda: {
// Metrics per Lambda Function
Errors: {
Expand Down Expand Up @@ -362,7 +363,8 @@ const defaultConfig = {
Statistic: ['Sum']
},
Latency: {
Statistic: ['Average']
Statistic: ['Average'],
yAxis: 'right'
},
Requests: {
Statistic: ['Maximum']
Expand Down
38 changes: 35 additions & 3 deletions core/tests/dashboard.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,38 @@ test('A dashboard includes metrics for ALB', (t) => {
)
t.same(actualTitles, expectedTitles)
t.end()

test('No widgets are created if all AppSync metrics are disabled', (t) => {
const services = ['Lambda', 'ApiGateway', 'States', 'DynamoDB', 'SQS', 'Kinesis', 'ECS', 'SNS', 'Events', 'ApplicationELB', 'ApplicationELBTarget', 'AppSync']
const dashConfig = cloneDeep(defaultConfig.dashboard)
for (const service of services) {
for (const metricConfig of Object.values(dashConfig.widgets[service])) {
metricConfig.enabled = false
}
}
const dash = dashboard(dashConfig, emptyFuncConfigs, context)
const cfTemplate = createTestCloudFormationTemplate(appSyncCfTemplate)
dash.addDashboard(cfTemplate)
const dashResources = cfTemplate.getResourcesByType('AWS::CloudWatch::Dashboard')
t.same(dashResources, {})
t.end()
})

test('No widgets are created if all Application Load Balancer metrics are disabled', (t) => {
const services = ['Lambda', 'ApiGateway', 'States', 'DynamoDB', 'SQS', 'Kinesis', 'ECS', 'SNS', 'Events', 'ApplicationELB', 'ApplicationELBTarget', 'AppSync']
const dashConfig = cloneDeep(defaultConfig.dashboard)
for (const service of services) {
for (const metricConfig of Object.values(dashConfig.widgets[service])) {
metricConfig.enabled = false
}
}
const dash = dashboard(dashConfig, emptyFuncConfigs, context)
const cfTemplate = createTestCloudFormationTemplate(albCfTemplate)
dash.addDashboard(cfTemplate)
const dashResources = cfTemplate.getResourcesByType('AWS::CloudWatch::Dashboard')
t.same(dashResources, {})
t.end()
})
})

t.test('target groups with no Lambda targets are excluded from metrics', (t) => {
Expand Down Expand Up @@ -393,7 +425,7 @@ test('A dashboard includes metrics for ALB', (t) => {
}
t.same(namespaces, new Set(['AWS/AppSync']))
const expectedTitles = new Set([
'AppSync CloudWatch awesome-appsync',
'AppSync API awesome-appsync',
'AppSync Real-time Subscriptions awesome-appsync'
])
// eslint-disable-next-line no-template-curly-in-string
Expand Down Expand Up @@ -458,7 +490,7 @@ test('DynamoDB widgets are created without GSIs', (t) => {
})

test('No dashboard is created if all widgets are disabled', (t) => {
const services = ['Lambda', 'ApiGateway', 'States', 'DynamoDB', 'SQS', 'Kinesis', 'ECS', 'SNS', 'Events', 'ApplicationELB', 'ApplicationELBTarget']
const services = ['Lambda', 'ApiGateway', 'States', 'DynamoDB', 'SQS', 'Kinesis', 'ECS', 'SNS', 'Events', 'ApplicationELB', 'ApplicationELBTarget', 'AppSync']
const dashConfig = cloneDeep(defaultConfig.dashboard)
for (const service of services) {
dashConfig.widgets[service].enabled = false
Expand All @@ -472,7 +504,7 @@ test('No dashboard is created if all widgets are disabled', (t) => {
})

test('No dashboard is created if all metrics are disabled', (t) => {
const services = ['Lambda', 'ApiGateway', 'States', 'DynamoDB', 'SQS', 'Kinesis', 'ECS', 'SNS', 'Events', 'ApplicationELB', 'ApplicationELBTarget']
const services = ['Lambda', 'ApiGateway', 'States', 'DynamoDB', 'SQS', 'Kinesis', 'ECS', 'SNS', 'Events', 'ApplicationELB', 'ApplicationELBTarget', 'AppSync']
const dashConfig = cloneDeep(defaultConfig.dashboard)
for (const service of services) {
for (const metricConfig of Object.values(dashConfig.widgets[service])) {
Expand Down
Binary file added docs/appsyncAPI.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/appsyncRealTimeSubscriptions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions serverless-test-project-appsync/serverless-v2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ resources:
Value: books-table

#cognito users pool

CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
Expand Down Expand Up @@ -86,6 +87,7 @@ resources:
PreventUserExistenceErrors: ENABLED

#Cognito users pool Admin group

CognitoAdminGroup:
Type: AWS::Cognito::UserPoolGroup
Properties:
Expand All @@ -95,6 +97,7 @@ resources:
Description: "Admin users belong to this group"

#Cognito Admin IAM role

CognitoAdminIAMrole:
Type: AWS::IAM::Role
Properties:
Expand All @@ -121,6 +124,7 @@ resources:
RoleName: "bookstore-admin-role"

#Cognito users pool Customer group

CognitoCustomerGroup:
Type: AWS::Cognito::UserPoolGroup
Properties:
Expand All @@ -130,6 +134,7 @@ resources:
Description: "Customer belongs to this group"

#Cognito Customer IAM role

CognitoUserIAMrole:
Type: AWS::IAM::Role
Properties:
Expand Down
7 changes: 6 additions & 1 deletion serverless-test-project-appsync/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ custom:
appSync:
- ${file(serverless.appsync-api.yml)}
slicWatch:
# topicArn: ${env:ALARM_TOPIC}
topicArn: ${env:ALARM_TOPIC}
alarms:
Lambda:
Invocations:
Expand Down Expand Up @@ -64,6 +64,7 @@ resources:
Value: books-table

#cognito users pool

CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
Expand Down Expand Up @@ -97,6 +98,7 @@ resources:
PreventUserExistenceErrors: ENABLED

#Cognito users pool Admin group

CognitoAdminGroup:
Type: AWS::Cognito::UserPoolGroup
Properties:
Expand All @@ -106,6 +108,7 @@ resources:
Description: "Admin users belong to this group"

#Cognito Admin IAM role

CognitoAdminIAMrole:
Type: AWS::IAM::Role
Properties:
Expand All @@ -132,6 +135,7 @@ resources:
RoleName: "bookstore-admin-role"

#Cognito users pool Customer group

CognitoCustomerGroup:
Type: AWS::Cognito::UserPoolGroup
Properties:
Expand All @@ -141,6 +145,7 @@ resources:
Description: "Customer belongs to this group"

#Cognito Customer IAM role

CognitoUserIAMrole:
Type: AWS::IAM::Role
Properties:
Expand Down

0 comments on commit 8afae38

Please sign in to comment.