Skip to content

Commit 0d58db7

Browse files
Add xpack.apm.tracing.names.include setting for filtering (#80871)
This commit adds a dynamic cluster setting called `xpack.apm.tracing.names.include` which allows the user to filter on the names of the transactions for which tracing is enabled.
1 parent d95c634 commit 0d58db7

File tree

3 files changed

+135
-1
lines changed

3 files changed

+135
-1
lines changed

x-pack/plugin/apm-integration/src/internalClusterTest/java/org/elasticsearch/xpack/apm/ApmIT.java

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.opentelemetry.api.common.AttributeKey;
1111
import io.opentelemetry.sdk.trace.data.SpanData;
1212

13+
import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsRequest;
1314
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest;
1415
import org.elasticsearch.action.bulk.BulkRequestBuilder;
1516
import org.elasticsearch.action.index.IndexRequestBuilder;
@@ -42,11 +43,13 @@
4243
import java.util.Collections;
4344
import java.util.List;
4445
import java.util.concurrent.TimeUnit;
46+
import java.util.stream.Collectors;
4547

4648
import static java.util.stream.Collectors.toList;
4749
import static org.elasticsearch.cluster.service.MasterService.STATE_UPDATE_ACTION_NAME;
4850
import static org.elasticsearch.indices.recovery.PeerRecoverySourceService.Actions.START_RECOVERY;
4951
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
52+
import static org.hamcrest.Matchers.contains;
5053
import static org.hamcrest.Matchers.empty;
5154
import static org.hamcrest.Matchers.equalTo;
5255
import static org.hamcrest.Matchers.hasItems;
@@ -294,4 +297,101 @@ public void testDoesNotRecordSpansWhenDisabled() {
294297
.actionGet();
295298
}
296299
}
300+
301+
public void testFilterByNameGivenSingleCompleteMatch() {
302+
303+
client().admin()
304+
.cluster()
305+
.updateSettings(
306+
new ClusterUpdateSettingsRequest().persistentSettings(
307+
Settings.builder().put(APMTracer.APM_TRACING_NAMES_INCLUDE_SETTING.getKey(), "cluster:monitor/tasks/lists").build()
308+
)
309+
)
310+
.actionGet();
311+
312+
APMTracer.CAPTURING_SPAN_EXPORTER.clear();// removing start related events
313+
314+
try {
315+
client().admin().cluster().prepareListTasks().get();
316+
317+
var parentTasks = APMTracer.CAPTURING_SPAN_EXPORTER.findSpanByName("cluster:monitor/tasks/lists").collect(toList());
318+
assertThat(parentTasks, hasSize(1));
319+
320+
var childrenTasks = APMTracer.CAPTURING_SPAN_EXPORTER.findSpanByName("cluster:monitor/tasks/lists[n]").collect(toList());
321+
assertThat(childrenTasks, empty());
322+
} finally {
323+
client().admin()
324+
.cluster()
325+
.updateSettings(
326+
new ClusterUpdateSettingsRequest().persistentSettings(
327+
Settings.builder().put(APMTracer.APM_TRACING_NAMES_INCLUDE_SETTING.getKey(), (String) null).build()
328+
)
329+
)
330+
.actionGet();
331+
}
332+
}
333+
334+
public void testFilterByNameGivenSinglePattern() {
335+
336+
client().admin()
337+
.cluster()
338+
.updateSettings(
339+
new ClusterUpdateSettingsRequest().persistentSettings(
340+
Settings.builder().put(APMTracer.APM_TRACING_NAMES_INCLUDE_SETTING.getKey(), "*/tasks/lists*").build()
341+
)
342+
)
343+
.actionGet();
344+
345+
APMTracer.CAPTURING_SPAN_EXPORTER.clear();// removing start related events
346+
347+
try {
348+
client().admin().cluster().prepareListTasks().get();
349+
350+
var parentTasks = APMTracer.CAPTURING_SPAN_EXPORTER.findSpanByName("cluster:monitor/tasks/lists").collect(toList());
351+
assertThat(parentTasks, hasSize(1));
352+
353+
var childrenTasks = APMTracer.CAPTURING_SPAN_EXPORTER.findSpanByName("cluster:monitor/tasks/lists[n]").collect(toList());
354+
assertThat(childrenTasks, hasSize(internalCluster().size()));
355+
} finally {
356+
client().admin()
357+
.cluster()
358+
.updateSettings(
359+
new ClusterUpdateSettingsRequest().persistentSettings(
360+
Settings.builder().put(APMTracer.APM_TRACING_NAMES_INCLUDE_SETTING.getKey(), (String) null).build()
361+
)
362+
)
363+
.actionGet();
364+
}
365+
}
366+
367+
public void testFilterByNameGivenTwoPatterns() {
368+
369+
client().admin()
370+
.cluster()
371+
.updateSettings(
372+
new ClusterUpdateSettingsRequest().persistentSettings(
373+
Settings.builder().put(APMTracer.APM_TRACING_NAMES_INCLUDE_SETTING.getKey(), "*/tasks/lists,*/nodes/stats").build()
374+
)
375+
)
376+
.actionGet();
377+
378+
APMTracer.CAPTURING_SPAN_EXPORTER.clear();// removing start related events
379+
380+
try {
381+
client().admin().cluster().prepareListTasks().get();
382+
client().admin().cluster().nodesStats(new NodesStatsRequest()).actionGet();
383+
384+
var spans = APMTracer.CAPTURING_SPAN_EXPORTER.getCapturedSpans().stream().map(SpanData::getName).collect(Collectors.toSet());
385+
assertThat(spans, contains("cluster:monitor/nodes/stats", "cluster:monitor/tasks/lists"));
386+
} finally {
387+
client().admin()
388+
.cluster()
389+
.updateSettings(
390+
new ClusterUpdateSettingsRequest().persistentSettings(
391+
Settings.builder().put(APMTracer.APM_TRACING_NAMES_INCLUDE_SETTING.getKey(), (String) null).build()
392+
)
393+
)
394+
.actionGet();
395+
}
396+
}
297397
}

