Header menu logo FsCDK

CloudWatch CloudWatch Monitoring and Dashboards

CloudWatch provides comprehensive monitoring for your AWS resources with dashboards, log groups, metric filters, and subscription filters. Monitor metrics, logs, and alarms in a customizable (and visual) interface.

Quick Start

#r "../src/bin/Release/net8.0/publish/Amazon.JSII.Runtime.dll"
#r "../src/bin/Release/net8.0/publish/Constructs.dll"
#r "../src/bin/Release/net8.0/publish/Amazon.CDK.Lib.dll"
#r "../src/bin/Release/net8.0/publish/FsCDK.dll"

open FsCDK
open Amazon.CDK
open Amazon.CDK.AWS.CloudWatch
open Amazon.CDK.AWS.Lambda
open Amazon.CDK.AWS.DynamoDB

Basic Dashboard

Create a simple dashboard to monitor Lambda functions.

stack "BasicDashboard" {
    let myFunction =
        lambda "MyFunction" {
            runtime Runtime.DOTNET_8
            handler "App::Handler"
            code "./lambda"
        }

    let invocationsMetric = myFunction.Function.Value.MetricInvocations()
    let errorsMetric = myFunction.Function.Value.MetricErrors()

    dashboard "LambdaDashboard" {
        dashboardName "lambda-monitoring"

        widgetRow
            [ DashboardWidgets.metricWidget "Invocations" [ invocationsMetric ]
              DashboardWidgets.metricWidget "Errors" [ errorsMetric ] ]
    }
}

Multi-Resource Dashboard

Monitor multiple services in one dashboard.

stack "MultiResourceDashboard" {
    let apiFunction =
        lambda "API" {
            runtime Runtime.DOTNET_8
            handler "App::API"
            code "./lambda"
        }

    let dataTable =
        table "Data" {
            partitionKey "id" AttributeType.STRING
            billingMode BillingMode.PAY_PER_REQUEST
        }

    let apiMetric = apiFunction.Function.Value.MetricInvocations()
    let apiDuration = apiFunction.Function.Value.MetricDuration()
    let tableReads = dataTable.Table.Value.MetricConsumedReadCapacityUnits()
    let tableWrites = dataTable.Table.Value.MetricConsumedWriteCapacityUnits()

    dashboard "ApplicationDashboard" {
        dashboardName "application-monitoring"
        defaultInterval (Duration.Minutes(5.0))

        // First row: Lambda metrics
        widgetRow
            [ DashboardWidgets.metricWidget "API Invocations" [ apiMetric ]
              DashboardWidgets.metricWidget "API Duration" [ apiDuration ] ]

        // Second row: DynamoDB metrics
        widgetRow
            [ DashboardWidgets.metricWidget "Table Reads" [ tableReads ]
              DashboardWidgets.metricWidget "Table Writes" [ tableWrites ] ]
    }
}

Dashboard with Alarms

Include CloudWatch alarms for critical metrics.

stack "DashboardWithAlarms" {
    let webFunction =
        lambda "WebApp" {
            runtime Runtime.DOTNET_8
            handler "App::Handle"
            code "./lambda"
        }

    let errorMetric = webFunction.Function.Value.MetricErrors()

    let errorAlarm =
        // CloudWatch Alarm for Lambda errors
        cloudwatchAlarm "lambda-error-alarm" {
            description "Alert when error rate is high"
            metric errorMetric
            dimensions [ "FunctionName", "my-function" ]
            statistic "Sum"
            threshold 10.0
            evaluationPeriods 2
            period (Duration.Minutes 5.0)
        }

    dashboard "AlertingDashboard" {
        dashboardName "web-app-alerts"

        widgetRow [ DashboardWidgets.metricWidget "Errors" [ errorMetric ] ]

        widget (DashboardWidgets.alarmWidgetSpec errorAlarm)
    }
}

Dashboard with Text Widgets

Add explanatory text and documentation.

