diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 220e2a953b..a84ad5b5a0 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -33,6 +33,7 @@ endif::[]
Note: this may change your service names if you relied on the auto-discovery that uses the name of the jar file. If that jar file also contains an `Implementation-Title` attribute in the `MANIFEST.MF` file, the latter will take precedence.
* When the `MANIFEST.MF` of the main jar contains the `Implementation-Version` attribute, it is used as the default service version (except for application servers) - {pull}1922[#1922]
* Added support for overwritting the service version per classloader - {pull}1726[#1726]
+* Support for the Java LDAP client - {pull}2355[#2355]
[float]
===== Bug fixes
diff --git a/apm-agent-core/src/test/resources/json-specs/span_types.json b/apm-agent-core/src/test/resources/json-specs/span_types.json
index 193489a7a7..06a5e6be34 100644
--- a/apm-agent-core/src/test/resources/json-specs/span_types.json
+++ b/apm-agent-core/src/test/resources/json-specs/span_types.json
@@ -225,6 +225,12 @@
"ruby",
"java"
]
+ },
+ "ldap": {
+ "__description": "LDAP client",
+ "__used_by": [
+ "java"
+ ]
}
}
},
diff --git a/apm-agent-plugins/apm-java-ldap-plugin/pom.xml b/apm-agent-plugins/apm-java-ldap-plugin/pom.xml
new file mode 100644
index 0000000000..b95665785c
--- /dev/null
+++ b/apm-agent-plugins/apm-java-ldap-plugin/pom.xml
@@ -0,0 +1,28 @@
+
+
+ 4.0.0
+
+
+ apm-agent-plugins
+ co.elastic.apm
+ 1.28.5-SNAPSHOT
+
+
+ apm-java-ldap-plugin
+ ${project.groupId}:${project.artifactId}
+
+
+ ${project.basedir}/../..
+ true
+
+
+
+
+ com.unboundid
+ unboundid-ldapsdk
+ 6.0.3
+ test
+
+
+
diff --git a/apm-agent-plugins/apm-java-ldap-plugin/src/main/java/co/elastic/apm/agent/java_ldap/LdapClientAdvice.java b/apm-agent-plugins/apm-java-ldap-plugin/src/main/java/co/elastic/apm/agent/java_ldap/LdapClientAdvice.java
new file mode 100644
index 0000000000..c092cb24aa
--- /dev/null
+++ b/apm-agent-plugins/apm-java-ldap-plugin/src/main/java/co/elastic/apm/agent/java_ldap/LdapClientAdvice.java
@@ -0,0 +1,71 @@
+/*
+ * 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.apm.agent.java_ldap;
+
+import co.elastic.apm.agent.impl.ElasticApmTracer;
+import co.elastic.apm.agent.impl.GlobalTracer;
+import co.elastic.apm.agent.impl.transaction.AbstractSpan;
+import co.elastic.apm.agent.impl.transaction.Outcome;
+import co.elastic.apm.agent.impl.transaction.Span;
+import com.sun.jndi.ldap.Connection;
+import com.sun.jndi.ldap.LdapResult;
+import net.bytebuddy.asm.Advice;
+import net.bytebuddy.implementation.bytecode.assign.Assigner;
+
+import javax.annotation.Nullable;
+
+public class LdapClientAdvice {
+
+ private static final ElasticApmTracer tracer = GlobalTracer.requireTracerImpl();
+
+ @Advice.OnMethodEnter(suppress = Throwable.class, inline = false)
+ public static Object onEnter(@Advice.Origin("#m") String methodName, @Advice.FieldValue(value = "conn", typing = Assigner.Typing.DYNAMIC) Connection connection) {
+ AbstractSpan> parent = tracer.getActive();
+ if (parent == null) {
+ return null;
+ }
+
+ Span span = parent.createExitSpan();
+ if (span == null) {
+ return null;
+ }
+
+ span.appendToName("LDAP ").appendToName(methodName)
+ .withType("external")
+ .withSubtype("ldap");
+
+ if (connection != null) {
+ span.getContext()
+ .getDestination().withAddress(connection.host).withPort(connection.port)
+ .getService().getResource().append(connection.host).append(":").append(connection.port);
+ }
+
+ return span.activate();
+ }
+
+ @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false)
+ public static void onExit(@Advice.Enter @Nullable Object spanObj, @Nullable @Advice.Return LdapResult ldapResult, @Nullable @Advice.Thrown Throwable t) {
+ Span span = (Span) spanObj;
+ if (span != null) {
+ span.withOutcome((ldapResult != null && ldapResult.status == 0 /* LDAP_SUCCESS */) ? Outcome.SUCCESS : Outcome.FAILURE)
+ .captureException(t)
+ .deactivate().end();
+ }
+ }
+}
diff --git a/apm-agent-plugins/apm-java-ldap-plugin/src/main/java/co/elastic/apm/agent/java_ldap/LdapClientInstrumentation.java b/apm-agent-plugins/apm-java-ldap-plugin/src/main/java/co/elastic/apm/agent/java_ldap/LdapClientInstrumentation.java
new file mode 100644
index 0000000000..edf5bec64d
--- /dev/null
+++ b/apm-agent-plugins/apm-java-ldap-plugin/src/main/java/co/elastic/apm/agent/java_ldap/LdapClientInstrumentation.java
@@ -0,0 +1,59 @@
+/*
+ * 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.apm.agent.java_ldap;
+
+import co.elastic.apm.agent.bci.TracerAwareInstrumentation;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+
+public class LdapClientInstrumentation extends TracerAwareInstrumentation {
+
+ @Override
+ public ElementMatcher super TypeDescription> getTypeMatcher() {
+ return named("com.sun.jndi.ldap.LdapClient");
+ }
+
+ @Override
+ public ElementMatcher super MethodDescription> getMethodMatcher() {
+ return named("authenticate")
+ .or(named("add"))
+ .or(named("compare"))
+ .or(named("delete"))
+ .or(named("extendedOp"))
+ .or(named("moddn"))
+ .or(named("modify"))
+ .or(named("search"));
+ }
+
+ @Override
+ public String getAdviceClassName() {
+ return "co.elastic.apm.agent.java_ldap.LdapClientAdvice";
+ }
+
+ @Override
+ public Collection getInstrumentationGroupNames() {
+ return Collections.singletonList("java-ldap");
+ }
+}
diff --git a/apm-agent-plugins/apm-java-ldap-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation b/apm-agent-plugins/apm-java-ldap-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation
new file mode 100644
index 0000000000..4f5d5f9bf4
--- /dev/null
+++ b/apm-agent-plugins/apm-java-ldap-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation
@@ -0,0 +1 @@
+co.elastic.apm.agent.java_ldap.LdapClientInstrumentation
diff --git a/apm-agent-plugins/apm-java-ldap-plugin/src/test/java/co/elastic/apm/agent/java_ldap/LdapClientAdviceTest.java b/apm-agent-plugins/apm-java-ldap-plugin/src/test/java/co/elastic/apm/agent/java_ldap/LdapClientAdviceTest.java
new file mode 100644
index 0000000000..b6872f740d
--- /dev/null
+++ b/apm-agent-plugins/apm-java-ldap-plugin/src/test/java/co/elastic/apm/agent/java_ldap/LdapClientAdviceTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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.apm.agent.java_ldap;
+
+import co.elastic.apm.agent.AbstractInstrumentationTest;
+import co.elastic.apm.agent.impl.transaction.Outcome;
+import co.elastic.apm.agent.impl.transaction.Span;
+import co.elastic.apm.agent.impl.transaction.Transaction;
+import co.elastic.apm.agent.testutils.TestPort;
+import com.unboundid.ldap.listener.InMemoryDirectoryServer;
+import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
+import com.unboundid.ldap.listener.InMemoryListenerConfig;
+import com.unboundid.ldif.LDIFReader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import javax.naming.Context;
+import javax.naming.directory.InitialDirContext;
+import java.util.Hashtable;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LdapClientAdviceTest extends AbstractInstrumentationTest {
+
+ private static InMemoryDirectoryServer ldapServer;
+
+ @BeforeAll
+ static void startServer() throws Exception {
+ InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=example,dc=com");
+ config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("test", TestPort.getAvailableRandomPort()));
+
+ ldapServer = new InMemoryDirectoryServer(config);
+ ldapServer.importFromLDIF(true, new LDIFReader(LdapClientAdviceTest.class.getResourceAsStream("/test.ldif")));
+ ldapServer.startListening();
+ }
+
+ @AfterAll
+ static void stopServer() {
+ ldapServer.shutDown(true);
+ }
+
+ @Test
+ void testSuccessfulAuthentication() throws Exception {
+ Hashtable environment = getEnvironment();
+
+ Transaction transaction = startTestRootTransaction();
+ try {
+ new InitialDirContext(environment).close();
+ } catch (Exception ignored) {
+ } finally {
+ transaction.deactivate().end();
+ }
+
+ List spans = reporter.getSpans();
+ assertThat(spans.size()).isEqualTo(1);
+
+ assertSpan(spans.get(0), "authenticate", Outcome.SUCCESS);
+ }
+
+ @Test
+ void testUnsuccessfulAuthentication() {
+ Hashtable environment = getEnvironment();
+ environment.put(Context.SECURITY_CREDENTIALS, "wrong password");
+
+ Transaction transaction = startTestRootTransaction();
+ try {
+ new InitialDirContext(environment).close();
+ } catch (Exception ignored) {
+ ignored.printStackTrace();
+ } finally {
+ transaction.deactivate().end();
+ }
+
+ List spans = reporter.getSpans();
+ assertThat(spans.size()).isEqualTo(1);
+
+ assertSpan(spans.get(0), "authenticate", Outcome.FAILURE);
+ }
+
+ @Test
+ void testSearch() {
+ Hashtable environment = getEnvironment();
+
+ Transaction transaction = startTestRootTransaction();
+ try {
+ InitialDirContext context = new InitialDirContext(environment);
+ context.search("dc=example,dc=com", "(&(objectClass=person)(uid=tobiasstadler))", null);
+ context.close();
+ } catch (Exception ignored) {
+ } finally {
+ transaction.deactivate().end();
+ }
+
+ List spans = reporter.getSpans();
+ assertThat(spans.size()).isEqualTo(2);
+
+ assertSpan(spans.get(0), "authenticate", Outcome.SUCCESS);
+ assertSpan(spans.get(1), "search", Outcome.SUCCESS);
+ }
+
+ private static Hashtable getEnvironment() {
+ Hashtable environment = new Hashtable<>();
+
+ environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ environment.put(Context.PROVIDER_URL, "ldap://localhost:" + ldapServer.getListenPort());
+ environment.put(Context.SECURITY_AUTHENTICATION, "simple");
+ environment.put(Context.SECURITY_PRINCIPAL, "cn=Tobias Stadler,ou=Users,dc=example,dc=com");
+ environment.put(Context.SECURITY_CREDENTIALS, "123456");
+
+ return environment;
+ }
+
+ static void assertSpan(Span span, String method, Outcome outcome) {
+ assertThat(span.getNameAsString()).isEqualTo("LDAP " + method);
+ assertThat(span.getType()).isEqualTo("external");
+ assertThat(span.getSubtype()).isEqualTo("ldap");
+ assertThat(span.getOutcome()).isEqualTo(outcome);
+ assertThat(span.getContext().getDestination().getAddress().toString()).isEqualTo("localhost");
+ assertThat(span.getContext().getDestination().getPort()).isEqualTo(ldapServer.getListenPort());
+ }
+}
diff --git a/apm-agent-plugins/apm-java-ldap-plugin/src/test/resources/test.ldif b/apm-agent-plugins/apm-java-ldap-plugin/src/test/resources/test.ldif
new file mode 100644
index 0000000000..01bf3ed14c
--- /dev/null
+++ b/apm-agent-plugins/apm-java-ldap-plugin/src/test/resources/test.ldif
@@ -0,0 +1,24 @@
+dn: dc=example,dc=com
+objectClass: domain
+objectClass: top
+dc: example
+
+dn: ou=Users,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: top
+ou: Users
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: top
+ou: Groups
+
+dn: cn=Tobias Stadler,ou=Users,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+objectClass: top
+cn: Tobias Stadler
+sn: Stadler
+uid: tobiasstadler
+userPassword: 123456
diff --git a/apm-agent-plugins/pom.xml b/apm-agent-plugins/pom.xml
index 2319871c24..ccffba1871 100644
--- a/apm-agent-plugins/pom.xml
+++ b/apm-agent-plugins/pom.xml
@@ -69,6 +69,7 @@
apm-jaxws-plugin-jakartaee-test
apm-jaxrs-plugin-jakartaee-test
apm-scheduled-annotation-plugin-jakartaee-test
+ apm-java-ldap-plugin
diff --git a/apm-agent/pom.xml b/apm-agent/pom.xml
index 6c35eb77f6..332acb9b7a 100644
--- a/apm-agent/pom.xml
+++ b/apm-agent/pom.xml
@@ -316,6 +316,11 @@
apm-awslambda-plugin
${project.version}
+
+ ${project.groupId}
+ apm-java-ldap-plugin
+ ${project.version}
+
diff --git a/docs/configuration.asciidoc b/docs/configuration.asciidoc
index 0ad9bfefbf..9f5c8b2506 100644
--- a/docs/configuration.asciidoc
+++ b/docs/configuration.asciidoc
@@ -718,7 +718,7 @@ you should add an additional entry to this list (make sure to also include the d
==== `enable_instrumentations` (added[1.28.0])
A list of instrumentations which should be selectively enabled.
-Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `javalin`, `jax-rs`, `jax-ws`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-ecs`, `log4j2-ecs`, `log4j2-error`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`.
+Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `java-ldap`, `javalin`, `jax-rs`, `jax-ws`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-ecs`, `log4j2-ecs`, `log4j2-error`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`.
When set to non-empty value, only listed instrumentations will be enabled if they are not disabled through <> or <>.
When not set or empty (default), all instrumentations enabled by default will be enabled unless they are disabled through <> or <>.
@@ -746,7 +746,7 @@ NOTE: Changing this value at runtime can slow down the application temporarily.
==== `disable_instrumentations` (added[1.0.0,Changing this value at runtime is possible since version 1.15.0])
A list of instrumentations which should be disabled.
-Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `javalin`, `jax-rs`, `jax-ws`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-ecs`, `log4j2-ecs`, `log4j2-error`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`.
+Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `java-ldap`, `javalin`, `jax-rs`, `jax-ws`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-ecs`, `log4j2-ecs`, `log4j2-error`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`.
For version `1.25.0` and later, use <> to enable experimental instrumentations.
NOTE: Changing this value at runtime can slow down the application temporarily.
@@ -2999,7 +2999,7 @@ The default unit for this option is `ms`.
# sanitize_field_names=password,passwd,pwd,secret,*key,*token*,*session*,*credit*,*card*,*auth*,set-cookie
# A list of instrumentations which should be selectively enabled.
-# Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `javalin`, `jax-rs`, `jax-ws`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-ecs`, `log4j2-ecs`, `log4j2-error`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`.
+# Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `java-ldap`, `javalin`, `jax-rs`, `jax-ws`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-ecs`, `log4j2-ecs`, `log4j2-error`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`.
# When set to non-empty value, only listed instrumentations will be enabled if they are not disabled through <> or <>.
# When not set or empty (default), all instrumentations enabled by default will be enabled unless they are disabled through <> or <>.
#
@@ -3012,7 +3012,7 @@ The default unit for this option is `ms`.
# enable_instrumentations=
# A list of instrumentations which should be disabled.
-# Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `javalin`, `jax-rs`, `jax-ws`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-ecs`, `log4j2-ecs`, `log4j2-error`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`.
+# Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `java-ldap`, `javalin`, `jax-rs`, `jax-ws`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-ecs`, `log4j2-ecs`, `log4j2-error`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`.
# For version `1.25.0` and later, use <> to enable experimental instrumentations.
#
# NOTE: Changing this value at runtime can slow down the application temporarily.
diff --git a/docs/supported-technologies.asciidoc b/docs/supported-technologies.asciidoc
index 8e23da8836..56cf37ffea 100644
--- a/docs/supported-technologies.asciidoc
+++ b/docs/supported-technologies.asciidoc
@@ -336,6 +336,10 @@ The spans are named after the schema ` `, for example `GET elastic
|
| 1.25.0
+|LdapClient
+|
+|
+| 1.29.0
|===