x-pack/plugin/apm-integration/src/main/java/org/elasticsearch/xpack/apm/APM.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,12 @@ public Collection<Object> createComponents(
6969

7070
@Override
7171
public List<Setting<?>> getSettings() {
72-
return List.of(APMTracer.APM_ENABLED_SETTING, APMTracer.APM_ENDPOINT_SETTING, APMTracer.APM_TOKEN_SETTING);
72+
return List.of(
73+
APMTracer.APM_ENABLED_SETTING,
74+
APMTracer.APM_ENDPOINT_SETTING,
75+
APMTracer.APM_TOKEN_SETTING,
76+
APMTracer.APM_TRACING_NAMES_INCLUDE_SETTING
77+
);
7378
}
7479

7580
public List<TransportInterceptor> getTransportInterceptors(NamedWriteableRegistry namedWriteableRegistry, ThreadContext threadContext) {

x-pack/plugin/apm-integration/src/main/java/org/elasticsearch/xpack/apm/APMTracer.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.elasticsearch.cluster.service.ClusterService;
3535
import org.elasticsearch.common.Strings;
3636
import org.elasticsearch.common.component.AbstractLifecycleComponent;
37+
import org.elasticsearch.common.regex.Regex;
3738
import org.elasticsearch.common.settings.SecureSetting;
3839
import org.elasticsearch.common.settings.SecureString;
3940
import org.elasticsearch.common.settings.Setting;
@@ -46,13 +47,15 @@
4647
import java.security.AccessController;
4748
import java.security.PrivilegedAction;
4849
import java.util.Collection;
50+
import java.util.Collections;
4951
import java.util.HashMap;
5052
import java.util.List;
5153
import java.util.Map;
5254
import java.util.Objects;
5355
import java.util.Queue;
5456
import java.util.concurrent.Semaphore;
5557
import java.util.concurrent.TimeUnit;
58+
import java.util.function.Function;
5659
import java.util.function.Predicate;
5760
import java.util.stream.Collectors;
5861
import java.util.stream.Stream;
@@ -67,6 +70,13 @@ public class APMTracer extends AbstractLifecycleComponent implements org.elastic
6770
static final Setting<Boolean> APM_ENABLED_SETTING = Setting.boolSetting("xpack.apm.tracing.enabled", false, Dynamic, NodeScope);
6871
static final Setting<SecureString> APM_ENDPOINT_SETTING = SecureSetting.secureString("xpack.apm.endpoint", null);
6972
static final Setting<SecureString> APM_TOKEN_SETTING = SecureSetting.secureString("xpack.apm.token", null);
73+
static final Setting<List<String>> APM_TRACING_NAMES_INCLUDE_SETTING = Setting.listSetting(
74+
"xpack.apm.tracing.names.include",
75+
Collections.emptyList(),
76+
Function.identity(),
77+
Dynamic,
78+
NodeScope
79+
);
7080

7181
private final Semaphore shutdownPermits = new Semaphore(Integer.MAX_VALUE);
7282
private final Map<String, Span> spans = ConcurrentCollections.newConcurrentMap();
@@ -78,6 +88,8 @@ public class APMTracer extends AbstractLifecycleComponent implements org.elastic
7888
private volatile boolean enabled;
7989
private volatile APMServices services;
8090

91+
private List<String> includeNames;
92+
8193
/** This class is required to make all open telemetry services visible at once */
8294
private static class APMServices {
8395
private final SdkTracerProvider provider;
@@ -97,7 +109,9 @@ public APMTracer(Settings settings, ThreadPool threadPool, ClusterService cluste
97109
this.endpoint = APM_ENDPOINT_SETTING.get(settings);
98110
this.token = APM_TOKEN_SETTING.get(settings);
99111
this.enabled = APM_ENABLED_SETTING.get(settings);
112+
this.includeNames = APM_TRACING_NAMES_INCLUDE_SETTING.get(settings);
100113
clusterService.getClusterSettings().addSettingsUpdateConsumer(APM_ENABLED_SETTING, this::setEnabled);
114+
clusterService.getClusterSettings().addSettingsUpdateConsumer(APM_TRACING_NAMES_INCLUDE_SETTING, this::setIncludeNames);
101115
}
102116

103117
public boolean isEnabled() {
@@ -113,6 +127,10 @@ private void setEnabled(boolean enabled) {
113127
}
114128
}
115129

130+
private void setIncludeNames(List<String> includeNames) {
131+
this.includeNames = includeNames;
132+
}
133+
116134
@Override
117135
protected void doStart() {
118136
if (enabled) {
@@ -195,6 +213,11 @@ public void onTraceStarted(Traceable traceable) {
195213
if (services == null) {
196214
return;
197215
}
216+
217+
if (isSpanNameIncluded(traceable.getSpanName()) == false) {
218+
return;
219+
}
220+
198221
spans.computeIfAbsent(traceable.getSpanId(), spanId -> {
199222
// services might be in shutdown state by this point, but this is handled by the open telemetry internally
200223
final SpanBuilder spanBuilder = services.tracer.spanBuilder(traceable.getSpanName());
@@ -245,6 +268,12 @@ public void onTraceStarted(Traceable traceable) {
245268
});
246269
}
247270

271+
private boolean isSpanNameIncluded(String name) {
272+
// Alternatively we could use automata here but it is much more complex
273+
// and it needs wrapping like done for use in the security plugin.
274+
return includeNames.isEmpty() || Regex.simpleMatch(includeNames, name);
275+
}
276+
248277
private Context getParentSpanContext(OpenTelemetry openTelemetry) {
249278
// If we already have a non-root span context that should be the parent
250279
if (Context.current() != Context.root()) {

0 commit comments

Comments
 (0)