stack "DocumentedDashboard" {
    dashboard "ProductionDashboard" {
        dashboardName "production-overview"

        widget (
            DashboardWidgets.textWidget
                "# Production System Overview\n\nThis dashboard monitors critical production metrics.\n\n**Contact:** ops-team@example.com"
        )

    // Add metric widgets here
    }
}

Custom Time Range

Set specific time ranges for the dashboard.

stack "CustomTimeRange" {
    dashboard "HistoricalDashboard" {
        dashboardName "last-7-days"
        defaultInterval (Duration.Days(7.0))
        startTime "-P7D" // ISO 8601 duration: 7 days ago
        endTime "PT0H" // Now
    }
}

CloudWatch Log Groups

Create and configure log groups for your applications.

stack "LogGroupsStack" {
    // Lambda function log group with custom retention
    logGroup "/aws/lambda/my-function" {
        retention Amazon.CDK.AWS.Logs.RetentionDays.ONE_MONTH
        removalPolicy RemovalPolicy.DESTROY
    }

    // ECS service log group
    logGroup (CloudWatchLogsHelpers.ecsLogGroup "my-service" "production") {
        retention CloudWatchLogsHelpers.RetentionPeriods.production
    }

    // API Gateway log group
    logGroup (CloudWatchLogsHelpers.apiGatewayLogGroup "my-api" "prod") {
        retention CloudWatchLogsHelpers.RetentionPeriods.audit
        constructId "ApiGatewayLogs"
    }
}

Metric Filters

Extract custom metrics from log data for monitoring and alerting.

stack "MetricFiltersStack" {
    let appLogGroup =
        logGroup "/aws/application/logs" { retention Amazon.CDK.AWS.Logs.RetentionDays.TWO_WEEKS }

    // Count error occurrences
    metricFilter "ErrorCount" {
        logGroup appLogGroup
        filterPattern (FilterPatterns.errorLogs ())
        metricName "ErrorCount"
        metricNamespace "MyApp"
        metricValue "1"
    }

    // Count HTTP 5xx errors
    metricFilter "ServerErrors" {
        logGroup appLogGroup
        filterPattern (FilterPatterns.http5xxErrors ())
        metricName "ServerErrorCount"
        metricNamespace "MyApp"
    }

    // Extract custom metric value from logs
    metricFilter "ResponseTime" {
        logGroup appLogGroup
        filterPattern (FilterPatterns.matchText "response_time")
        metricName "ResponseTime"
        metricNamespace "MyApp"
        metricValue "$responseTime"
        unit Unit.MILLISECONDS
    }
}

Log Insights Widget

Query and visualize CloudWatch Logs.

stack "LogsDashboard" {
    let logWidget =
        DashboardWidgets.logQueryWidget
            "Error Logs"
            [ "/aws/lambda/my-function" ]
            "fields @timestamp, @message | filter @message like /ERROR/ | sort @timestamp desc | limit 20"
            (Some 12)
            (Some 6)

    dashboard "LogsDashboard" {
        dashboardName "application-logs"
        widget logWidget
    }
}

Single Value Widgets

Display current metric values prominently.

stack "SingleValueDashboard" {
    let activeUsers =
        Metric(MetricProps(Namespace = "MyApp", MetricName = "ActiveUsers"))

    let currentRequests =
        Metric(MetricProps(Namespace = "MyApp", MetricName = "CurrentRequests"))

    dashboard "CurrentStatsDashboard" {
        dashboardName "current-stats"

        widgetRow
            [ DashboardWidgets.singleValueWidget "Active Users" [ activeUsers ]
              DashboardWidgets.singleValueWidget "Current Requests" [ currentRequests ] ]
    }
}

CloudWatch Helpers

FsCDK provides helper functions for common CloudWatch patterns.

// Common retention periods
let devRetention = CloudWatchLogsHelpers.RetentionPeriods.dev // 3 days
let prodRetention = CloudWatchLogsHelpers.RetentionPeriods.production // 30 days
let auditRetention = CloudWatchLogsHelpers.RetentionPeriods.audit // 5 years

