123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- """
- 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
|