Name:
interface
Value:
Extend your Amplify Gen 2 app with AWS Blocks — self-contained backend capabilities you compose into your existing backend.

CloudWatch client

AmplifyCloudWatchClient is a standalone client for sending application logs to Amazon CloudWatch Logs. It provides:

  • Local persistence for offline support
  • Automatic batching and interval-based flushing (default: every 60 seconds)
  • Integration with Amplify logging as a log sink
  • Per-namespace and per-user log level constraints
  • Enable/disable toggle that silently drops new logs while preserving cached ones

This is an experimental API. These public APIs are subject to change and are not meant for production code.

You must opt in by importing the module with the AmplifyExperimental SPI:

@_spi(AmplifyExperimental) import AmplifyCloudWatchClient

This is a standalone client, separate from the Amplify Logging category plugin. It communicates directly with the CloudWatch Logs API.

Before using this client, ensure your backend is configured with the required log group and IAM permissions. See Set up CloudWatch Logs.

Getting started

Installation

Add AmplifyCloudWatchClient to your project using Swift Package Manager. In Xcode, go to File > Add Package Dependencies and enter the repository URL for the Amplify Swift library: https://github.com/aws-amplify/amplify-swift.

Initialize the client

The logGroupName is required. It must match a CloudWatch log group your app has permission to write to.

@_spi(AmplifyExperimental) import AmplifyCloudWatchClient
let cloudWatch = try AmplifyCloudWatchClient(
region: "us-east-1",
credentialsProvider: credentialsProvider,
options: .init(logGroupName: "/app/my-ios-app")
)

Configuration options

You can customize the client behavior through the options object:

OptionDefaultDescription
logGroupName(required)The CloudWatch log group logs are sent to.
localStoreMaxSizeInMB5Maximum size of the local log cache in MB.
flushStrategy.interval() (60s)Automatic flush interval. Use .none for manual-only flushing.
loggingConstraintsLoggingConstraints()Per-namespace and per-user log level rules.
configureClientnilClosure to customize the underlying CloudWatchLogsClientConfig.
let cloudWatch = try AmplifyCloudWatchClient(
region: "us-east-1",
credentialsProvider: credentialsProvider,
options: .init(
logGroupName: "/app/my-ios-app",
localStoreMaxSizeInMB: 10,
flushStrategy: .interval(30),
loggingConstraints: LoggingConstraints(defaultLogLevel: .verbose),
configureClient: { config in
// Customize the underlying CloudWatchLogsClientConfig
}
)
)

To disable automatic flushing:

options: .init(logGroupName: "/app/my-ios-app", flushStrategy: .none)

Usage

Register as a logging sink

The client implements AmplifyFoundation's LogSinkBehavior protocol. Register it once to capture every message logged through Amplify logging:

AmplifyLogging.addSink(cloudWatch)
let log = AmplifyLogging.logger(for: "Storage")
log.info("Upload started")

Emit a message directly

You can also send a log message to the client without going through Amplify logging:

cloudWatch.emit(message: LogMessage(logLevel: .error, namespace: "MyNamespace", message: "Something went wrong"))

Messages emitted while the client is disabled are silently dropped.

Flush logs

The client automatically flushes cached logs at the configured interval (default: 60 seconds). You can also trigger a manual flush:

try await cloudWatch.flushLogs()

Manual flushes work even when the client is disabled, allowing you to drain cached logs without re-enabling collection.

Enable and disable

You can toggle log collection and automatic flushing at runtime. When disabled, new logs are silently dropped but already-cached logs remain in storage.

cloudWatch.disable()
// Logs are dropped, auto-flush paused
cloudWatch.enable()
// Collection and auto-flush resume

Set the user identifier

Associate cached and future logs with a user identifier, for example after sign-in.

Pass nil to clear it on sign-out.

cloudWatch.setUserIdentifier("user-123")

Set logging constraints

Update the log level rules at runtime, for example after fetching remote configuration:

cloudWatch.setLoggingConstraints(
LoggingConstraints(
defaultLogLevel: .warn,
namespaceLogLevel: ["Storage": .debug]
)
)

Observe events

The client surfaces write and flush failures through an events stream so you can react to persistent problems:

import Combine
let cancellable = cloudWatch.events.sink { event in
switch event {
case .writeLogFailure(let context, let error):
// handle write failure
break
case .flushLogFailure(let context, let error):
// handle flush failure
break
}
}

Advanced

Escape hatch

Access the underlying AWS SDK CloudWatchLogsClient for operations not covered by this client's API:

let sdkClient = cloudWatch.getCloudWatchLogsClient()
// Use sdkClient for direct CloudWatch Logs API calls

Error handling

Operations surface errors through a sealed error hierarchy:

Error typeDescription
CloudWatchError.storageLocal storage error (file I/O, log rotation).
CloudWatchError.serviceA CloudWatch Logs API call failed.
CloudWatchError.configurationThe client was misconfigured.
CloudWatchError.unknownUnexpected or uncategorized error.

The initializer and flushLogs() throw CloudWatchError:

do {
try await cloudWatch.flushLogs()
} catch let error as CloudWatchError {
switch error {
case .storage(let desc, _, _):
print("Storage error: \(desc)")
case .service(let desc, _, _):
print("Service error: \(desc)")
case .configuration(let desc, _, _):
print("Configuration error: \(desc)")
case .unknown(let desc, _, _):
print("Unknown error: \(desc)")
}
}

CloudWatch Logs service limits

The client batches logs to stay within the CloudWatch Logs PutLogEvents limits:

LimitValue
Max batch size1 MB
Max events per batch10,000
Max single event size256 KB