// Standard log group naming
let lambdaLogGroup = CloudWatchLogsHelpers.lambdaLogGroup "my-function"
let ecsLogGroup = CloudWatchLogsHelpers.ecsLogGroup "my-service" "production"
let apiGatewayLogGroup = CloudWatchLogsHelpers.apiGatewayLogGroup "my-api" "prod"

// Common filter patterns
let allEvents = FilterPatterns.allEvents ()
let errorLogs = FilterPatterns.errorLogs ()
let warningLogs = FilterPatterns.warningLogs ()
let http5xxErrors = FilterPatterns.http5xxErrors ()
let http4xxErrors = FilterPatterns.http4xxErrors ()

Best Practices

Design

Operational Excellence

Cost Optimization

Security

namespace FsCDK
namespace Amazon
namespace Amazon.CDK
namespace Amazon.CDK.AWS
namespace Amazon.CDK.AWS.CloudWatch
namespace Amazon.CDK.AWS.Lambda
namespace Amazon.CDK.AWS.DynamoDB
val stack: name: string -> StackBuilder
<summary>Creates an AWS CDK Stack construct.</summary>
<param name="name">The name of the stack.</param>
<code lang="fsharp"> stack "MyStack" { lambda myFunction bucket myBucket } </code>
val myFunction: FunctionSpec
val lambda: name: string -> FunctionBuilder
<summary>Creates a Lambda function configuration.</summary>
<param name="name">The function name.</param>
<code lang="fsharp"> lambda "MyFunction" { handler "index.handler" runtime Runtime.NODEJS_18_X code "./lambda" timeout 30.0 } </code>
custom operation: runtime (Runtime) Calls FunctionBuilder.Runtime
<summary>Sets the runtime for the Lambda function.</summary>
<param name="runtime">The Lambda runtime.</param>
<code lang="fsharp"> lambda "MyFunction" { runtime Runtime.NODEJS_18_X } </code>
Multiple items
type Runtime = inherit DeputyBase new: name: string * ?family: Nullable<RuntimeFamily> * ?props: ILambdaRuntimeProps -> unit member RuntimeEquals: other: Runtime -> bool member ToString: unit -> string member BundlingImage: DockerImage member Family: Nullable<RuntimeFamily> member IsVariable: bool member Name: string member SupportsCodeGuruProfiling: bool member SupportsInlineCode: bool ...

