androidamazon-web-servicesamazon-cloudwatchamazon-cloudwatchlogs

Log to Cloudwatch directly from Android


I've been trying to integrate Cloud Watch Logging in my Android app that Logs directly without using any other API calls. I went over the AWS Amplify but there is no Logging for Android

It's easy to just create an API using API Gateway and connect it to Lambda that logs the sent data but I'm not looking into that kind of solution.


Solution

  • I was able to implement the solution using AWS SDK in the app with Amplify providing temporary credentials.

    UPDATE: This is how I implemented it in Android

    Client Provided using Hilt:

        @ViewModelScoped
        @Provides
        fun provideAmazonCloudWatchLogsClient(@ApplicationContext context: Context): AmazonCloudWatchLogsClient {
    
            val credentialsProvider = CognitoCachingCredentialsProvider(
                context,
                "<Identity-Pool-id-from-cognito>",
                Regions.US_WEST_2
            )
    
            val region: Region = Region.getRegion(Regions.US_WEST_2)
            val client =  AmazonCloudWatchLogsClient(credentialsProvider)
            client.setRegion(region)
            return client
        }
    

    Logging from a Usecase:

    fun cloudWatchLog(eventType: EventType, appLog: AppLog, level: LogLevel = LogLevel.INFO) =
            flow {
                try {
                    emit(Resource.Loading())
    
                    val isUserSignedIn = authUser.getUserId().let { id -> !(id.isEmpty() || id == "null") }
    
                    val logEvent = PutLogEventsRequest().apply {
                        logGroupName = LogStream.ANDROID_LOG.stream
                        logStreamName = authUser.getUserId()
                            .let { id -> if (id.isEmpty() || id == "null") preferences.getLogsUserId()
                                         else id
                            }
                    }
    
                    val currentTime = System.currentTimeMillis()
                    val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                    val currentDate = simpleDateFormat.format(Date(currentTime))
    
                    val event = InputLogEvent().apply {
                        timestamp = currentTime
                        message =
                            "[$currentDate] ${level.level} [${if(isUserSignedIn) "LOGGED ✅" else "TEMP ❌"}] ${eventType.type} " +
                                    when (eventType) {
                                        EventType.CODE -> appLog.toCodeString()
                                        EventType.CUSTOM, EventType.QR  -> appLog.toMessageString()
                                        else -> appLog
                                    }
                    }
    
                    val list = ArrayList<InputLogEvent>().apply { add(event) }
    
                    logEvent.setLogEvents(list)
    
                    if (internetUseCase()) {
    
                        val describeRequest = DescribeLogStreamsRequest()
                            .withLogGroupName(logEvent.logGroupName)
                            .withLogStreamNamePrefix(logEvent.logStreamName)
    
                        describeRequest.limit = 1
    
                        val describeResult = logs.describeLogStreams(describeRequest);
    
                        if (describeResult.logStreams.isEmpty()) {
    
                            val createRequest = CreateLogStreamRequest()
                                .withLogGroupName(logEvent.logGroupName)
                                .withLogStreamName(logEvent.logStreamName);
    
                            LogAndToast.log("AppLogUploadUseCase", "Log Stream does not exist")
    
                            logs.createLogStream(createRequest)
                            logs.putLogEvents(logEvent)
                        } else {
                            LogAndToast.log("AppLogUploadUseCase", "Log Stream does exists")
                            logs.putLogEvents(logEvent)
                        }
                        emit(Resource.Success(data = "Success!"))
                        return@flow
                    } else {
                        emit(Resource.Error(message = "Failed App Logs - No Internet"))
                    }
    
                } catch (e: Exception) {
                    LogAndToast.log("AppLogUploadUseCase", "Error = ${e.message}")
                    emit(Resource.Error(message = e.message ?: "An unknown error"))
                }
            }