From b86116e5f1aec1f3af9a2b463364f1a3a7ecf86b Mon Sep 17 00:00:00 2001 From: Sylvain Juge <763082+SylvainJuge@users.noreply.github.com> Date: Thu, 4 Dec 2025 16:57:56 +0100 Subject: [PATCH 01/12] move 'compositesampling' package to 'sampling' --- .../java/co/elastic/otel/config/ConfigLoggingAgentListener.java | 2 +- .../main/java/co/elastic/otel/dynamicconfig/CentralConfig.java | 2 +- .../CompositeParentBasedTraceIdRatioBasedSamplerProvider.java | 2 +- .../DynamicCompositeParentBasedTraceIdRatioBasedSampler.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename custom/src/main/java/co/elastic/otel/{compositesampling => sampling}/CompositeParentBasedTraceIdRatioBasedSamplerProvider.java (97%) rename custom/src/main/java/co/elastic/otel/{compositesampling => sampling}/DynamicCompositeParentBasedTraceIdRatioBasedSampler.java (98%) diff --git a/custom/src/main/java/co/elastic/otel/config/ConfigLoggingAgentListener.java b/custom/src/main/java/co/elastic/otel/config/ConfigLoggingAgentListener.java index a047869d..7a788c63 100644 --- a/custom/src/main/java/co/elastic/otel/config/ConfigLoggingAgentListener.java +++ b/custom/src/main/java/co/elastic/otel/config/ConfigLoggingAgentListener.java @@ -18,7 +18,7 @@ */ package co.elastic.otel.config; -import co.elastic.otel.compositesampling.DynamicCompositeParentBasedTraceIdRatioBasedSampler; +import co.elastic.otel.sampling.DynamicCompositeParentBasedTraceIdRatioBasedSampler; import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.AgentListener; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; diff --git a/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java b/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java index 1607bfb6..1f165348 100644 --- a/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java +++ b/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java @@ -18,7 +18,7 @@ */ package co.elastic.otel.dynamicconfig; -import co.elastic.otel.compositesampling.DynamicCompositeParentBasedTraceIdRatioBasedSampler; +import co.elastic.otel.sampling.DynamicCompositeParentBasedTraceIdRatioBasedSampler; import co.elastic.otel.config.ConfigLoggingAgentListener; import co.elastic.otel.dynamicconfig.internal.OpampManager; import co.elastic.otel.logging.AgentLog; diff --git a/custom/src/main/java/co/elastic/otel/compositesampling/CompositeParentBasedTraceIdRatioBasedSamplerProvider.java b/custom/src/main/java/co/elastic/otel/sampling/CompositeParentBasedTraceIdRatioBasedSamplerProvider.java similarity index 97% rename from custom/src/main/java/co/elastic/otel/compositesampling/CompositeParentBasedTraceIdRatioBasedSamplerProvider.java rename to custom/src/main/java/co/elastic/otel/sampling/CompositeParentBasedTraceIdRatioBasedSamplerProvider.java index 21704f5c..e9a80f95 100644 --- a/custom/src/main/java/co/elastic/otel/compositesampling/CompositeParentBasedTraceIdRatioBasedSamplerProvider.java +++ b/custom/src/main/java/co/elastic/otel/sampling/CompositeParentBasedTraceIdRatioBasedSamplerProvider.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.otel.compositesampling; +package co.elastic.otel.sampling; import com.google.auto.service.AutoService; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; diff --git a/custom/src/main/java/co/elastic/otel/compositesampling/DynamicCompositeParentBasedTraceIdRatioBasedSampler.java b/custom/src/main/java/co/elastic/otel/sampling/DynamicCompositeParentBasedTraceIdRatioBasedSampler.java similarity index 98% rename from custom/src/main/java/co/elastic/otel/compositesampling/DynamicCompositeParentBasedTraceIdRatioBasedSampler.java rename to custom/src/main/java/co/elastic/otel/sampling/DynamicCompositeParentBasedTraceIdRatioBasedSampler.java index 6135135f..a88a771b 100644 --- a/custom/src/main/java/co/elastic/otel/compositesampling/DynamicCompositeParentBasedTraceIdRatioBasedSampler.java +++ b/custom/src/main/java/co/elastic/otel/sampling/DynamicCompositeParentBasedTraceIdRatioBasedSampler.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.otel.compositesampling; +package co.elastic.otel.sampling; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; From c632c22422fbfea6390b30827e4b0cecd67eaeab Mon Sep 17 00:00:00 2001 From: Sylvain Juge <763082+SylvainJuge@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:31:38 +0100 Subject: [PATCH 02/12] first draft implementation --- custom/build.gradle.kts | 3 + ...icAutoConfigurationCustomizerProvider.java | 4 +- .../config/ConfigLoggingAgentListener.java | 4 +- .../otel/dynamicconfig/CentralConfig.java | 6 +- ...oBasedSampler.java => ElasticSampler.java} | 45 ++++++++--- ...vider.java => ElasticSamplerProvider.java} | 19 +++-- .../otel/sampling/HttpFilteringSampler.java | 78 +++++++++++++++++++ gradle/libs.versions.toml | 1 + 8 files changed, 134 insertions(+), 26 deletions(-) rename custom/src/main/java/co/elastic/otel/sampling/{DynamicCompositeParentBasedTraceIdRatioBasedSampler.java => ElasticSampler.java} (57%) rename custom/src/main/java/co/elastic/otel/sampling/{CompositeParentBasedTraceIdRatioBasedSamplerProvider.java => ElasticSamplerProvider.java} (67%) create mode 100644 custom/src/main/java/co/elastic/otel/sampling/HttpFilteringSampler.java diff --git a/custom/build.gradle.kts b/custom/build.gradle.kts index a415796a..e5e13dcb 100644 --- a/custom/build.gradle.kts +++ b/custom/build.gradle.kts @@ -45,6 +45,9 @@ dependencies { exclude(group = "io.opentelemetry", module = "opentelemetry-sdk-extension-autoconfigure-spi") } + // samplers, included in upstream agent + compileOnly(libs.contribSamplers) + testImplementation(libs.contribSpanStacktrace) // needs to be added in order to allow access to AgentListener interface diff --git a/custom/src/main/java/co/elastic/otel/ElasticAutoConfigurationCustomizerProvider.java b/custom/src/main/java/co/elastic/otel/ElasticAutoConfigurationCustomizerProvider.java index 874e119e..9368cf4d 100644 --- a/custom/src/main/java/co/elastic/otel/ElasticAutoConfigurationCustomizerProvider.java +++ b/custom/src/main/java/co/elastic/otel/ElasticAutoConfigurationCustomizerProvider.java @@ -156,9 +156,7 @@ private static void resourceProviders( private static void defaultSampler( Map config, ConfigProperties configProperties) { // enable EDOT default sampler by default if not explicitly disabled - String sampler = - configProperties.getString( - TRACES_SAMPLER, "experimental_composite_parentbased_traceidratio"); + String sampler = configProperties.getString(TRACES_SAMPLER, "elastic"); config.put(TRACES_SAMPLER, sampler); } diff --git a/custom/src/main/java/co/elastic/otel/config/ConfigLoggingAgentListener.java b/custom/src/main/java/co/elastic/otel/config/ConfigLoggingAgentListener.java index 7a788c63..91d55cec 100644 --- a/custom/src/main/java/co/elastic/otel/config/ConfigLoggingAgentListener.java +++ b/custom/src/main/java/co/elastic/otel/config/ConfigLoggingAgentListener.java @@ -18,7 +18,7 @@ */ package co.elastic.otel.config; -import co.elastic.otel.sampling.DynamicCompositeParentBasedTraceIdRatioBasedSampler; +import co.elastic.otel.sampling.ElasticSampler; import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.AgentListener; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; @@ -49,7 +49,7 @@ public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetr logger.info(autoConfiguredOpenTelemetrySdk.toString()); } if (autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk().getSdkTracerProvider().getSampler() - instanceof DynamicCompositeParentBasedTraceIdRatioBasedSampler) { + instanceof ElasticSampler) { enableDynamicSamplingRate = true; } } diff --git a/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java b/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java index 1f165348..fcd46363 100644 --- a/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java +++ b/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java @@ -18,10 +18,10 @@ */ package co.elastic.otel.dynamicconfig; -import co.elastic.otel.sampling.DynamicCompositeParentBasedTraceIdRatioBasedSampler; import co.elastic.otel.config.ConfigLoggingAgentListener; import co.elastic.otel.dynamicconfig.internal.OpampManager; import co.elastic.otel.logging.AgentLog; +import co.elastic.otel.sampling.ElasticSampler; import io.opentelemetry.contrib.inferredspans.InferredSpans; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; @@ -334,8 +334,8 @@ void update(String configurationValue, OpampManager opampManager) logger.warning("ignoring \"sampling_rate\" because non-default sampler in use"); return; } - DynamicCompositeParentBasedTraceIdRatioBasedSampler.setRatio( - Double.parseDouble(configurationValue)); + ElasticSampler.setRatio(Double.parseDouble(configurationValue)); + // TODO: refresh ignore HTTP urls and user agents } } diff --git a/custom/src/main/java/co/elastic/otel/sampling/DynamicCompositeParentBasedTraceIdRatioBasedSampler.java b/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java similarity index 57% rename from custom/src/main/java/co/elastic/otel/sampling/DynamicCompositeParentBasedTraceIdRatioBasedSampler.java rename to custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java index a88a771b..8e6ebaa6 100644 --- a/custom/src/main/java/co/elastic/otel/sampling/DynamicCompositeParentBasedTraceIdRatioBasedSampler.java +++ b/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java @@ -24,19 +24,27 @@ import io.opentelemetry.contrib.sampler.consistent56.ConsistentSampler; import io.opentelemetry.sdk.trace.data.LinkData; import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.samplers.SamplingDecision; import io.opentelemetry.sdk.trace.samplers.SamplingResult; import java.util.List; -public enum DynamicCompositeParentBasedTraceIdRatioBasedSampler implements Sampler { +public enum ElasticSampler implements Sampler { INSTANCE; static final double DEFAULT_TRACEIDRATIO_SAMPLE_RATIO = 1.0d; private static volatile double latestRatio; - private static volatile Sampler delegate = newSampler(DEFAULT_TRACEIDRATIO_SAMPLE_RATIO); + private static volatile Sampler probabilitySampler = + newProbabilitySampler(DEFAULT_TRACEIDRATIO_SAMPLE_RATIO); + private static volatile Sampler filteringSampler = Sampler.alwaysOn(); + /** + * Set the ratio of the probability sampler + * + * @param ratio sampling probability + */ public static void setRatio(double ratio) { - delegate = newSampler(ratio); + probabilitySampler = newProbabilitySampler(ratio); } @Override @@ -47,24 +55,39 @@ public SamplingResult shouldSample( SpanKind spanKind, Attributes attributes, List parentLinks) { - return delegate.shouldSample(parentContext, traceId, name, spanKind, attributes, parentLinks); + SamplingResult filterResult = + filteringSampler.shouldSample( + parentContext, traceId, name, spanKind, attributes, parentLinks); + + if (filterResult.getDecision() == SamplingDecision.DROP) { + return filterResult; + } + + return probabilitySampler.shouldSample( + parentContext, traceId, name, spanKind, attributes, parentLinks); + } + + /** + * Configures the filtering of HTTP spans + * + * @param urlPatterns url regex patterns to drop + * @param userAgentPatterns user-agent regex patterns to drop + */ + public static void setFilterHttp(List urlPatterns, List userAgentPatterns) { + filteringSampler = HttpFilteringSampler.create(urlPatterns, userAgentPatterns); } @Override public String getDescription() { - return INSTANCE.getDescription(); + return toString(); } - private static Sampler newSampler(double ratio) { + private static Sampler newProbabilitySampler(double ratio) { latestRatio = ratio; return ConsistentSampler.parentBased(ConsistentSampler.probabilityBased(ratio)); } public String toString() { - return "DynamicCompositeParentBasedTraceIdRatioBasedSampler(ratio=" - + latestRatio - + ", " - + delegate.toString() - + ")"; + return "ElasticSampler(ratio=" + latestRatio + ", " + probabilitySampler.toString() + ")"; } } diff --git a/custom/src/main/java/co/elastic/otel/sampling/CompositeParentBasedTraceIdRatioBasedSamplerProvider.java b/custom/src/main/java/co/elastic/otel/sampling/ElasticSamplerProvider.java similarity index 67% rename from custom/src/main/java/co/elastic/otel/sampling/CompositeParentBasedTraceIdRatioBasedSamplerProvider.java rename to custom/src/main/java/co/elastic/otel/sampling/ElasticSamplerProvider.java index e9a80f95..5aa941fc 100644 --- a/custom/src/main/java/co/elastic/otel/sampling/CompositeParentBasedTraceIdRatioBasedSamplerProvider.java +++ b/custom/src/main/java/co/elastic/otel/sampling/ElasticSamplerProvider.java @@ -24,20 +24,25 @@ import io.opentelemetry.sdk.trace.samplers.Sampler; @AutoService(ConfigurableSamplerProvider.class) -public class CompositeParentBasedTraceIdRatioBasedSamplerProvider - implements ConfigurableSamplerProvider { +public class ElasticSamplerProvider implements ConfigurableSamplerProvider { + + private static final String ELASTIC_OTEL_IGNORE_URLS = + "elastic.otel.experimental.http.ignore.urls"; + private static final String ELASTIC_OTEL_IGNORE_USER_AGENTS = + "elastic.otel.experimental.http.ignore.user-agents"; @Override public Sampler createSampler(ConfigProperties config) { - DynamicCompositeParentBasedTraceIdRatioBasedSampler.setRatio( + ElasticSampler.setRatio( config.getDouble( - "otel.traces.sampler.arg", - DynamicCompositeParentBasedTraceIdRatioBasedSampler.DEFAULT_TRACEIDRATIO_SAMPLE_RATIO)); - return DynamicCompositeParentBasedTraceIdRatioBasedSampler.INSTANCE; + "otel.traces.sampler.arg", ElasticSampler.DEFAULT_TRACEIDRATIO_SAMPLE_RATIO)); + ElasticSampler.setFilterHttp( + config.getList(ELASTIC_OTEL_IGNORE_URLS), config.getList(ELASTIC_OTEL_IGNORE_USER_AGENTS)); + return ElasticSampler.INSTANCE; } @Override public String getName() { - return "experimental_composite_parentbased_traceidratio"; + return "elastic"; } } diff --git a/custom/src/main/java/co/elastic/otel/sampling/HttpFilteringSampler.java b/custom/src/main/java/co/elastic/otel/sampling/HttpFilteringSampler.java new file mode 100644 index 00000000..02b6c8be --- /dev/null +++ b/custom/src/main/java/co/elastic/otel/sampling/HttpFilteringSampler.java @@ -0,0 +1,78 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License 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 co.elastic.otel.sampling; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Context; +import io.opentelemetry.contrib.sampler.RuleBasedRoutingSampler; +import io.opentelemetry.contrib.sampler.RuleBasedRoutingSamplerBuilder; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.samplers.SamplingResult; +import io.opentelemetry.semconv.HttpAttributes; +import io.opentelemetry.semconv.UrlAttributes; +import java.util.List; + +public class HttpFilteringSampler implements Sampler { + + private final Sampler delegate; + + private HttpFilteringSampler(Sampler delegate) { + this.delegate = delegate; + } + + @Override + public SamplingResult shouldSample( + Context parentContext, + String traceId, + String name, + SpanKind spanKind, + Attributes attributes, + List parentLinks) { + return delegate.shouldSample(parentContext, traceId, name, spanKind, attributes, parentLinks); + } + + @Override + public String getDescription() { + return delegate.getDescription(); + } + + public static HttpFilteringSampler create( + List urlPatterns, List userAgentPatterns) { + RuleBasedRoutingSamplerBuilder builder = + RuleBasedRoutingSampler.builder(SpanKind.SERVER, Sampler.alwaysOn()); + AttributeKey userAgentKey = + AttributeKey.stringKey( + HttpAttributes.HTTP_REQUEST_HEADER.getAttributeKey("user-agent").getKey()); + for (String pattern : userAgentPatterns) { + builder.drop(userAgentKey, pattern); + } + for (String pattern : urlPatterns) { + builder.drop(UrlAttributes.URL_PATH, pattern); + } + return new HttpFilteringSampler(builder.build()); + } + + @Override + public String toString() { + return "HttpFilteringSampler(delegate=" + delegate + ')'; + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 169e4ce5..e21d0a14 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,6 +37,7 @@ contribSpanStacktrace = { group = "io.opentelemetry.contrib", name = "openteleme contribInferredSpans = { group = "io.opentelemetry.contrib", name = "opentelemetry-inferred-spans", version.ref = "opentelemetryContribAlpha" } contribRuntimeAttach = { group = "io.opentelemetry.contrib", name = "opentelemetry-runtime-attach-core", version.ref = "opentelemetryContribAlpha" } opentelemetry-opamp = { module = "io.opentelemetry.contrib:opentelemetry-opamp-client", version.ref = "opentelemetryContribAlpha" } +contribSamplers = { module = "io.opentelemetry.contrib:opentelemetry-samplers", version.ref = "opentelemetryContribAlpha" } opentelemetrySemconv = { group = "io.opentelemetry.semconv", name = "opentelemetry-semconv", version.ref = "opentelemetrySemconv" } opentelemetrySemconvIncubating = { group = "io.opentelemetry.semconv", name = "opentelemetry-semconv-incubating", version.ref = "opentelemetrySemconvAlpha" } From 58af29abba5948172dce3151053ca6ce6d07fa68 Mon Sep 17 00:00:00 2001 From: Sylvain Juge <763082+SylvainJuge@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:53:18 +0100 Subject: [PATCH 03/12] fix user agent filtering --- .../co/elastic/otel/sampling/HttpFilteringSampler.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/custom/src/main/java/co/elastic/otel/sampling/HttpFilteringSampler.java b/custom/src/main/java/co/elastic/otel/sampling/HttpFilteringSampler.java index 02b6c8be..5c183065 100644 --- a/custom/src/main/java/co/elastic/otel/sampling/HttpFilteringSampler.java +++ b/custom/src/main/java/co/elastic/otel/sampling/HttpFilteringSampler.java @@ -18,7 +18,6 @@ */ package co.elastic.otel.sampling; -import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.context.Context; @@ -27,8 +26,8 @@ import io.opentelemetry.sdk.trace.data.LinkData; import io.opentelemetry.sdk.trace.samplers.Sampler; import io.opentelemetry.sdk.trace.samplers.SamplingResult; -import io.opentelemetry.semconv.HttpAttributes; import io.opentelemetry.semconv.UrlAttributes; +import io.opentelemetry.semconv.UserAgentAttributes; import java.util.List; public class HttpFilteringSampler implements Sampler { @@ -59,11 +58,8 @@ public static HttpFilteringSampler create( List urlPatterns, List userAgentPatterns) { RuleBasedRoutingSamplerBuilder builder = RuleBasedRoutingSampler.builder(SpanKind.SERVER, Sampler.alwaysOn()); - AttributeKey userAgentKey = - AttributeKey.stringKey( - HttpAttributes.HTTP_REQUEST_HEADER.getAttributeKey("user-agent").getKey()); for (String pattern : userAgentPatterns) { - builder.drop(userAgentKey, pattern); + builder.drop(UserAgentAttributes.USER_AGENT_ORIGINAL, pattern); } for (String pattern : urlPatterns) { builder.drop(UrlAttributes.URL_PATH, pattern); From 9857cf9b7ac5d08bcbedd99c3e104b1ed890fb15 Mon Sep 17 00:00:00 2001 From: Sylvain Juge <763082+SylvainJuge@users.noreply.github.com> Date: Fri, 5 Dec 2025 09:22:47 +0100 Subject: [PATCH 04/12] fix test --- .../otel/ElasticAutoConfigurationCustomizerProviderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom/src/test/java/co/elastic/otel/ElasticAutoConfigurationCustomizerProviderTest.java b/custom/src/test/java/co/elastic/otel/ElasticAutoConfigurationCustomizerProviderTest.java index 42aff53e..4ba82d2a 100644 --- a/custom/src/test/java/co/elastic/otel/ElasticAutoConfigurationCustomizerProviderTest.java +++ b/custom/src/test/java/co/elastic/otel/ElasticAutoConfigurationCustomizerProviderTest.java @@ -55,7 +55,7 @@ void defaultConfiguration() { assertThat(config) .describedAs("edot default sampler when not set by user") - .containsEntry("otel.traces.sampler", "experimental_composite_parentbased_traceidratio"); + .containsEntry("otel.traces.sampler", "elastic"); } @Test From d0cfa2b283d650fb06a2e4d963faa0c303924175 Mon Sep 17 00:00:00 2001 From: Sylvain Juge <763082+SylvainJuge@users.noreply.github.com> Date: Wed, 14 Jan 2026 17:56:14 +0100 Subject: [PATCH 05/12] use composable sampler --- custom/build.gradle.kts | 2 +- .../otel/dynamicconfig/CentralConfig.java | 5 +- .../elastic/otel/sampling/ElasticSampler.java | 128 ++++++++++++++---- .../otel/sampling/ElasticSamplerProvider.java | 12 +- .../otel/sampling/HttpFilteringSampler.java | 74 ---------- 5 files changed, 113 insertions(+), 108 deletions(-) delete mode 100644 custom/src/main/java/co/elastic/otel/sampling/HttpFilteringSampler.java diff --git a/custom/build.gradle.kts b/custom/build.gradle.kts index e5e13dcb..2538ae5c 100644 --- a/custom/build.gradle.kts +++ b/custom/build.gradle.kts @@ -46,7 +46,7 @@ dependencies { } // samplers, included in upstream agent - compileOnly(libs.contribSamplers) + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator") testImplementation(libs.contribSpanStacktrace) diff --git a/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java b/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java index fcd46363..90eee10e 100644 --- a/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java +++ b/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java @@ -334,8 +334,9 @@ void update(String configurationValue, OpampManager opampManager) logger.warning("ignoring \"sampling_rate\" because non-default sampler in use"); return; } - ElasticSampler.setRatio(Double.parseDouble(configurationValue)); - // TODO: refresh ignore HTTP urls and user agents + ElasticSampler.globalBuilder() + .withProbability(Double.parseDouble(configurationValue)) + .buildAndSetGlobal(); } } diff --git a/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java b/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java index 8e6ebaa6..a9281999 100644 --- a/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java +++ b/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java @@ -18,25 +18,38 @@ */ package co.elastic.otel.sampling; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.context.Context; -import io.opentelemetry.contrib.sampler.consistent56.ConsistentSampler; +import io.opentelemetry.sdk.extension.incubator.trace.samplers.ComposableRuleBasedSamplerBuilder; +import io.opentelemetry.sdk.extension.incubator.trace.samplers.ComposableSampler; +import io.opentelemetry.sdk.extension.incubator.trace.samplers.CompositeSampler; +import io.opentelemetry.sdk.extension.incubator.trace.samplers.SamplingPredicate; +import io.opentelemetry.sdk.internal.IncludeExcludePredicate; import io.opentelemetry.sdk.trace.data.LinkData; import io.opentelemetry.sdk.trace.samplers.Sampler; -import io.opentelemetry.sdk.trace.samplers.SamplingDecision; import io.opentelemetry.sdk.trace.samplers.SamplingResult; +import io.opentelemetry.semconv.UrlAttributes; +import io.opentelemetry.semconv.UserAgentAttributes; +import java.util.Collections; import java.util.List; +import java.util.function.Predicate; +import java.util.logging.Logger; -public enum ElasticSampler implements Sampler { - INSTANCE; - static final double DEFAULT_TRACEIDRATIO_SAMPLE_RATIO = 1.0d; +public class ElasticSampler implements Sampler { - private static volatile double latestRatio; - private static volatile Sampler probabilitySampler = - newProbabilitySampler(DEFAULT_TRACEIDRATIO_SAMPLE_RATIO); - private static volatile Sampler filteringSampler = Sampler.alwaysOn(); + private static final Logger logger = Logger.getLogger(ElasticSampler.class.getName()); + + static final double DEFAULT_SAMPLE_RATIO = 1.0d; + + private static volatile Sampler delegate; + private static final Builder builder = new Builder(); + + public static Builder globalBuilder() { + return builder; + } /** * Set the ratio of the probability sampler @@ -44,7 +57,7 @@ public enum ElasticSampler implements Sampler { * @param ratio sampling probability */ public static void setRatio(double ratio) { - probabilitySampler = newProbabilitySampler(ratio); + globalBuilder().withProbability(ratio).buildAndSetGlobal(); } @Override @@ -55,16 +68,8 @@ public SamplingResult shouldSample( SpanKind spanKind, Attributes attributes, List parentLinks) { - SamplingResult filterResult = - filteringSampler.shouldSample( - parentContext, traceId, name, spanKind, attributes, parentLinks); - if (filterResult.getDecision() == SamplingDecision.DROP) { - return filterResult; - } - - return probabilitySampler.shouldSample( - parentContext, traceId, name, spanKind, attributes, parentLinks); + return delegate.shouldSample(parentContext, traceId, name, spanKind, attributes, parentLinks); } /** @@ -74,7 +79,10 @@ public SamplingResult shouldSample( * @param userAgentPatterns user-agent regex patterns to drop */ public static void setFilterHttp(List urlPatterns, List userAgentPatterns) { - filteringSampler = HttpFilteringSampler.create(urlPatterns, userAgentPatterns); + globalBuilder() + .withIgnoredUrlPatterns(urlPatterns) + .withIgnoredUserAgentPatterns(userAgentPatterns) + .buildAndSetGlobal(); } @Override @@ -82,12 +90,82 @@ public String getDescription() { return toString(); } - private static Sampler newProbabilitySampler(double ratio) { - latestRatio = ratio; - return ConsistentSampler.parentBased(ConsistentSampler.probabilityBased(ratio)); + public String toString() { + return delegate.getDescription(); } - public String toString() { - return "ElasticSampler(ratio=" + latestRatio + ", " + probabilitySampler.toString() + ")"; + public static class Builder { + + private List ignoredUrlPatterns; + private List ignoredUserAgentPatterns; + private double ratio; + + // package-private for testing + Builder() { + this.ignoredUrlPatterns = Collections.emptyList(); + this.ignoredUserAgentPatterns = Collections.emptyList(); + this.ratio = DEFAULT_SAMPLE_RATIO; + } + + public Builder withProbability(double ratio) { + this.ratio = ratio; + return this; + } + + public Builder withIgnoredUrlPatterns(List patterns) { + this.ignoredUrlPatterns = patterns; + return this; + } + + public Builder withIgnoredUserAgentPatterns(List patterns) { + this.ignoredUserAgentPatterns = patterns; + return this; + } + + // package-private for testing + Sampler build() { + if (ignoredUrlPatterns.isEmpty() && ignoredUrlPatterns.isEmpty()) { + return CompositeSampler.wrap(ComposableSampler.probability(ratio)); + } + ComposableRuleBasedSamplerBuilder ruleBuilder = ComposableSampler.ruleBasedBuilder(); + + ruleBuilder.add( + valueMatching(UrlAttributes.URL_PATH, ignoredUrlPatterns), + ComposableSampler.alwaysOff()); + + ruleBuilder.add( + valueMatching(UserAgentAttributes.USER_AGENT_ORIGINAL, ignoredUserAgentPatterns), + ComposableSampler.alwaysOff()); + + // probability sampler applied last without any attribute filtering + ruleBuilder.add(any(), ComposableSampler.probability(ratio)); + return CompositeSampler.wrap(ruleBuilder.build()); + } + + public Sampler buildAndSetGlobal() { + Sampler sampler = build(); + logger.fine("set global sampler to " + sampler.getDescription()); + delegate = sampler; + return sampler; + } + + private static SamplingPredicate any() { + return (parentContext, traceId, name, spanKind, attributes, parentLinks) -> true; + } + + private static SamplingPredicate valueMatching(AttributeKey attributeKey, List patterns) { + Predicate predicate = IncludeExcludePredicate.createPatternMatching(patterns, null); + return new SamplingPredicate() { + @Override + public boolean matches(Context parentContext, String traceId, String name, + SpanKind spanKind, Attributes attributes, List parentLinks) { + String value = attributes.get(attributeKey); + return predicate.test(value); + } + }; + } + } + + } diff --git a/custom/src/main/java/co/elastic/otel/sampling/ElasticSamplerProvider.java b/custom/src/main/java/co/elastic/otel/sampling/ElasticSamplerProvider.java index 5aa941fc..b1793215 100644 --- a/custom/src/main/java/co/elastic/otel/sampling/ElasticSamplerProvider.java +++ b/custom/src/main/java/co/elastic/otel/sampling/ElasticSamplerProvider.java @@ -33,12 +33,12 @@ public class ElasticSamplerProvider implements ConfigurableSamplerProvider { @Override public Sampler createSampler(ConfigProperties config) { - ElasticSampler.setRatio( - config.getDouble( - "otel.traces.sampler.arg", ElasticSampler.DEFAULT_TRACEIDRATIO_SAMPLE_RATIO)); - ElasticSampler.setFilterHttp( - config.getList(ELASTIC_OTEL_IGNORE_URLS), config.getList(ELASTIC_OTEL_IGNORE_USER_AGENTS)); - return ElasticSampler.INSTANCE; + return ElasticSampler.globalBuilder() + .withProbability(config.getDouble( + "otel.traces.sampler.arg", ElasticSampler.DEFAULT_SAMPLE_RATIO)) + .withIgnoredUrlPatterns(config.getList(ELASTIC_OTEL_IGNORE_URLS)) + .withIgnoredUserAgentPatterns(config.getList(ELASTIC_OTEL_IGNORE_USER_AGENTS)) + .buildAndSetGlobal(); } @Override diff --git a/custom/src/main/java/co/elastic/otel/sampling/HttpFilteringSampler.java b/custom/src/main/java/co/elastic/otel/sampling/HttpFilteringSampler.java deleted file mode 100644 index 5c183065..00000000 --- a/custom/src/main/java/co/elastic/otel/sampling/HttpFilteringSampler.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License 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 co.elastic.otel.sampling; - -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.context.Context; -import io.opentelemetry.contrib.sampler.RuleBasedRoutingSampler; -import io.opentelemetry.contrib.sampler.RuleBasedRoutingSamplerBuilder; -import io.opentelemetry.sdk.trace.data.LinkData; -import io.opentelemetry.sdk.trace.samplers.Sampler; -import io.opentelemetry.sdk.trace.samplers.SamplingResult; -import io.opentelemetry.semconv.UrlAttributes; -import io.opentelemetry.semconv.UserAgentAttributes; -import java.util.List; - -public class HttpFilteringSampler implements Sampler { - - private final Sampler delegate; - - private HttpFilteringSampler(Sampler delegate) { - this.delegate = delegate; - } - - @Override - public SamplingResult shouldSample( - Context parentContext, - String traceId, - String name, - SpanKind spanKind, - Attributes attributes, - List parentLinks) { - return delegate.shouldSample(parentContext, traceId, name, spanKind, attributes, parentLinks); - } - - @Override - public String getDescription() { - return delegate.getDescription(); - } - - public static HttpFilteringSampler create( - List urlPatterns, List userAgentPatterns) { - RuleBasedRoutingSamplerBuilder builder = - RuleBasedRoutingSampler.builder(SpanKind.SERVER, Sampler.alwaysOn()); - for (String pattern : userAgentPatterns) { - builder.drop(UserAgentAttributes.USER_AGENT_ORIGINAL, pattern); - } - for (String pattern : urlPatterns) { - builder.drop(UrlAttributes.URL_PATH, pattern); - } - return new HttpFilteringSampler(builder.build()); - } - - @Override - public String toString() { - return "HttpFilteringSampler(delegate=" + delegate + ')'; - } -} From 2f4c8bdd6a03ebe0d7f4ca6f3dead245ea918847 Mon Sep 17 00:00:00 2001 From: Sylvain Juge <763082+SylvainJuge@users.noreply.github.com> Date: Thu, 15 Jan 2026 12:26:33 +0100 Subject: [PATCH 06/12] polish new implementation --- .../otel/dynamicconfig/CentralConfig.java | 2 +- .../elastic/otel/sampling/ElasticSampler.java | 98 +++++++++++-------- .../otel/sampling/ElasticSamplerProvider.java | 8 +- .../otel/sampling/ElasticSamplerTest.java | 82 ++++++++++++++++ 4 files changed, 143 insertions(+), 47 deletions(-) create mode 100644 custom/src/test/java/co/elastic/otel/sampling/ElasticSamplerTest.java diff --git a/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java b/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java index 90eee10e..6538ab84 100644 --- a/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java +++ b/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java @@ -334,7 +334,7 @@ void update(String configurationValue, OpampManager opampManager) logger.warning("ignoring \"sampling_rate\" because non-default sampler in use"); return; } - ElasticSampler.globalBuilder() + ElasticSampler.INSTANCE.toBuilder() .withProbability(Double.parseDouble(configurationValue)) .buildAndSetGlobal(); } diff --git a/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java b/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java index a9281999..b403b2cd 100644 --- a/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java +++ b/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java @@ -42,22 +42,13 @@ public class ElasticSampler implements Sampler { private static final Logger logger = Logger.getLogger(ElasticSampler.class.getName()); - static final double DEFAULT_SAMPLE_RATIO = 1.0d; - - private static volatile Sampler delegate; + public static ElasticSampler INSTANCE = new ElasticSampler(); private static final Builder builder = new Builder(); + private static volatile Sampler delegate = builder.build(); - public static Builder globalBuilder() { - return builder; - } + static final double DEFAULT_SAMPLE_RATIO = 1.0d; - /** - * Set the ratio of the probability sampler - * - * @param ratio sampling probability - */ - public static void setRatio(double ratio) { - globalBuilder().withProbability(ratio).buildAndSetGlobal(); + private ElasticSampler() { } @Override @@ -72,17 +63,8 @@ public SamplingResult shouldSample( return delegate.shouldSample(parentContext, traceId, name, spanKind, attributes, parentLinks); } - /** - * Configures the filtering of HTTP spans - * - * @param urlPatterns url regex patterns to drop - * @param userAgentPatterns user-agent regex patterns to drop - */ - public static void setFilterHttp(List urlPatterns, List userAgentPatterns) { - globalBuilder() - .withIgnoredUrlPatterns(urlPatterns) - .withIgnoredUserAgentPatterns(userAgentPatterns) - .buildAndSetGlobal(); + public Builder toBuilder() { + return builder; } @Override @@ -124,29 +106,38 @@ public Builder withIgnoredUserAgentPatterns(List patterns) { // package-private for testing Sampler build() { - if (ignoredUrlPatterns.isEmpty() && ignoredUrlPatterns.isEmpty()) { - return CompositeSampler.wrap(ComposableSampler.probability(ratio)); - } + int rulesCount = 0; ComposableRuleBasedSamplerBuilder ruleBuilder = ComposableSampler.ruleBasedBuilder(); - ruleBuilder.add( - valueMatching(UrlAttributes.URL_PATH, ignoredUrlPatterns), - ComposableSampler.alwaysOff()); + if (!ignoredUrlPatterns.isEmpty()) { + ruleBuilder.add( + valueMatching(UrlAttributes.URL_PATH, ignoredUrlPatterns), + ComposableSampler.alwaysOff()); + rulesCount++; + } + + if (!ignoredUserAgentPatterns.isEmpty()) { + ruleBuilder.add( + valueMatching(UserAgentAttributes.USER_AGENT_ORIGINAL, ignoredUserAgentPatterns), + ComposableSampler.alwaysOff()); + rulesCount++; + } - ruleBuilder.add( - valueMatching(UserAgentAttributes.USER_AGENT_ORIGINAL, ignoredUserAgentPatterns), - ComposableSampler.alwaysOff()); + if (rulesCount == 0) { + // no rules, just return the probability sampler directly + return CompositeSampler.wrap(ComposableSampler.probability(ratio)); + } // probability sampler applied last without any attribute filtering ruleBuilder.add(any(), ComposableSampler.probability(ratio)); return CompositeSampler.wrap(ruleBuilder.build()); } - public Sampler buildAndSetGlobal() { + public ElasticSampler buildAndSetGlobal() { Sampler sampler = build(); logger.fine("set global sampler to " + sampler.getDescription()); delegate = sampler; - return sampler; + return INSTANCE; } private static SamplingPredicate any() { @@ -155,16 +146,37 @@ private static SamplingPredicate any() { private static SamplingPredicate valueMatching(AttributeKey attributeKey, List patterns) { Predicate predicate = IncludeExcludePredicate.createPatternMatching(patterns, null); - return new SamplingPredicate() { - @Override - public boolean matches(Context parentContext, String traceId, String name, - SpanKind spanKind, Attributes attributes, List parentLinks) { - String value = attributes.get(attributeKey); - return predicate.test(value); - } - }; + return new ValueMatchingSamplingPredicate(attributeKey, predicate); } + private static class ValueMatchingSamplingPredicate implements SamplingPredicate { + private final AttributeKey attributeKey; + private final Predicate predicate; + + public ValueMatchingSamplingPredicate(AttributeKey attributeKey, + Predicate predicate) { + this.attributeKey = attributeKey; + this.predicate = predicate; + } + + @Override + public boolean matches(Context parentContext, String traceId, String name, + SpanKind spanKind, Attributes attributes, List parentLinks) { + String value = attributes.get(attributeKey); + if (value == null) { + return false; + } + return predicate.test(value); + } + + @Override + public String toString() { + return "ValueMatchingSamplingPredicate{" + + "attributeKey=" + attributeKey + + ", predicate=" + predicate + + '}'; + } + } } diff --git a/custom/src/main/java/co/elastic/otel/sampling/ElasticSamplerProvider.java b/custom/src/main/java/co/elastic/otel/sampling/ElasticSamplerProvider.java index b1793215..cc2facbf 100644 --- a/custom/src/main/java/co/elastic/otel/sampling/ElasticSamplerProvider.java +++ b/custom/src/main/java/co/elastic/otel/sampling/ElasticSamplerProvider.java @@ -33,9 +33,11 @@ public class ElasticSamplerProvider implements ConfigurableSamplerProvider { @Override public Sampler createSampler(ConfigProperties config) { - return ElasticSampler.globalBuilder() - .withProbability(config.getDouble( - "otel.traces.sampler.arg", ElasticSampler.DEFAULT_SAMPLE_RATIO)) + double ratio = config.getDouble( + "otel.traces.sampler.arg", ElasticSampler.DEFAULT_SAMPLE_RATIO); + + return ElasticSampler.INSTANCE.toBuilder() + .withProbability(ratio) .withIgnoredUrlPatterns(config.getList(ELASTIC_OTEL_IGNORE_URLS)) .withIgnoredUserAgentPatterns(config.getList(ELASTIC_OTEL_IGNORE_USER_AGENTS)) .buildAndSetGlobal(); diff --git a/custom/src/test/java/co/elastic/otel/sampling/ElasticSamplerTest.java b/custom/src/test/java/co/elastic/otel/sampling/ElasticSamplerTest.java new file mode 100644 index 00000000..4b9895e0 --- /dev/null +++ b/custom/src/test/java/co/elastic/otel/sampling/ElasticSamplerTest.java @@ -0,0 +1,82 @@ +package co.elastic.otel.sampling; + +import static io.opentelemetry.sdk.trace.samplers.SamplingDecision.DROP; +import static io.opentelemetry.sdk.trace.samplers.SamplingDecision.RECORD_AND_SAMPLE; +import static io.opentelemetry.semconv.UrlAttributes.URL_PATH; +import static io.opentelemetry.semconv.UserAgentAttributes.USER_AGENT_ORIGINAL; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.TraceId; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.samplers.SamplingDecision; +import io.opentelemetry.sdk.trace.samplers.SamplingResult; +import java.util.Arrays; +import java.util.Collections; +import org.junit.jupiter.api.Test; + +class ElasticSamplerTest { + + // those tests rely on the implementation, but it's the simplest way to verify the effective + // sampler configuration we get as multiple samplers are composed. + + @Test + void defaultProbability() { + Sampler sampler = new ElasticSampler.Builder().build(); + assertThat(sampler.getDescription()).isEqualTo("ComposableTraceIdRatioBasedSampler{threshold=0, ratio=1.0}"); + } + + @Test + void highProbability() { + Sampler sampler = new ElasticSampler.Builder().withProbability(0.99999999).build(); + assertThat(sampler.getDescription()).isEqualTo("ComposableTraceIdRatioBasedSampler{threshold=0000002af31dc8, ratio=0.99999999}"); + } + + @Test + void halfProbability() { + Sampler sampler = new ElasticSampler.Builder().withProbability(0.5).build(); + assertThat(sampler.getDescription()).isEqualTo("ComposableTraceIdRatioBasedSampler{threshold=8, ratio=0.5}"); + } + + @Test + void offProbability() { + Sampler sampler = new ElasticSampler.Builder().withProbability(0.0).build(); + assertThat(sampler.getDescription()).isEqualTo("ComposableTraceIdRatioBasedSampler{threshold=max, ratio=0.0}"); + } + + @Test + void ignoreUrlPath() { + Sampler sampler = new ElasticSampler.Builder().withIgnoredUrlPatterns(Collections.singletonList("/health/*")).build(); + checkSampling(sampler, Attributes.empty(), RECORD_AND_SAMPLE); + checkSampling(sampler, Attributes.of(URL_PATH, "/health/"), DROP); + checkSampling(sampler, Attributes.of(URL_PATH, "/health/test"), DROP); + checkSampling(sampler, Attributes.of(URL_PATH, "/healthcheck"), RECORD_AND_SAMPLE); + } + + @Test + void ignoreUserAgent() { + Sampler sampler = new ElasticSampler.Builder().withIgnoredUserAgentPatterns( + Arrays.asList("curl*", "*Curly")).build(); + checkSampling(sampler, Attributes.empty(), RECORD_AND_SAMPLE); + checkSampling(sampler, Attributes.of(USER_AGENT_ORIGINAL, "curl"), DROP); + checkSampling(sampler, Attributes.of(USER_AGENT_ORIGINAL, "HappyCurly"), DROP); + checkSampling(sampler, Attributes.of(USER_AGENT_ORIGINAL, "CURL"), RECORD_AND_SAMPLE); + } + + private static void checkSampling(Sampler sampler, Attributes attributes, + SamplingDecision expectedDecision) { + SamplingResult samplingResult = sampler.shouldSample(Context.root(), TraceId.getInvalid(), "name", null, + attributes, + Collections.emptyList()); + assertThat(samplingResult.getDecision()).isEqualTo(expectedDecision); + } + + @Test + void singleGlobalBuilderInstance() { + ElasticSampler.Builder first = ElasticSampler.INSTANCE.toBuilder(); + ElasticSampler.Builder second = ElasticSampler.INSTANCE.toBuilder(); + assertThat(first).isSameAs(second); + } + +} From d534ca9c200b3d73fc3a37da6576449961fc0968 Mon Sep 17 00:00:00 2001 From: Sylvain Juge <763082+SylvainJuge@users.noreply.github.com> Date: Thu, 15 Jan 2026 13:24:28 +0100 Subject: [PATCH 07/12] code reformat --- .../elastic/otel/sampling/ElasticSampler.java | 32 +++++----- .../otel/sampling/ElasticSamplerProvider.java | 3 +- .../otel/sampling/ElasticSamplerTest.java | 58 ++++++++++++++----- 3 files changed, 64 insertions(+), 29 deletions(-) diff --git a/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java b/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java index b403b2cd..d28ee56e 100644 --- a/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java +++ b/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java @@ -37,7 +37,6 @@ import java.util.function.Predicate; import java.util.logging.Logger; - public class ElasticSampler implements Sampler { private static final Logger logger = Logger.getLogger(ElasticSampler.class.getName()); @@ -48,8 +47,7 @@ public class ElasticSampler implements Sampler { static final double DEFAULT_SAMPLE_RATIO = 1.0d; - private ElasticSampler() { - } + private ElasticSampler() {} @Override public SamplingResult shouldSample( @@ -144,7 +142,8 @@ private static SamplingPredicate any() { return (parentContext, traceId, name, spanKind, attributes, parentLinks) -> true; } - private static SamplingPredicate valueMatching(AttributeKey attributeKey, List patterns) { + private static SamplingPredicate valueMatching( + AttributeKey attributeKey, List patterns) { Predicate predicate = IncludeExcludePredicate.createPatternMatching(patterns, null); return new ValueMatchingSamplingPredicate(attributeKey, predicate); } @@ -153,15 +152,20 @@ private static class ValueMatchingSamplingPredicate implements SamplingPredicate private final AttributeKey attributeKey; private final Predicate predicate; - public ValueMatchingSamplingPredicate(AttributeKey attributeKey, - Predicate predicate) { + public ValueMatchingSamplingPredicate( + AttributeKey attributeKey, Predicate predicate) { this.attributeKey = attributeKey; this.predicate = predicate; } @Override - public boolean matches(Context parentContext, String traceId, String name, - SpanKind spanKind, Attributes attributes, List parentLinks) { + public boolean matches( + Context parentContext, + String traceId, + String name, + SpanKind spanKind, + Attributes attributes, + List parentLinks) { String value = attributes.get(attributeKey); if (value == null) { return false; @@ -171,13 +175,13 @@ public boolean matches(Context parentContext, String traceId, String name, @Override public String toString() { - return "ValueMatchingSamplingPredicate{" + - "attributeKey=" + attributeKey + - ", predicate=" + predicate + - '}'; + return "ValueMatchingSamplingPredicate{" + + "attributeKey=" + + attributeKey + + ", predicate=" + + predicate + + '}'; } } } - - } diff --git a/custom/src/main/java/co/elastic/otel/sampling/ElasticSamplerProvider.java b/custom/src/main/java/co/elastic/otel/sampling/ElasticSamplerProvider.java index cc2facbf..c4fa6046 100644 --- a/custom/src/main/java/co/elastic/otel/sampling/ElasticSamplerProvider.java +++ b/custom/src/main/java/co/elastic/otel/sampling/ElasticSamplerProvider.java @@ -33,8 +33,7 @@ public class ElasticSamplerProvider implements ConfigurableSamplerProvider { @Override public Sampler createSampler(ConfigProperties config) { - double ratio = config.getDouble( - "otel.traces.sampler.arg", ElasticSampler.DEFAULT_SAMPLE_RATIO); + double ratio = config.getDouble("otel.traces.sampler.arg", ElasticSampler.DEFAULT_SAMPLE_RATIO); return ElasticSampler.INSTANCE.toBuilder() .withProbability(ratio) diff --git a/custom/src/test/java/co/elastic/otel/sampling/ElasticSamplerTest.java b/custom/src/test/java/co/elastic/otel/sampling/ElasticSamplerTest.java index 4b9895e0..96e51128 100644 --- a/custom/src/test/java/co/elastic/otel/sampling/ElasticSamplerTest.java +++ b/custom/src/test/java/co/elastic/otel/sampling/ElasticSamplerTest.java @@ -1,3 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License 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 co.elastic.otel.sampling; import static io.opentelemetry.sdk.trace.samplers.SamplingDecision.DROP; @@ -24,30 +42,38 @@ class ElasticSamplerTest { @Test void defaultProbability() { Sampler sampler = new ElasticSampler.Builder().build(); - assertThat(sampler.getDescription()).isEqualTo("ComposableTraceIdRatioBasedSampler{threshold=0, ratio=1.0}"); + assertThat(sampler.getDescription()) + .isEqualTo("ComposableTraceIdRatioBasedSampler{threshold=0, ratio=1.0}"); } @Test void highProbability() { Sampler sampler = new ElasticSampler.Builder().withProbability(0.99999999).build(); - assertThat(sampler.getDescription()).isEqualTo("ComposableTraceIdRatioBasedSampler{threshold=0000002af31dc8, ratio=0.99999999}"); + assertThat(sampler.getDescription()) + .isEqualTo( + "ComposableTraceIdRatioBasedSampler{threshold=0000002af31dc8, ratio=0.99999999}"); } @Test void halfProbability() { Sampler sampler = new ElasticSampler.Builder().withProbability(0.5).build(); - assertThat(sampler.getDescription()).isEqualTo("ComposableTraceIdRatioBasedSampler{threshold=8, ratio=0.5}"); + assertThat(sampler.getDescription()) + .isEqualTo("ComposableTraceIdRatioBasedSampler{threshold=8, ratio=0.5}"); } @Test void offProbability() { Sampler sampler = new ElasticSampler.Builder().withProbability(0.0).build(); - assertThat(sampler.getDescription()).isEqualTo("ComposableTraceIdRatioBasedSampler{threshold=max, ratio=0.0}"); + assertThat(sampler.getDescription()) + .isEqualTo("ComposableTraceIdRatioBasedSampler{threshold=max, ratio=0.0}"); } @Test void ignoreUrlPath() { - Sampler sampler = new ElasticSampler.Builder().withIgnoredUrlPatterns(Collections.singletonList("/health/*")).build(); + Sampler sampler = + new ElasticSampler.Builder() + .withIgnoredUrlPatterns(Collections.singletonList("/health/*")) + .build(); checkSampling(sampler, Attributes.empty(), RECORD_AND_SAMPLE); checkSampling(sampler, Attributes.of(URL_PATH, "/health/"), DROP); checkSampling(sampler, Attributes.of(URL_PATH, "/health/test"), DROP); @@ -56,19 +82,26 @@ void ignoreUrlPath() { @Test void ignoreUserAgent() { - Sampler sampler = new ElasticSampler.Builder().withIgnoredUserAgentPatterns( - Arrays.asList("curl*", "*Curly")).build(); + Sampler sampler = + new ElasticSampler.Builder() + .withIgnoredUserAgentPatterns(Arrays.asList("curl*", "*Curly")) + .build(); checkSampling(sampler, Attributes.empty(), RECORD_AND_SAMPLE); checkSampling(sampler, Attributes.of(USER_AGENT_ORIGINAL, "curl"), DROP); checkSampling(sampler, Attributes.of(USER_AGENT_ORIGINAL, "HappyCurly"), DROP); checkSampling(sampler, Attributes.of(USER_AGENT_ORIGINAL, "CURL"), RECORD_AND_SAMPLE); } - private static void checkSampling(Sampler sampler, Attributes attributes, - SamplingDecision expectedDecision) { - SamplingResult samplingResult = sampler.shouldSample(Context.root(), TraceId.getInvalid(), "name", null, - attributes, - Collections.emptyList()); + private static void checkSampling( + Sampler sampler, Attributes attributes, SamplingDecision expectedDecision) { + SamplingResult samplingResult = + sampler.shouldSample( + Context.root(), + TraceId.getInvalid(), + "name", + null, + attributes, + Collections.emptyList()); assertThat(samplingResult.getDecision()).isEqualTo(expectedDecision); } @@ -78,5 +111,4 @@ void singleGlobalBuilderInstance() { ElasticSampler.Builder second = ElasticSampler.INSTANCE.toBuilder(); assertThat(first).isSameAs(second); } - } From 9e6f9b5795004891ae396ee6682e989f1d0eeeb1 Mon Sep 17 00:00:00 2001 From: Sylvain Juge <763082+SylvainJuge@users.noreply.github.com> Date: Thu, 15 Jan 2026 14:58:46 +0100 Subject: [PATCH 08/12] fix a few things --- .../otel/dynamicconfig/CentralConfig.java | 40 +++++++++++++++++-- .../elastic/otel/sampling/ElasticSampler.java | 10 ++++- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java b/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java index 6538ab84..9f34d1f1 100644 --- a/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java +++ b/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java @@ -29,7 +29,9 @@ import java.text.MessageFormat; import java.time.Duration; import java.time.format.DateTimeParseException; +import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; @@ -151,7 +153,9 @@ public static class Configs { new LoggingLevel(), new SamplingRate(), new InferSpans(), - new PollingInterval()) + new PollingInterval(), + new HttpExcludeUrls(), + new HttpExcludeUserAgents()) .collect(Collectors.toMap(ConfigOption::getConfigName, option -> option)); } @@ -199,9 +203,9 @@ public abstract static class ConfigOption { protected final String configName; protected final String defaultConfigStringValue; - protected ConfigOption(String configName1, String defaultConfigStringValue1) { - configName = configName1; - defaultConfigStringValue = defaultConfigStringValue1; + protected ConfigOption(String configName, String defaultConfigValue) { + this.configName = configName; + this.defaultConfigStringValue = defaultConfigValue; } public String getConfigName() { @@ -373,4 +377,32 @@ void update(String configurationValue, OpampManager opampManager) } } } + + public static final class HttpExcludeUrls extends ConfigOption { + HttpExcludeUrls() { + super("http_ignore_urls", ""); + } + + @Override + void update(String configurationValue, OpampManager opampManager) + throws IllegalArgumentException { + + List patterns = Arrays.asList(configurationValue.split(",")); + ElasticSampler.INSTANCE.toBuilder().withIgnoredUrlPatterns(patterns).buildAndSetGlobal(); + } + } + + public static final class HttpExcludeUserAgents extends ConfigOption { + HttpExcludeUserAgents() { + super("http_ignore_user_agents", ""); + } + + @Override + void update(String configurationValue, OpampManager opampManager) + throws IllegalArgumentException { + + List patterns = Arrays.asList(configurationValue.split(",")); + ElasticSampler.INSTANCE.toBuilder().withIgnoredUserAgentPatterns(patterns).buildAndSetGlobal(); + } + } } diff --git a/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java b/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java index d28ee56e..2981f974 100644 --- a/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java +++ b/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java @@ -35,6 +35,7 @@ import java.util.Collections; import java.util.List; import java.util.function.Predicate; +import java.util.logging.Level; import java.util.logging.Logger; public class ElasticSampler implements Sampler { @@ -170,7 +171,14 @@ public boolean matches( if (value == null) { return false; } - return predicate.test(value); + boolean result = predicate.test(value); + if (logger.isLoggable(Level.FINE)) { + // note: matching on a key means that the sampling intent will be applied, + logger.log(Level.FINE, + "matching on '" + attributeKey + "' with value '" + value + "' result: " + result); + } + + return result; } @Override From 808d62c604d5933b710ba7ca92fe95a88bd32e48 Mon Sep 17 00:00:00 2001 From: Sylvain Juge <763082+SylvainJuge@users.noreply.github.com> Date: Thu, 15 Jan 2026 14:59:49 +0100 Subject: [PATCH 09/12] code cleanup --- .../java/co/elastic/otel/dynamicconfig/CentralConfig.java | 4 +++- .../main/java/co/elastic/otel/sampling/ElasticSampler.java | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java b/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java index 9f34d1f1..7ac23328 100644 --- a/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java +++ b/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java @@ -402,7 +402,9 @@ void update(String configurationValue, OpampManager opampManager) throws IllegalArgumentException { List patterns = Arrays.asList(configurationValue.split(",")); - ElasticSampler.INSTANCE.toBuilder().withIgnoredUserAgentPatterns(patterns).buildAndSetGlobal(); + ElasticSampler.INSTANCE.toBuilder() + .withIgnoredUserAgentPatterns(patterns) + .buildAndSetGlobal(); } } } diff --git a/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java b/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java index 2981f974..0b6f396f 100644 --- a/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java +++ b/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java @@ -174,7 +174,8 @@ public boolean matches( boolean result = predicate.test(value); if (logger.isLoggable(Level.FINE)) { // note: matching on a key means that the sampling intent will be applied, - logger.log(Level.FINE, + logger.log( + Level.FINE, "matching on '" + attributeKey + "' with value '" + value + "' result: " + result); } From bfc7f515095db7ba1c7ff0a405510b9b0ae97a11 Mon Sep 17 00:00:00 2001 From: Sylvain Juge <763082+SylvainJuge@users.noreply.github.com> Date: Thu, 15 Jan 2026 14:59:59 +0100 Subject: [PATCH 10/12] make opamp client less chatty in logs --- .../elastic/otel/dynamicconfig/internal/OpampManager.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/custom/src/main/java/co/elastic/otel/dynamicconfig/internal/OpampManager.java b/custom/src/main/java/co/elastic/otel/dynamicconfig/internal/OpampManager.java index abed7d05..f7348f21 100644 --- a/custom/src/main/java/co/elastic/otel/dynamicconfig/internal/OpampManager.java +++ b/custom/src/main/java/co/elastic/otel/dynamicconfig/internal/OpampManager.java @@ -165,17 +165,17 @@ private Map parseCentralConfiguration(ByteString centralConfig) @Override public void onConnect(OpampClient client) { - logger.log(Level.INFO, "onConnect({0})", client); + logger.log(Level.FINE, "onConnect({0})", client); } @Override public void onConnectFailed(OpampClient client, @Nullable Throwable throwable) { - logger.log(Level.INFO, "onConnect({0}, {1})", new Object[] {client, throwable}); + logger.log(Level.FINE, "onConnect({0}, {1})", new Object[] {client, throwable}); } @Override public void onErrorResponse(OpampClient client, @Nonnull ServerErrorResponse errorResponse) { - logger.log(Level.INFO, "onErrorResponse({0}, {1})", new Object[] {client, errorResponse}); + logger.log(Level.FINE, "onErrorResponse({0}, {1})", new Object[] {client, errorResponse}); } } From af1f655e61e8791d0f1381a3c7e880ce15b17bed Mon Sep 17 00:00:00 2001 From: Sylvain Juge <763082+SylvainJuge@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:53:06 +0100 Subject: [PATCH 11/12] enhance tests with multiple attributes --- .../elastic/otel/sampling/ElasticSampler.java | 27 ++++++++--- .../otel/sampling/ElasticSamplerTest.java | 47 ++++++++++++++----- 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java b/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java index 0b6f396f..abfc5f9e 100644 --- a/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java +++ b/custom/src/main/java/co/elastic/otel/sampling/ElasticSampler.java @@ -32,6 +32,8 @@ import io.opentelemetry.sdk.trace.samplers.SamplingResult; import io.opentelemetry.semconv.UrlAttributes; import io.opentelemetry.semconv.UserAgentAttributes; +import io.opentelemetry.semconv.incubating.UserAgentIncubatingAttributes; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.function.Predicate; @@ -109,16 +111,29 @@ Sampler build() { ComposableRuleBasedSamplerBuilder ruleBuilder = ComposableSampler.ruleBasedBuilder(); if (!ignoredUrlPatterns.isEmpty()) { - ruleBuilder.add( - valueMatching(UrlAttributes.URL_PATH, ignoredUrlPatterns), - ComposableSampler.alwaysOff()); + Arrays.asList( + UrlAttributes.URL_PATH, // stable + UrlAttributes.URL_FULL, // stable + AttributeKey.stringKey("http.url") // legacy (deprecated) + ) + .forEach( + key -> + ruleBuilder.add( + valueMatching(key, ignoredUrlPatterns), ComposableSampler.alwaysOff())); rulesCount++; } if (!ignoredUserAgentPatterns.isEmpty()) { - ruleBuilder.add( - valueMatching(UserAgentAttributes.USER_AGENT_ORIGINAL, ignoredUserAgentPatterns), - ComposableSampler.alwaysOff()); + Arrays.asList( + UserAgentAttributes.USER_AGENT_ORIGINAL, // stable + UserAgentIncubatingAttributes.USER_AGENT_NAME, // incubating + AttributeKey.stringKey("http.user_agent") // legacy (deprecated) + ) + .forEach( + key -> + ruleBuilder.add( + valueMatching(key, ignoredUserAgentPatterns), + ComposableSampler.alwaysOff())); rulesCount++; } diff --git a/custom/src/test/java/co/elastic/otel/sampling/ElasticSamplerTest.java b/custom/src/test/java/co/elastic/otel/sampling/ElasticSamplerTest.java index 96e51128..f6c500a5 100644 --- a/custom/src/test/java/co/elastic/otel/sampling/ElasticSamplerTest.java +++ b/custom/src/test/java/co/elastic/otel/sampling/ElasticSamplerTest.java @@ -24,15 +24,22 @@ import static io.opentelemetry.semconv.UserAgentAttributes.USER_AGENT_ORIGINAL; import static org.assertj.core.api.Assertions.assertThat; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.TraceId; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.trace.samplers.Sampler; import io.opentelemetry.sdk.trace.samplers.SamplingDecision; import io.opentelemetry.sdk.trace.samplers.SamplingResult; +import io.opentelemetry.semconv.UrlAttributes; +import io.opentelemetry.semconv.incubating.UserAgentIncubatingAttributes; import java.util.Arrays; import java.util.Collections; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; class ElasticSamplerTest { @@ -68,28 +75,44 @@ void offProbability() { .isEqualTo("ComposableTraceIdRatioBasedSampler{threshold=max, ratio=0.0}"); } - @Test - void ignoreUrlPath() { + @ParameterizedTest + @MethodSource("urlPathKeys") + void ignoreUrlPath(AttributeKey key) { Sampler sampler = new ElasticSampler.Builder() .withIgnoredUrlPatterns(Collections.singletonList("/health/*")) .build(); checkSampling(sampler, Attributes.empty(), RECORD_AND_SAMPLE); - checkSampling(sampler, Attributes.of(URL_PATH, "/health/"), DROP); - checkSampling(sampler, Attributes.of(URL_PATH, "/health/test"), DROP); - checkSampling(sampler, Attributes.of(URL_PATH, "/healthcheck"), RECORD_AND_SAMPLE); + checkSampling(sampler, Attributes.of(key, "/health/"), DROP); + checkSampling(sampler, Attributes.of(key, "/health/test"), DROP); + checkSampling(sampler, Attributes.of(key, "/healthcheck"), RECORD_AND_SAMPLE); } - @Test - void ignoreUserAgent() { + public static Stream urlPathKeys() { + return Stream.of( + Arguments.of(URL_PATH), + Arguments.of(UrlAttributes.URL_FULL), + Arguments.of(AttributeKey.stringKey("http.url"))); + } + + @ParameterizedTest + @MethodSource("userAgentKeys") + void ignoreUserAgent(AttributeKey key) { Sampler sampler = new ElasticSampler.Builder() .withIgnoredUserAgentPatterns(Arrays.asList("curl*", "*Curly")) .build(); checkSampling(sampler, Attributes.empty(), RECORD_AND_SAMPLE); - checkSampling(sampler, Attributes.of(USER_AGENT_ORIGINAL, "curl"), DROP); - checkSampling(sampler, Attributes.of(USER_AGENT_ORIGINAL, "HappyCurly"), DROP); - checkSampling(sampler, Attributes.of(USER_AGENT_ORIGINAL, "CURL"), RECORD_AND_SAMPLE); + checkSampling(sampler, Attributes.of(key, "curl"), DROP); + checkSampling(sampler, Attributes.of(key, "HappyCurly"), DROP); + checkSampling(sampler, Attributes.of(key, "CURL"), RECORD_AND_SAMPLE); + } + + public static Stream userAgentKeys() { + return Stream.of( + Arguments.of(USER_AGENT_ORIGINAL), + Arguments.of(UserAgentIncubatingAttributes.USER_AGENT_NAME), + Arguments.of(AttributeKey.stringKey("http.user_agent"))); } private static void checkSampling( @@ -102,7 +125,9 @@ private static void checkSampling( null, attributes, Collections.emptyList()); - assertThat(samplingResult.getDecision()).isEqualTo(expectedDecision); + assertThat(samplingResult.getDecision()) + .describedAs("sampling decision for attributes " + attributes) + .isEqualTo(expectedDecision); } @Test From c4e3f483e4d5b7bbd30fd522cef945835c62cb65 Mon Sep 17 00:00:00 2001 From: Sylvain Juge <763082+SylvainJuge@users.noreply.github.com> Date: Mon, 19 Jan 2026 14:04:00 +0100 Subject: [PATCH 12/12] simplify dependencies --- custom/build.gradle.kts | 5 ----- .../java/co/elastic/otel/dynamicconfig/CentralConfig.java | 1 + gradle/libs.versions.toml | 2 -- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/custom/build.gradle.kts b/custom/build.gradle.kts index 2538ae5c..f133470f 100644 --- a/custom/build.gradle.kts +++ b/custom/build.gradle.kts @@ -29,11 +29,6 @@ dependencies { compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-tooling") compileOnly(libs.bundles.semconv) - implementation(libs.contribConsistentSampling) { - // exclude transitive dependency as it's provided through agent packaging - exclude(group = "io.opentelemetry", module = "opentelemetry-sdk-trace") - exclude(group = "io.opentelemetry", module = "opentelemetry-sdk-extension-autoconfigure-spi") - } implementation(libs.contribSpanStacktrace) { // exclude transitive dependency as it's provided through agent packaging exclude(group = "io.opentelemetry", module = "opentelemetry-sdk") diff --git a/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java b/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java index 7ac23328..f8e0eb75 100644 --- a/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java +++ b/custom/src/main/java/co/elastic/otel/dynamicconfig/CentralConfig.java @@ -335,6 +335,7 @@ public static final class SamplingRate extends ConfigOption { void update(String configurationValue, OpampManager opampManager) throws IllegalArgumentException { if (!ConfigLoggingAgentListener.getEnableDynamicSamplingRate()) { + // TODO: why do we see this log message on startup ? logger.warning("ignoring \"sampling_rate\" because non-default sampler in use"); return; } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3b3800db..529e136d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,13 +31,11 @@ opentelemetryInstrumentationAlphaBom = { group = "io.opentelemetry.instrumentati opentelemetryProto = { group = "io.opentelemetry.proto", name = "opentelemetry-proto", version.ref = "opentelemetryProto" } -contribConsistentSampling = { group = "io.opentelemetry.contrib", name = "opentelemetry-consistent-sampling", version.ref = "opentelemetryContribAlpha" } contribResources = { group = "io.opentelemetry.contrib", name = "opentelemetry-resource-providers", version.ref = "opentelemetryContribAlpha" } contribSpanStacktrace = { group = "io.opentelemetry.contrib", name = "opentelemetry-span-stacktrace", version.ref = "opentelemetryContribAlpha" } contribInferredSpans = { group = "io.opentelemetry.contrib", name = "opentelemetry-inferred-spans", version.ref = "opentelemetryContribAlpha" } contribRuntimeAttach = { group = "io.opentelemetry.contrib", name = "opentelemetry-runtime-attach-core", version.ref = "opentelemetryContribAlpha" } opentelemetry-opamp = { module = "io.opentelemetry.contrib:opentelemetry-opamp-client", version.ref = "opentelemetryContribAlpha" } -contribSamplers = { module = "io.opentelemetry.contrib:opentelemetry-samplers", version.ref = "opentelemetryContribAlpha" } opentelemetrySemconv = { group = "io.opentelemetry.semconv", name = "opentelemetry-semconv", version.ref = "opentelemetrySemconv" } opentelemetrySemconvIncubating = { group = "io.opentelemetry.semconv", name = "opentelemetry-semconv-incubating", version.ref = "opentelemetrySemconvAlpha" }