--------------------
Runtime(name: string, ?family: System.Nullable<RuntimeFamily>, ?props: ILambdaRuntimeProps) : Runtime
property Runtime.DOTNET_8: Runtime with get
custom operation: handler (string) Calls FunctionBuilder.Handler
<summary>Sets the handler for the Lambda function.</summary>
<param name="handler">The handler name (e.g., "index.handler").</param>
<code lang="fsharp"> lambda "MyFunction" { handler "index.handler" } </code>
custom operation: code (Code) Calls FunctionBuilder.Code
<summary>Sets the code source from a Code object.</summary>
<param name="path">The Code object.</param>
<code lang="fsharp"> lambda "MyFunction" { code (Code.FromBucket myBucket "lambda.zip") } </code>
val invocationsMetric: Metric
FunctionSpec.Function: IFunction option
property Option.Value: IFunction with get
IFunction.MetricInvocations(?props: IMetricOptions) : Metric
val errorsMetric: Metric
IFunction.MetricErrors(?props: IMetricOptions) : Metric
val dashboard: name: string -> DashboardBuilder
<summary>Creates a CloudWatch Dashboard with AWS monitoring best practices.</summary>
<param name="name">The dashboard name.</param>
<code lang="fsharp"> dashboard "MyAppDashboard" { dashboardName "my-app-production" defaultInterval (Duration.Minutes(5.0)) widgetRow [ DashboardWidgets.metricWidget "API Requests" [requestMetric] DashboardWidgets.metricWidget "Error Rate" [errorMetric] ] widgetRow [ DashboardWidgets.singleValueWidget "Current Users" [userMetric] ] } </code>
custom operation: dashboardName (string) Calls DashboardBuilder.DashboardName
<summary>Sets the dashboard name as it appears in CloudWatch.</summary>
custom operation: widgetRow (IWidget list) Calls DashboardBuilder.WidgetRow
<summary>Adds a row of widgets to the dashboard.</summary>
module DashboardWidgets from FsCDK
val metricWidget: title: string -> metrics: IMetric list -> GraphWidget
<summary> Creates a metric widget (line graph) </summary>
val apiFunction: FunctionSpec
val dataTable: TableSpec
val table: name: string -> TableBuilder
<summary>Creates a DynamoDB table configuration.</summary>
<param name="name">The table name.</param>
<code lang="fsharp"> table "MyTable" { partitionKey "id" AttributeType.STRING billingMode BillingMode.PAY_PER_REQUEST } </code>
custom operation: partitionKey (string) (AttributeType) Calls TableBuilder.PartitionKey
<summary>Sets the partition key for the table.</summary>
<param name="name">The attribute name for the partition key.</param>
<param name="attrType">The attribute type (STRING, NUMBER, or BINARY).</param>
<code lang="fsharp"> table "MyTable" { partitionKey "id" AttributeType.STRING } </code>
[<Struct>] type AttributeType = | BINARY = 0 | NUMBER = 1 | STRING = 2
field AttributeType.STRING: AttributeType = 2
custom operation: billingMode (BillingMode) Calls TableBuilder.BillingMode
<summary>Sets the billing mode for the table.</summary>
<param name="mode">The billing mode (PAY_PER_REQUEST or PROVISIONED).</param>
<code lang="fsharp"> table "MyTable" { billingMode BillingMode.PAY_PER_REQUEST } </code>
[<Struct>] type BillingMode = | PAY_PER_REQUEST = 0 | PROVISIONED = 1
field BillingMode.PAY_PER_REQUEST: BillingMode = 0
val apiMetric: Metric
val apiDuration: Metric
IFunction.MetricDuration(?props: IMetricOptions) : Metric
val tableReads: Metric
TableSpec.Table: ITable option
property Option.Value: ITable with get
ITable.MetricConsumedReadCapacityUnits(?props: IMetricOptions) : Metric
val tableWrites: Metric
ITable.MetricConsumedWriteCapacityUnits(?props: IMetricOptions) : Metric
custom operation: defaultInterval (Duration) Calls DashboardBuilder.DefaultInterval
<summary>Sets the default time range interval for metrics.</summary>
type Duration = inherit DeputyBase member FormatTokenToNumber: unit -> string member IsUnresolved: unit -> bool member Minus: rhs: Duration -> Duration member Plus: rhs: Duration -> Duration member ToDays: ?opts: ITimeConversionOptions -> float member ToHours: ?opts: ITimeConversionOptions -> float member ToHumanString: unit -> string member ToIsoString: unit -> string member ToMilliseconds: ?opts: ITimeConversionOptions -> float ...
Duration.Minutes(amount: float) : Duration
val webFunction: FunctionSpec
val errorMetric: Metric
val errorAlarm: CloudWatchAlarmSpec
val cloudwatchAlarm: name: string -> CloudWatchAlarmBuilder
custom operation: description (string) Calls CloudWatchAlarmBuilder.Description
<summary>Sets the alarm description.</summary>
custom operation: metric (IMetric) Calls CloudWatchAlarmBuilder.Metric
<summary>Sets the IMetric. If you use this, don't define metricName or metricNamespace.</summary>
custom operation: dimensions ((string * string) list) Calls CloudWatchAlarmBuilder.Dimensions
<summary>Sets the metric dimensions for filtering (e.g., FunctionName, DBInstanceIdentifier).</summary>
custom operation: statistic (string) Calls CloudWatchAlarmBuilder.Statistic
<summary>Sets the statistic (Average, Sum, Minimum, Maximum, SampleCount).</summary>
custom operation: threshold (float) Calls CloudWatchAlarmBuilder.Threshold
<summary>Sets the alarm threshold value.</summary>
custom operation: evaluationPeriods (int) Calls CloudWatchAlarmBuilder.EvaluationPeriods
<summary>Sets the number of periods to evaluate.</summary>
custom operation: period (Duration) Calls CloudWatchAlarmBuilder.Period
<summary>Sets the evaluation period.</summary>
custom operation: widget (IWidget) Calls DashboardBuilder.Widget
<summary>Adds a single widget to the dashboard.</summary>
val alarmWidgetSpec: alarmSpec: CloudWatchAlarmSpec -> AlarmWidget
<summary> Creates an alarm widget </summary>
val textWidget: markdown: string -> TextWidget
<summary> Creates a text widget with markdown content </summary>
Duration.Days(amount: float) : Duration
custom operation: startTime (string) Calls DashboardBuilder.StartTime
<summary>Sets the start time for the dashboard.</summary>
custom operation: endTime (string) Calls DashboardBuilder.EndTime
<summary>Sets the end time for the dashboard.</summary>
val logGroup: name: string -> CloudWatchLogGroupBuilder
<summary> Creates a new CloudWatch Log Group builder with sensible defaults. Example: logGroup "/aws/ecs/my-service" { retention RetentionDays.ONE_MONTH } </summary>
custom operation: retention (AWS.Logs.RetentionDays) Calls CloudWatchLogGroupBuilder.Retention
namespace Amazon.CDK.AWS.Logs
[<Struct>] type RetentionDays = | ONE_DAY = 0 | THREE_DAYS = 1 | FIVE_DAYS = 2 | ONE_WEEK = 3 | TWO_WEEKS = 4 | ONE_MONTH = 5 | TWO_MONTHS = 6 | THREE_MONTHS = 7 | FOUR_MONTHS = 8 | FIVE_MONTHS = 9 ...
field AWS.Logs.RetentionDays.ONE_MONTH: AWS.Logs.RetentionDays = 5
custom operation: removalPolicy (RemovalPolicy) Calls CloudWatchLogGroupBuilder.RemovalPolicy
[<Struct>] type RemovalPolicy = | DESTROY = 0 | RETAIN = 1 | SNAPSHOT = 2 | RETAIN_ON_UPDATE_OR_DELETE = 3
field RemovalPolicy.DESTROY: RemovalPolicy = 0
module CloudWatchLogsHelpers from FsCDK
<summary> Helper functions for CloudWatch Logs </summary>
val ecsLogGroup: serviceName: string -> environment: string -> string
<summary> Creates a log group name for ECS services </summary>
module RetentionPeriods from FsCDK.CloudWatchLogsHelpers
<summary> Common retention periods for different use cases </summary>
val production: AWS.Logs.RetentionDays
<summary> Production: 30 days </summary>
val apiGatewayLogGroup: apiName: string -> stage: string -> string
<summary> Creates a log group name for API Gateway </summary>
val audit: AWS.Logs.RetentionDays
<summary> Audit logs: 5 years </summary>
custom operation: constructId (string) Calls CloudWatchLogGroupBuilder.ConstructId
val appLogGroup: CloudWatchLogGroupResource
field AWS.Logs.RetentionDays.TWO_WEEKS: AWS.Logs.RetentionDays = 4
val metricFilter: name: string -> CloudWatchMetricFilterBuilder
<summary> Creates a CloudWatch Metric Filter to extract metrics from logs. Example: metricFilter "ErrorCount" { logGroup myLogGroup filterPattern (FilterPatterns.errorLogs()) metricName "ErrorCount" metricNamespace "MyApp" } </summary>
custom operation: logGroup (CloudWatchLogGroupResource) Calls CloudWatchMetricFilterBuilder.LogGroup
custom operation: filterPattern (AWS.Logs.IFilterPattern) Calls CloudWatchMetricFilterBuilder.FilterPattern
module FilterPatterns from FsCDK
<summary> Helper module for common filter patterns </summary>
val errorLogs: unit -> AWS.Logs.IFilterPattern
<summary> Match log events by severity level </summary>
custom operation: metricName (string) Calls CloudWatchMetricFilterBuilder.MetricName
custom operation: metricNamespace (string) Calls CloudWatchMetricFilterBuilder.MetricNamespace
custom operation: metricValue (string) Calls CloudWatchMetricFilterBuilder.MetricValue
val http5xxErrors: unit -> AWS.Logs.IFilterPattern
<summary> Match HTTP 5xx errors </summary>
val matchText: text: string -> AWS.Logs.IFilterPattern
<summary> Match log events containing specific text </summary>
Multiple items
custom operation: unit (Unit) Calls CloudWatchMetricFilterBuilder.Unit

