Search code examples
javaspring-bootamazon-cloudwatchaws-cloudwatch-log-insightsaws-java-sdk-2.x

Specify credentials to AWS Cloudwatch logs Client


Hi I am trying to create a exception logs of my java application code in AWS cloudwatch for that I have used CloudWatchLogsClient to put my events to it but i am getting a below Error

DEBUG software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain - Unable to load credentials from SystemPropertyCredentialsProvider(): Unable to load credentials from system settings. Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) or system property (aws.accessKeyId).
software.amazon.awssdk.core.exception.SdkClientException: Unable to load credentials from system settings. Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) or system property (aws.accessKeyId).
    at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:97)
    at software.amazon.awssdk.auth.credentials.internal.SystemSettingsCredentialsProvider.resolveCredentials(SystemSettingsCredentialsProvider.java:58)
    at software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain.resolveCredentials(AwsCredentialsProviderChain.java:91)
    at software.amazon.awssdk.auth.credentials.internal.LazyAwsCredentialsProvider.resolveCredentials(LazyAwsCredentialsProvider.java:52)
    at software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider.resolveCredentials(DefaultCredentialsProvider.java:100)
    at software.amazon.awssdk.awscore.client.handler.AwsClientHandlerUtils.createExecutionContext(AwsClientHandlerUtils.java:71)
    at software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler.createExecutionContext(AwsSyncClientHandler.java:68)
    at software.amazon.awssdk.core.client.handler.BaseSyncClientHandler.execute(BaseSyncClientHandler.java:68)
    at software.amazon.awssdk.core.client.handler.SdkSyncClientHandler.execute(SdkSyncClientHandler.java:44)
    at software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler.execute(AwsSyncClientHandler.java:55)
    at software.amazon.awssdk.services.cloudwatchlogs.DefaultCloudWatchLogsClient.describeLogStreams(DefaultCloudWatchLogsClient.java:1168)
    at com.WorkingwithS3.WorkingwithS3.PutLogEvents.main(PutLogEvents.java:58)

Here is my code sample

package com.WorkingwithS3.WorkingwithS3;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient;
import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClientBuilder;
import software.amazon.awssdk.services.cloudwatchlogs.model.DescribeLogStreamsRequest;
import software.amazon.awssdk.services.cloudwatchlogs.model.DescribeLogStreamsResponse;
import software.amazon.awssdk.services.cloudwatchlogs.model.InputLogEvent;
import software.amazon.awssdk.services.cloudwatchlogs.model.PutLogEventsRequest;

import java.util.Arrays;
// snippet-end:[cloudwatch.java2.put_log_events.import]

/**
 * Puts a sample CloudWatch log event
 */
public class PutLogEvents {
    public static void main(String[] args) {
        BasicAWSCredentials creds = new BasicAWSCredentials("xxxx",
                "xxxxx");

//        BasicAWSCredentials creds = new BasicAWSCredentials("xxxxxxxx",
//                "xxxx");

        String regionId = "xxx";
        String logGroupName = "xxxx";
        String streamName = "xxxxx";


        // snippet-start:[cloudwatch.java2.put_log_events.main]

        CloudWatchLogsClient logsClient = CloudWatchLogsClient.builder().region(Region.of(regionId)).build();

        // A sequence token is required to put a log event in an existing stream.
        // Look up the stream to find its sequence token.

        // First describe all streams in the log group.
        DescribeLogStreamsRequest logStreamRequest = DescribeLogStreamsRequest.builder()
                .logGroupName(logGroupName)
                .logStreamNamePrefix(streamName)
                .build();
        DescribeLogStreamsResponse describeLogStreamsResponse = logsClient.describeLogStreams(logStreamRequest);

        // Assume that a single stream is returned since a specific stream name was specified in the previous request.
        String sequenceToken = describeLogStreamsResponse.logStreams().get(0).uploadSequenceToken();

        // Build an input log message to put to CloudWatch.
        InputLogEvent inputLogEvent = InputLogEvent.builder()
                .message("{ \"key1\": \"value1\", \"key2\": \"value2\" }")
                .timestamp(System.currentTimeMillis())
                .build();

        // Specify the request parameters.
        PutLogEventsRequest putLogEventsRequest = PutLogEventsRequest.builder()
                .logEvents(Arrays.asList(inputLogEvent))
                .logGroupName(logGroupName)
                .logStreamName(streamName)
                // Sequence token is required so that the log can be written to the
                // latest location in the stream.
                .sequenceToken(sequenceToken)
                .build();
        logsClient.putLogEvents(putLogEventsRequest);
        // snippet-end:[cloudwatch.java2.put_log_events.main]

        System.out.println("Successfully put CloudWatch log event");
    }
}

Could anyone please guide how to specify the credentials for CloudWatchLogsClient? Thanks in advance


Solution

  • From the trace we can see the sdk client in this instance CloudWatchLogsClient.builder() is failing to find the credentials and hence failing to build.
    The client will look for the credentials in the following defaults locations

    For a lot of reasons its good to set up your code to read the credentials from environment variables.

    This follows for many reasons.

    AWS encourage the use of environment variables for credentials.

    The increasing need to run your application in some kind of a container cluster like Kubernetes for example.

    Often in a containerized environment access to the file system can be problematic.

    In many container tools like docker-compose its trivial to pass environment variables to the container.

    In the link defaults locations it specifies the options precisely how to supply the credentials for the CloudWatchLogsClient.builder() operation and for the reasons above suggest you adopt the environment variables solution and you can test that they are set correctly by using `

           Map<String, String> mapOfEnvironmentVariables = System.getenv();
    

    to retrieve them.

    Update 20-12-2023
    changed AWS docs link for 'defaults locations' look for section 2 under subheading 'Credential settings retrieval order'