diff --git a/examples/src/main/java/com/salesforce/multicloudj/sts/Main.java b/examples/src/main/java/com/salesforce/multicloudj/sts/Main.java
index 41890c767..87978a72f 100644
--- a/examples/src/main/java/com/salesforce/multicloudj/sts/Main.java
+++ b/examples/src/main/java/com/salesforce/multicloudj/sts/Main.java
@@ -23,14 +23,14 @@
public class Main {
- static String provider = "gcp";
+ static String provider = "aws";
public static void main(String[] args) {
assumeRole();
- assumeRoleWebIdentityCredentialsOverrider();
- getCallerIdentity();
- nativeAuthSignerUtilityWithStsCredentials();
- nativeAuthSignerUtilityWithDefaultCredentials();
+ //assumeRoleWebIdentityCredentialsOverrider();
+ //getCallerIdentity();
+ //nativeAuthSignerUtilityWithStsCredentials();
+ //nativeAuthSignerUtilityWithDefaultCredentials();
}
public static void assumeRole() {
@@ -55,9 +55,9 @@ public static void assumeRole() {
.build();
AssumedRoleRequest request = AssumedRoleRequest.newBuilder()
- .withRole("chameleon@substrate-sdk-gcp-poc1.iam.gserviceaccount.com")
+ .withRole("arn:aws:iam::654654370895:role/chameleon-multi--f4msu63ppffhs")
.withSessionName("my-session")
- .withCredentialScope(credentialScope)
+ //.withCredentialScope(credentialScope)
.build();
StsCredentials stsCredentials = client.getAssumeRoleCredentials(request);
@@ -76,7 +76,7 @@ public static void assumeRoleWebIdentityCredentialsOverrider() {
.withWebIdentityTokenSupplier(tokenSupplier)
.build();
BucketClient bucketClient = BucketClient.builder(provider)
- .withRegion("us-west-2").withBucket("chameleon-jclouds")
+ .withRegion("us-west-2").withBucket("chameleon-jcloud")
.withCredentialsOverrider(overrider)
.build();
ListBlobsPageResponse r=bucketClient.listPage(ListBlobsPageRequest.builder().withMaxResults(1).build());
diff --git a/multicloudj-common-aws/pom.xml b/multicloudj-common-aws/pom.xml
index d25753e69..d8be97467 100644
--- a/multicloudj-common-aws/pom.xml
+++ b/multicloudj-common-aws/pom.xml
@@ -73,6 +73,17 @@
+
+ software.amazon.awssdk
+ cloudwatch
+ 2.35.0
+
+
+ *
+ *
+
+
+
software.amazon.awssdk
aws-core
diff --git a/multicloudj-common-aws/src/main/java/com/salesforce/multicloudj/common/aws/StsDiagnosticsInterceptor.java b/multicloudj-common-aws/src/main/java/com/salesforce/multicloudj/common/aws/StsDiagnosticsInterceptor.java
new file mode 100644
index 000000000..3399865cd
--- /dev/null
+++ b/multicloudj-common-aws/src/main/java/com/salesforce/multicloudj/common/aws/StsDiagnosticsInterceptor.java
@@ -0,0 +1,85 @@
+package com.salesforce.multicloudj.common.aws;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import software.amazon.awssdk.core.SdkRequest;
+import software.amazon.awssdk.core.interceptor.Context;
+import software.amazon.awssdk.core.interceptor.ExecutionAttribute;
+import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
+import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
+import software.amazon.awssdk.services.cloudwatch.CloudWatchClient;
+import software.amazon.awssdk.services.cloudwatch.model.MetricDatum;
+import software.amazon.awssdk.services.cloudwatch.model.PutMetricDataRequest;
+import software.amazon.awssdk.services.cloudwatch.model.StandardUnit;
+import software.amazon.awssdk.services.sts.model.AssumeRoleRequest;
+
+import java.time.Duration;
+import java.time.Instant;
+
+public class StsDiagnosticsInterceptor implements ExecutionInterceptor {
+ private static final Logger log = LoggerFactory.getLogger(StsDiagnosticsInterceptor.class);
+ private static final ExecutionAttribute START_TIME = new ExecutionAttribute<>("StartTime");
+ private final CloudWatchClient cwClient;
+ private final String namespace;
+
+ public StsDiagnosticsInterceptor(CloudWatchClient cwClient, String namespace) {
+ this.cwClient = cwClient;
+ this.namespace = namespace;
+ }
+
+ @Override
+ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes executionAttributes) {
+ executionAttributes.putAttribute(START_TIME, Instant.now());
+ }
+
+ @Override
+ public void afterExecution(Context.AfterExecution context, ExecutionAttributes executionAttributes) {
+ // Correctly passing the SdkRequest
+ processCompletion(context.request(), executionAttributes, "SUCCESS", null);
+ }
+
+ @Override
+ public void onExecutionFailure(Context.FailedExecution context, ExecutionAttributes executionAttributes) {
+ // Correctly passing the SdkRequest
+ processCompletion(context.request(), executionAttributes, "FAILURE", context.exception().getMessage());
+ }
+
+ private void processCompletion(SdkRequest request, ExecutionAttributes executionAttributes, String status, String errorMsg) {
+ String action = request.getClass().getSimpleName();
+ Instant start = executionAttributes.getAttribute(START_TIME);
+ long duration = (start != null) ? Duration.between(start, Instant.now()).toMillis() : 0;
+
+ // FIXED: The type-safe way to check and cast
+ String targetRole = "N/A";
+ if (request instanceof AssumeRoleRequest) {
+ // Because AssumeRoleRequest implements SdkRequest through the STS hierarchy,
+ // this cast is valid and safe within this block.
+ targetRole = ((AssumeRoleRequest) request).roleArn();
+ }
+
+ // 1. STRUCTURED LOGGING
+ log.info("STS_COMPLETE | Action: {} | Status: {} | Duration: {}ms | Role: {} | Error: {}",
+ action, status, duration, targetRole, (errorMsg != null ? errorMsg : "None"));
+
+ // 2. METRIC PUBLISHING
+ publishToCloudWatch(action, status, (double) duration, targetRole);
+ }
+
+ private void publishToCloudWatch(String action, String status, Double duration, String roleArn) {
+ try {
+ // Use a background thread or EMF in high-volume prod to avoid blocking
+ MetricDatum count = MetricDatum.builder()
+ .metricName("StsCallCount")
+ .dimensions(d -> d.name("Action").value(action),
+ d -> d.name("Status").value(status))
+ .value(1.0).unit(StandardUnit.COUNT).build();
+
+ cwClient.putMetricData(PutMetricDataRequest.builder()
+ .namespace(this.namespace)
+ .metricData(count)
+ .build());
+ } catch (Exception e) {
+ log.warn("Telemetry Error: Could not publish STS metrics", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/sts/sts-aws/src/main/java/com/salesforce/multicloudj/sts/aws/AwsSts.java b/sts/sts-aws/src/main/java/com/salesforce/multicloudj/sts/aws/AwsSts.java
index f5d865a8d..313a3126b 100644
--- a/sts/sts-aws/src/main/java/com/salesforce/multicloudj/sts/aws/AwsSts.java
+++ b/sts/sts-aws/src/main/java/com/salesforce/multicloudj/sts/aws/AwsSts.java
@@ -4,6 +4,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.auto.service.AutoService;
import com.salesforce.multicloudj.common.aws.CommonErrorCodeMapping;
+import com.salesforce.multicloudj.common.aws.StsDiagnosticsInterceptor;
import com.salesforce.multicloudj.common.exceptions.InvalidArgumentException;
import com.salesforce.multicloudj.common.exceptions.SubstrateSdkException;
import com.salesforce.multicloudj.common.exceptions.UnAuthorizedException;
@@ -17,6 +18,8 @@
import com.salesforce.multicloudj.sts.model.StsCredentials;
import software.amazon.awssdk.awscore.exception.AwsServiceException;
import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.cloudwatch.CloudWatchClient;
+import software.amazon.awssdk.services.cloudwatch.CloudWatchClientBuilder;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.StsClientBuilder;
import software.amazon.awssdk.services.sts.model.AssumeRoleRequest;
@@ -45,9 +48,13 @@ public AwsSts(Builder builder) {
super(builder);
Region region = Region.of(builder.getRegion());
StsClientBuilder sb = StsClient.builder().region(region);
+ CloudWatchClientBuilder cwb = CloudWatchClient.builder().region(region);
if (builder.getEndpoint() != null) {
sb = sb.endpointOverride(builder.getEndpoint());
+ cwb = cwb.endpointOverride(builder.getEndpoint());
}
+ CloudWatchClient cw = cwb.build();
+ sb.overrideConfiguration(cfg -> cfg .addExecutionInterceptor(new StsDiagnosticsInterceptor(cw, this.getClass().getName())));
this.stsClient = sb.build();
}