@@ -902,7 +902,7 @@ index 1ef8abf5..328e63dd 100644
902902 }
903903 }
904904diff --git a/aws-xray/src/main/java/io/opentelemetry/contrib/awsxray/XrayRulesSampler.java b/aws-xray/src/main/java/io/opentelemetry/contrib/awsxray/XrayRulesSampler.java
905- index 75977dc0..b933a044 100644
905+ index 75977dc0..1edf4c10 100644
906906--- a/aws-xray/src/main/java/io/opentelemetry/contrib/awsxray/XrayRulesSampler.java
907907+++ b/aws-xray/src/main/java/io/opentelemetry/contrib/awsxray/XrayRulesSampler.java
908908@@ -5,42 +5,81 @@
@@ -1008,7 +1008,7 @@ index 75977dc0..b933a044 100644
10081008 }
10091009
10101010 private XrayRulesSampler(
1011- @@ -58,12 +106,42 @@ final class XrayRulesSampler implements Sampler {
1011+ @@ -58,12 +106,46 @@ final class XrayRulesSampler implements Sampler {
10121012 Resource resource,
10131013 Clock clock,
10141014 Sampler fallbackSampler,
@@ -1042,8 +1042,12 @@ index 75977dc0..b933a044 100644
10421042+ .expireAfterWrite(Duration.ofMinutes(1))
10431043+ .build();
10441044+
1045- + // Initialize anomaly capture rate limiter if error capture limit is configured
1046- + if (adaptiveSamplingConfig != null && adaptiveSamplingConfig.getErrorCaptureLimit() != null) {
1045+ + // Initialize anomaly capture rate limiter
1046+ + if (this.adaptiveSamplingConfig != null
1047+ + && this.adaptiveSamplingConfig.getErrorCaptureLimit() == null) {
1048+ + this.anomalyCaptureRateLimiter = new RateLimiter(1, 1, clock);
1049+ + } else if (adaptiveSamplingConfig != null
1050+ + && adaptiveSamplingConfig.getErrorCaptureLimit() != null) {
10471051+ int errorTracesPerSecond =
10481052+ adaptiveSamplingConfig.getErrorCaptureLimit().getErrorTracesPerSecond();
10491053+ this.anomalyCaptureRateLimiter =
@@ -1052,7 +1056,7 @@ index 75977dc0..b933a044 100644
10521056 }
10531057
10541058 @Override
1055- @@ -74,10 +152 ,36 @@ final class XrayRulesSampler implements Sampler {
1059+ @@ -74,10 +156 ,36 @@ final class XrayRulesSampler implements Sampler {
10561060 SpanKind spanKind,
10571061 Attributes attributes,
10581062 List<LinkData> parentLinks) {
@@ -1091,7 +1095,7 @@ index 75977dc0..b933a044 100644
10911095 }
10921096 }
10931097
1094- @@ -96,7 +200 ,164 @@ final class XrayRulesSampler implements Sampler {
1098+ @@ -96,7 +204 ,164 @@ final class XrayRulesSampler implements Sampler {
10951099 return "XrayRulesSampler{" + Arrays.toString(ruleAppliers) + "}";
10961100 }
10971101
@@ -1257,7 +1261,7 @@ index 75977dc0..b933a044 100644
12571261 return Arrays.stream(ruleAppliers)
12581262 .map(rule -> rule.snapshot(now))
12591263 .filter(Objects::nonNull)
1260- @@ -115,15 +376 ,16 @@ final class XrayRulesSampler implements Sampler {
1264+ @@ -115,15 +380 ,16 @@ final class XrayRulesSampler implements Sampler {
12611265 Map<String, SamplingTargetDocument> ruleTargets,
12621266 Set<String> requestedTargetRuleNames,
12631267 Date now) {
@@ -1276,7 +1280,7 @@ index 75977dc0..b933a044 100644
12761280 }
12771281 if (requestedTargetRuleNames.contains(rule.getRuleName())) {
12781282 // In practice X-Ray should return a target for any rule we requested but
1279- @@ -135,6 +397 ,90 @@ final class XrayRulesSampler implements Sampler {
1283+ @@ -135,6 +401 ,90 @@ final class XrayRulesSampler implements Sampler {
12801284 return rule;
12811285 })
12821286 .toArray(SamplingRuleApplier[]::new);
@@ -2005,7 +2009,7 @@ index 6bb6e82a..6d71711b 100644
20052009 return applier.shouldSample(
20062010 Context.current(),
20072011diff --git a/aws-xray/src/test/java/io/opentelemetry/contrib/awsxray/XrayRulesSamplerTest.java b/aws-xray/src/test/java/io/opentelemetry/contrib/awsxray/XrayRulesSamplerTest.java
2008- index 1ca8df34..2f25c8af 100644
2012+ index 1ca8df34..5830a083 100644
20092013--- a/aws-xray/src/test/java/io/opentelemetry/contrib/awsxray/XrayRulesSamplerTest.java
20102014+++ b/aws-xray/src/test/java/io/opentelemetry/contrib/awsxray/XrayRulesSamplerTest.java
20112015@@ -5,17 +5,28 @@
@@ -2225,7 +2229,7 @@ index 1ca8df34..2f25c8af 100644
22252229
22262230 // Minimum is batTarget, 5s from now
22272231 assertThat(sampler.nextTargetFetchTimeNanos())
2228- @@ -169,6 +251,763 @@ class XrayRulesSamplerTest {
2232+ @@ -169,6 +251,774 @@ class XrayRulesSamplerTest {
22292233 assertThat(sampler.snapshot(Date.from(now))).hasSize(4);
22302234 }
22312235
@@ -2897,13 +2901,10 @@ index 1ca8df34..2f25c8af 100644
28972901+ SamplingRateBoost.create(1, 300));
28982902+
28992903+ TestClock clock = TestClock.create();
2904+ + // Error span capture should default to 1/s
29002905+ AwsXrayAdaptiveSamplingConfig config =
29012906+ AwsXrayAdaptiveSamplingConfig.builder()
29022907+ .setVersion(1.0)
2903- + .setErrorCaptureLimit(
2904- + AwsXrayAdaptiveSamplingConfig.ErrorCaptureLimit.builder()
2905- + .setErrorTracesPerSecond(10)
2906- + .build())
29072908+ .setAnomalyConditions(
29082909+ Arrays.asList(
29092910+ AwsXrayAdaptiveSamplingConfig.AnomalyConditions.builder()
@@ -2930,42 +2931,56 @@ index 1ca8df34..2f25c8af 100644
29302931+
29312932+ SpanData spanDataMock = mock(SpanData.class);
29322933+ Attributes attributesMock = mock(Attributes.class);
2933- + when(spanDataMock.getTraceId()).thenReturn("TRACE_ID");
29342934+ when(spanDataMock.getAttributes()).thenReturn(attributesMock);
29352935+ when(attributesMock.get(HTTP_RESPONSE_STATUS_CODE)).thenReturn(500L);
29362936+
29372937+ LongAdder exportCounter = new LongAdder();
29382938+ Consumer<ReadableSpan> stubbedConsumer = x -> exportCounter.increment();
29392939+
29402940+ // Test matching operations
2941+ + when(spanDataMock.getTraceId()).thenReturn("TRACE_ID1");
29412942+ when(attributesMock.get(URL_PATH)).thenReturn("/api1/ext");
29422943+ when(attributesMock.get(HTTP_METHOD)).thenReturn("GET");
29432944+ sampler.adaptSampling(readableSpanMock, spanDataMock, stubbedConsumer);
2945+ +
2946+ + clock.advance(Duration.ofSeconds(5));
2947+ + when(spanDataMock.getTraceId()).thenReturn("TRACE_ID2");
29442948+ when(attributesMock.get(URL_PATH)).thenReturn("/api2");
29452949+ when(attributesMock.get(HTTP_METHOD)).thenReturn("GET");
29462950+ sampler.adaptSampling(readableSpanMock, spanDataMock, stubbedConsumer);
29472951+ assertThat(exportCounter.sumThenReset()).isEqualTo(2L);
29482952+
2953+ + // Not enough time elapsed, error rate limit was hit
2954+ + when(spanDataMock.getTraceId()).thenReturn("TRACE_ID3");
2955+ + when(attributesMock.get(URL_PATH)).thenReturn("/api2");
2956+ + when(attributesMock.get(HTTP_METHOD)).thenReturn("GET");
2957+ + sampler.adaptSampling(readableSpanMock, spanDataMock, stubbedConsumer);
2958+ + assertThat(exportCounter.sumThenReset()).isEqualTo(0L);
2959+ +
29492960+ // Test non-matching operation
2950- + when(spanDataMock.getTraceId()).thenReturn("TRACE_ID_2 ");
2961+ + when(spanDataMock.getTraceId()).thenReturn("TRACE_ID4 ");
29512962+ when(attributesMock.get(URL_PATH)).thenReturn("/api1/ext");
29522963+ when(attributesMock.get(HTTP_METHOD)).thenReturn("POST");
29532964+ sampler.adaptSampling(readableSpanMock, spanDataMock, stubbedConsumer);
2965+ + when(spanDataMock.getTraceId()).thenReturn("TRACE_ID5");
29542966+ when(attributesMock.get(URL_PATH)).thenReturn("/non-matching");
29552967+ when(attributesMock.get(HTTP_METHOD)).thenReturn("GET");
29562968+ sampler.adaptSampling(readableSpanMock, spanDataMock, stubbedConsumer);
29572969+ assertThat(exportCounter.sumThenReset()).isEqualTo(0L);
29582970+
29592971+ // Test aws.local.operation takes priority
2972+ + clock.advance(Duration.ofSeconds(5));
2973+ + when(spanDataMock.getTraceId()).thenReturn("TRACE_ID6");
29602974+ when(attributesMock.get(AwsAttributeKeys.AWS_LOCAL_OPERATION)).thenReturn("GET /api1");
29612975+ sampler.adaptSampling(readableSpanMock, spanDataMock, stubbedConsumer);
29622976+ assertThat(exportCounter.sumThenReset()).isEqualTo(1L);
29632977+
29642978+ // Test sending previously matched traceIDs gets captured
2965- + when(spanDataMock.getTraceId()).thenReturn("TRACE_ID");
2979+ + clock.advance(Duration.ofSeconds(5));
2980+ + when(spanDataMock.getTraceId()).thenReturn("TRACE_ID1");
29662981+ when(attributesMock.get(AwsAttributeKeys.AWS_LOCAL_OPERATION)).thenReturn("GET /non-matching");
29672982+ sampler.adaptSampling(readableSpanMock, spanDataMock, stubbedConsumer);
2968- + when(spanDataMock.getTraceId()).thenReturn("TRACE_ID_2 ");
2983+ + when(spanDataMock.getTraceId()).thenReturn("TRACE_ID2 ");
29692984+ sampler.adaptSampling(readableSpanMock, spanDataMock, stubbedConsumer);
29702985+ assertThat(exportCounter.sumThenReset()).isEqualTo(2L);
29712986+
0 commit comments