Gems/AWSMetrics/cdk/aws_metrics/dashboard.py (173 lines of code) (raw):
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
from constructs import Construct
from aws_cdk import (
CfnOutput,
Duration,
aws_cloudwatch as cloudwatch
)
from . import aws_metrics_constants
from .layout_widget_construct import LayoutWidget
from .aws_utils import resource_name_sanitizer
class Dashboard:
"""
Create the real time analytics CloudWatch dashboard for the AWSMetrics Gem.
"""
def __init__(
self,
stack: Construct,
input_stream_name: str,
analytics_processing_lambda_name: str,
application_name: str,
delivery_stream_name: str = '',
events_processing_lambda_name: str = '',
) -> None:
self._dashboard_name = resource_name_sanitizer.sanitize_resource_name(
f'{stack.stack_name}-Dashboard', 'cloudwatch_dashboard')
self._dashboard = cloudwatch.Dashboard(
stack,
id="DashBoard",
dashboard_name=self._dashboard_name,
start=aws_metrics_constants.DASHBOARD_TIME_RANGE_START
)
self._dashboard.add_widgets(
LayoutWidget(
layout_description=aws_metrics_constants.DASHBOARD_GLOBAL_DESCRIPTION,
widgets=[
self._create_operational_health_layout(
input_stream_name,
delivery_stream_name,
analytics_processing_lambda_name,
events_processing_lambda_name),
self._create_real_time_analytics_layout()
],
max_width=aws_metrics_constants.DASHBOARD_MAX_WIDGET_WIDTH)
)
CfnOutput(
stack,
id='DashboardName',
description='CloudWatch dashboard to monitor the operational health and real-time metrics',
export_name=f"{application_name}:Dashboard",
value=self._dashboard_name)
def _create_operational_health_layout(
self,
input_stream_name: str,
delivery_stream_name: str,
analytics_processing_lambda_name: str,
events_processing_lambda_name: str) -> LayoutWidget:
"""
This layout contains operational health metrics during events ingestion and analytics processing.
@param input_stream_name Name of the input Kinesis data stream.
@param delivery_stream_name Name of the Kinesis Firehose delivery stream.
@param analytics_processing_lambda_name Name of the analytics processing Lambda function.
@param events_processing_lambda_name Name of the events processing Lambda function.
@return Operational health layout widget.
"""
operational_health_graph_widgets = list()
event_ingestion_left_widgets = [
cloudwatch.Metric(
metric_name="IncomingRecords",
label="Kinesis Incoming Records",
namespace="AWS/Kinesis",
period=Duration.minutes(aws_metrics_constants.DASHBOARD_METRICS_TIME_PERIOD),
statistic="Sum",
dimensions_map={
"StreamName": input_stream_name
}
)
]
if delivery_stream_name:
event_ingestion_left_widgets.append(
cloudwatch.Metric(
metric_name="DeliveryToS3.Records",
label="Firehose Delivery To S3 Records",
namespace="AWS/Firehose",
period=Duration.minutes(aws_metrics_constants.DASHBOARD_METRICS_TIME_PERIOD),
statistic="Sum",
dimensions_map={
"DeliveryStreamName": delivery_stream_name
}
)
)
operational_health_graph_widgets.append(
cloudwatch.GraphWidget(
title="Events Ingestion",
left=event_ingestion_left_widgets,
live_data=True
)
)
analytics_processing_lambda_errors_metrics, analytics_processing_lambda_error_rate_metrics = \
self._get_lambda_operational_health_metrics(
analytics_processing_lambda_name,
"Analytics Processing Lambda"
)
lambda_processing_left_widgets = [analytics_processing_lambda_errors_metrics]
lambda_processing_right_widgets = [analytics_processing_lambda_error_rate_metrics]
if events_processing_lambda_name:
events_processing_lambda_errors_metrics, events_processing_lambda_error_rate_metrics = \
self._get_lambda_operational_health_metrics(
events_processing_lambda_name,
"Events Processing Lambda"
)
lambda_processing_left_widgets.append(events_processing_lambda_errors_metrics)
lambda_processing_right_widgets.append(events_processing_lambda_error_rate_metrics)
operational_health_graph_widgets.append(
cloudwatch.GraphWidget(
title="Lambda Processing",
left=lambda_processing_left_widgets,
right=lambda_processing_right_widgets,
right_y_axis=cloudwatch.YAxisProps(
show_units=False,
min=0,
max=100
),
live_data=True,
view=cloudwatch.GraphWidgetView.TIME_SERIES
)
)
operational_health_layout = LayoutWidget(
layout_description=aws_metrics_constants.DASHBOARD_OPERATIONAL_HEALTH_DESCRIPTION,
widgets=operational_health_graph_widgets,
max_width=aws_metrics_constants.DASHBOARD_MAX_WIDGET_WIDTH // 2)
return operational_health_layout
def _get_lambda_operational_health_metrics(self, function_name: str, metrics_label_prefix: str):
"""
Get the errors and error rate metrics for the provided Lambda function.
@param function_name Name of the Lambda function.
@param metrics_label_prefix Prefix for the metrics Label. Metrics Label needs to be unique in a graph.
@return Error and error rate metrics of the Lambda function.
"""
lambda_errors_metrics = cloudwatch.Metric(
metric_name='Errors',
label=f'{metrics_label_prefix} Errors',
namespace='AWS/Lambda',
period=Duration.minutes(aws_metrics_constants.DASHBOARD_METRICS_TIME_PERIOD),
statistic='Sum',
dimensions_map={
'FunctionName': function_name
}
)
error_metrics_id = f'{metrics_label_prefix.replace(" ", "_")}_error'.lower()
invocations_metrics_id = f'{metrics_label_prefix.replace(" ", "_")}_invocations'.lower()
# Divide the Errors metric by the Invocations metric to get an error rate.
lambda_error_rate_metrics = cloudwatch.MathExpression(
expression=f'100 - 100 * {error_metrics_id} / MAX([{error_metrics_id}, {invocations_metrics_id}])',
period=Duration.minutes(aws_metrics_constants.DASHBOARD_METRICS_TIME_PERIOD),
label=f'{metrics_label_prefix} Success Rate (%)',
using_metrics={
error_metrics_id: lambda_errors_metrics,
invocations_metrics_id: cloudwatch.Metric(
metric_name='Invocations',
namespace='AWS/Lambda',
period=Duration.minutes(aws_metrics_constants.DASHBOARD_METRICS_TIME_PERIOD),
statistic='Sum',
dimensions_map={
'FunctionName': function_name
}
),
}
)
return lambda_errors_metrics, lambda_error_rate_metrics
def _create_real_time_analytics_layout(self) -> LayoutWidget:
"""
This layout contains real-time analytics metrics including login.
"""
real_time_analytics_layout = LayoutWidget(
layout_description=aws_metrics_constants.DASHBOARD_REAL_TIME_ANALYTICS_DESCRIPTION,
widgets=[
cloudwatch.GraphWidget(
title="Logins",
left=[
cloudwatch.Metric(
metric_name="TotalLogins",
label="Logins",
namespace="AWSMetrics",
period=Duration.minutes(aws_metrics_constants.DASHBOARD_METRICS_TIME_PERIOD),
statistic="Sum"
)
],
live_data=True
)
],
max_width=aws_metrics_constants.DASHBOARD_MAX_WIDGET_WIDTH // 2)
return real_time_analytics_layout
@property
def dashboard_name(self) -> str:
return self._dashboard_name