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
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:
| Option | Default | Description |
|---|---|---|
logGroupName | (required) | The CloudWatch log group logs are sent to. |
localStoreMaxSizeInMB | 5 | Maximum size of the local log cache in MB. |
flushStrategy | .interval() (60s) | Automatic flush interval. Use .none for manual-only flushing. |
loggingConstraints | LoggingConstraints() | Per-namespace and per-user log level rules. |
configureClient | nil | Closure 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 resumeSet 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 callsError handling
Operations surface errors through a sealed error hierarchy:
| Error type | Description |
|---|---|
CloudWatchError.storage | Local storage error (file I/O, log rotation). |
CloudWatchError.service | A CloudWatch Logs API call failed. |
CloudWatchError.configuration | The client was misconfigured. |
CloudWatchError.unknown | Unexpected 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:
| Limit | Value |
|---|---|
| Max batch size | 1 MB |
| Max events per batch | 10,000 |
| Max single event size | 256 KB |