--------------------
type unit = Unit
[<Struct>] type Unit = | SECONDS = 0 | MICROSECONDS = 1 | MILLISECONDS = 2 | BYTES = 3 | KILOBYTES = 4 | MEGABYTES = 5 | GIGABYTES = 6 | TERABYTES = 7 | BITS = 8 | KILOBITS = 9 ...
field Unit.MILLISECONDS: Unit = 2
val logWidget: LogQueryWidget
val logQueryWidget: title: string -> logGroupNames: string list -> queryString: string -> width: int option -> height: int option -> LogQueryWidget
<summary> Creates a log query widget </summary>
union case Option.Some: Value: 'T -> Option<'T>
val activeUsers: Metric
Multiple items
type Metric = inherit DeputyBase interface IMetric new: props: IMetricProps -> unit member AttachTo: scope: IConstruct -> Metric member CreateAlarm: scope: Construct * id: string * props: ICreateAlarmOptions -> Alarm member ToMetricConfig: unit -> IMetricConfig member ToString: unit -> string member With: props: IMetricOptions -> Metric static member AnomalyDetectionFor: props: IAnomalyDetectionMetricOptions -> MathExpression static member GrantPutMetricData: grantee: IGrantable -> Grant ...

--------------------
Metric(props: IMetricProps) : Metric
Multiple items
type MetricProps = interface IMetricProps interface ICommonMetricOptions new: unit -> unit member Account: string member Color: string member DimensionsMap: IDictionary<string,string> member Id: string member Label: string member MetricName: string member Namespace: string ...

