Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion aws-logging-cloudwatch/api/aws-logging-cloudwatch.api
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ public final class com/amplifyframework/logging/cloudwatch/AWSCloudWatchLoggingP
public fun <init> ()V
public fun <init> (Lcom/amplifyframework/logging/cloudwatch/models/AWSCloudWatchLoggingPluginConfiguration;)V
public fun <init> (Lcom/amplifyframework/logging/cloudwatch/models/AWSCloudWatchLoggingPluginConfiguration;Lcom/amplifyframework/logging/cloudwatch/RemoteLoggingConstraintProvider;)V
public synthetic fun <init> (Lcom/amplifyframework/logging/cloudwatch/models/AWSCloudWatchLoggingPluginConfiguration;Lcom/amplifyframework/logging/cloudwatch/RemoteLoggingConstraintProvider;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lcom/amplifyframework/logging/cloudwatch/models/AWSCloudWatchLoggingPluginConfiguration;Lcom/amplifyframework/logging/cloudwatch/RemoteLoggingConstraintProvider;Lcom/amplifyframework/logging/cloudwatch/models/LogStreamNameFormatter;)V
public synthetic fun <init> (Lcom/amplifyframework/logging/cloudwatch/models/AWSCloudWatchLoggingPluginConfiguration;Lcom/amplifyframework/logging/cloudwatch/RemoteLoggingConstraintProvider;Lcom/amplifyframework/logging/cloudwatch/models/LogStreamNameFormatter;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun configure (Lorg/json/JSONObject;Landroid/content/Context;)V
public fun disable ()V
public fun enable ()V
Expand Down Expand Up @@ -132,6 +133,23 @@ public final class com/amplifyframework/logging/cloudwatch/models/DefaultRemoteC
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class com/amplifyframework/logging/cloudwatch/models/LogStreamContext {
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lcom/amplifyframework/logging/cloudwatch/models/LogStreamContext;
public static synthetic fun copy$default (Lcom/amplifyframework/logging/cloudwatch/models/LogStreamContext;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/amplifyframework/logging/cloudwatch/models/LogStreamContext;
public fun equals (Ljava/lang/Object;)Z
public final fun getDeviceId ()Ljava/lang/String;
public final fun getUserId ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public abstract interface class com/amplifyframework/logging/cloudwatch/models/LogStreamNameFormatter {
public abstract fun format (Lcom/amplifyframework/logging/cloudwatch/models/LogStreamContext;)Ljava/lang/String;
}

public final class com/amplifyframework/logging/cloudwatch/models/LoggingConstraints {
public static final field Companion Lcom/amplifyframework/logging/cloudwatch/models/LoggingConstraints$Companion;
public fun <init> ()V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.amplifyframework.core.category.CategoryType
import com.amplifyframework.logging.Logger
import com.amplifyframework.logging.LoggingPlugin
import com.amplifyframework.logging.cloudwatch.models.AWSCloudWatchLoggingPluginConfiguration
import com.amplifyframework.logging.cloudwatch.models.LogStreamNameFormatter
import com.amplifyframework.logging.cloudwatch.worker.CloudwatchRouterWorker
import com.amplifyframework.logging.cloudwatch.worker.CloudwatchWorkerFactory
import com.amplifyframework.util.setHttpEngine
Expand All @@ -39,7 +40,8 @@ import org.json.JSONObject
*/
class AWSCloudWatchLoggingPlugin @JvmOverloads constructor(
private val awsCloudWatchLoggingPluginConfig: AWSCloudWatchLoggingPluginConfiguration? = null,
private val awsRemoteLoggingConstraintProvider: RemoteLoggingConstraintProvider? = null
private val awsRemoteLoggingConstraintProvider: RemoteLoggingConstraintProvider? = null,
private val logStreamNameFormatter: LogStreamNameFormatter? = null
) : LoggingPlugin<CloudWatchLogsClient>() {

private val loggingConstraintsResolver =
Expand Down Expand Up @@ -102,8 +104,13 @@ class AWSCloudWatchLoggingPlugin @JvmOverloads constructor(
)
}
}
val cloudWatchLogManager =
CloudWatchLogManager(context, awsLoggingConfig, cloudWatchLogsClient, loggingConstraintsResolver)
val cloudWatchLogManager = CloudWatchLogManager(
context,
awsLoggingConfig,
cloudWatchLogsClient,
loggingConstraintsResolver,
logStreamNameFormatter = logStreamNameFormatter
)
awsCloudWatchLoggingPluginImplementation.cloudWatchLogManager = cloudWatchLogManager
CloudwatchRouterWorker.workerFactories[CloudwatchRouterWorker.WORKER_FACTORY_KEY] = CloudwatchWorkerFactory(
cloudWatchLogManager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import com.amplifyframework.logging.cloudwatch.db.CloudWatchLoggingDatabase
import com.amplifyframework.logging.cloudwatch.db.LogEvent
import com.amplifyframework.logging.cloudwatch.models.AWSCloudWatchLoggingPluginConfiguration
import com.amplifyframework.logging.cloudwatch.models.CloudWatchLogEvent
import com.amplifyframework.logging.cloudwatch.models.LogStreamContext
import com.amplifyframework.logging.cloudwatch.models.LogStreamNameFormatter
import com.amplifyframework.logging.cloudwatch.worker.CloudwatchLogsSyncWorker
import com.amplifyframework.logging.cloudwatch.worker.CloudwatchRouterWorker
import java.text.SimpleDateFormat
Expand All @@ -57,7 +59,8 @@ internal class CloudWatchLogManager(
private val loggingConstraintsResolver: LoggingConstraintsResolver,
private val cloudWatchLoggingDatabase: CloudWatchLoggingDatabase = CloudWatchLoggingDatabase(context),
private val customCognitoCredentialsProvider: CustomCognitoCredentialsProvider = CustomCognitoCredentialsProvider(),
private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO
private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO,
private val logStreamNameFormatter: LogStreamNameFormatter? = null
) {
private val deviceIdKey = "unique_device_id"
private var stopSync = false
Expand Down Expand Up @@ -117,7 +120,14 @@ internal class CloudWatchLogManager(
if (queriedEvents.isEmpty()) break
while (queriedEvents.isNotEmpty()) {
val groupName = pluginConfiguration.logGroupName
val streamName = "$todayDate.${uniqueDeviceId()}.${userIdentityId ?: "guest"}"
val deviceId = uniqueDeviceId()
val context = LogStreamContext(deviceId = deviceId, userId = userIdentityId)

// Generate stream name: use custom formatter if provided, otherwise use default format
val streamName = logStreamNameFormatter?.format(context)
?: // Default format: MM-dd-yyyy.deviceId.userId
"$todayDate.$deviceId.${userIdentityId ?: "guest"}"

val nextBatch = getNextBatch(queriedEvents)
val inputLogEvents = nextBatch.first
inputLogEventsIdToBeDeleted = nextBatch.second
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.amplifyframework.logging.cloudwatch.models

/**
* Contains contextual information used when generating a CloudWatch log stream name.
* This data class is passed to [LogStreamNameFormatter.format] to allow custom log stream naming.
*
* Using a data class allows for future expansion of available context data without breaking
* existing implementations.
*
* @property deviceId A unique identifier for the device
* @property userId The user's identity ID, or null if not authenticated
*/
data class LogStreamContext(
val deviceId: String,
val userId: String?
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.amplifyframework.logging.cloudwatch.models

/**
* A functional interface for customizing CloudWatch log stream names.
*
* Implement this interface to provide custom log stream naming logic. The formatter receives
* a [LogStreamContext] containing relevant information about the device and user, which can
* be used to construct a meaningful log stream name.
*
* Example usage:
* ```kotlin
* val formatter = LogStreamNameFormatter { context ->
* "my-app-${context.deviceId}-${context.userId ?: "anonymous"}"
* }
* ```
*
* @see LogStreamContext
*/
fun interface LogStreamNameFormatter {
/**
* Generates a log stream name based on the provided context.
*
* @param context The [LogStreamContext] containing device and user information
* @return A string to be used as the CloudWatch log stream name
*/
fun format(context: LogStreamContext): String
}
Loading