--------------------
MetricProps() : MetricProps
val currentRequests: Metric
val singleValueWidget: title: string -> metrics: IMetric list -> SingleValueWidget
<summary> Creates a single value widget (shows latest metric value) </summary>
val devRetention: AWS.Logs.RetentionDays
val dev: AWS.Logs.RetentionDays
<summary> Development/testing: 3 days </summary>
val prodRetention: AWS.Logs.RetentionDays
val auditRetention: AWS.Logs.RetentionDays
val lambdaLogGroup: string
val lambdaLogGroup: functionName: string -> string
<summary> Creates a log group name for Lambda functions </summary>
val ecsLogGroup: string
val apiGatewayLogGroup: string
val allEvents: AWS.Logs.IFilterPattern
val allEvents: unit -> AWS.Logs.IFilterPattern
<summary> Match all log events </summary>
val errorLogs: AWS.Logs.IFilterPattern
val warningLogs: AWS.Logs.IFilterPattern
val warningLogs: unit -> AWS.Logs.IFilterPattern
val http5xxErrors: AWS.Logs.IFilterPattern
val http4xxErrors: AWS.Logs.IFilterPattern
val http4xxErrors: unit -> AWS.Logs.IFilterPattern
<summary> Match HTTP 4xx errors </summary>

Type something to start searching.