diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetAttributesExtractor.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetAttributesExtractor.java index f0cf96e95544..1a19303c5587 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetAttributesExtractor.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetAttributesExtractor.java @@ -22,8 +22,15 @@ public abstract class NetAttributesExtractor @Override protected final void onStart(AttributesBuilder attributes, REQUEST request) { set(attributes, SemanticAttributes.NET_TRANSPORT, transport(request)); - set(attributes, SemanticAttributes.NET_PEER_IP, peerIp(request, null)); - set(attributes, SemanticAttributes.NET_PEER_NAME, peerName(request, null)); + + String peerIp = peerIp(request, null); + String peerName = peerName(request, null); + + if (peerName != null && !peerName.equals(peerIp)) { + set(attributes, SemanticAttributes.NET_PEER_NAME, peerName); + } + set(attributes, SemanticAttributes.NET_PEER_IP, peerIp); + Integer peerPort = peerPort(request, null); if (peerPort != null) { set(attributes, SemanticAttributes.NET_PEER_PORT, (long) peerPort); @@ -36,8 +43,15 @@ protected final void onEnd( REQUEST request, @Nullable RESPONSE response, @Nullable Throwable error) { - set(attributes, SemanticAttributes.NET_PEER_IP, peerIp(request, response)); - set(attributes, SemanticAttributes.NET_PEER_NAME, peerName(request, response)); + + String peerIp = peerIp(request, response); + String peerName = peerName(request, response); + + if (peerName != null && !peerName.equals(peerIp)) { + set(attributes, SemanticAttributes.NET_PEER_NAME, peerName); + } + set(attributes, SemanticAttributes.NET_PEER_IP, peerIp); + Integer peerPort = peerPort(request, response); if (peerPort != null) { set(attributes, SemanticAttributes.NET_PEER_PORT, (long) peerPort); diff --git a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetAttributesExtractorTest.java b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetAttributesExtractorTest.java index e90c72a1e0f8..f7db8ba52615 100644 --- a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetAttributesExtractorTest.java +++ b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetAttributesExtractorTest.java @@ -87,4 +87,40 @@ void normal() { entry(SemanticAttributes.NET_PEER_PORT, 42L), entry(SemanticAttributes.NET_PEER_IP, "4.3.2.1")); } + + @Test + public void doesNotSetDuplicateAttributes() { + // given + Map request = new HashMap<>(); + request.put("transport", "TCP"); + request.put("peerName", "1.2.3.4"); + request.put("peerIp", "1.2.3.4"); + request.put("peerPort", "123"); + + Map response = new HashMap<>(); + response.put("peerName", "4.3.2.1"); + response.put("peerPort", "42"); + response.put("peerIp", "4.3.2.1"); + + TestNetAttributesExtractor extractor = new TestNetAttributesExtractor(); + + // when + AttributesBuilder startAttributes = Attributes.builder(); + extractor.onStart(startAttributes, request); + + AttributesBuilder endAttributes = Attributes.builder(); + extractor.onEnd(endAttributes, request, response, null); + + // then + assertThat(startAttributes.build()) + .containsOnly( + entry(SemanticAttributes.NET_TRANSPORT, "TCP"), + entry(SemanticAttributes.NET_PEER_PORT, 123L), + entry(SemanticAttributes.NET_PEER_IP, "1.2.3.4")); + + assertThat(endAttributes.build()) + .containsOnly( + entry(SemanticAttributes.NET_PEER_PORT, 42L), + entry(SemanticAttributes.NET_PEER_IP, "4.3.2.1")); + } } diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/AbstractClientInstrumentation.java b/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/AbstractClientInstrumentation.java index 3b315143f5b2..d441df9acca4 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/AbstractClientInstrumentation.java +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/AbstractClientInstrumentation.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_0; import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext; -import static io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticsearchTransportClientTracer.tracer; +import static io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_0.Elasticsearch5TransportSingletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; @@ -15,6 +15,8 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticTransportRequest; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.TransportActionListener; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -51,27 +53,38 @@ public static void onEnter( @Advice.Argument(1) ActionRequest actionRequest, @Advice.Local("otelContext") Context context, @Advice.Local("otelScope") Scope scope, + @Advice.Local("otelRequest") ElasticTransportRequest transportRequest, @Advice.Argument(value = 2, readOnly = false) ActionListener actionListener) { + transportRequest = ElasticTransportRequest.create(action, actionRequest); Context parentContext = currentContext(); - context = tracer().startSpan(parentContext, null, action); + if (!instrumenter().shouldStart(parentContext, transportRequest)) { + return; + } + + context = instrumenter().start(parentContext, transportRequest); scope = context.makeCurrent(); - tracer().onRequest(context, action.getClass(), actionRequest.getClass()); actionListener = - new TransportActionListener<>(actionRequest, actionListener, context, parentContext); + new TransportActionListener<>( + instrumenter(), transportRequest, actionListener, context, parentContext); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( @Advice.Thrown Throwable throwable, @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { + @Advice.Local("otelScope") Scope scope, + @Advice.Local("otelRequest") ElasticTransportRequest transportRequest) { + if (scope == null) { + return; + } + scope.close(); if (throwable != null) { - tracer().endExceptionally(context, throwable); + instrumenter().end(context, transportRequest, null, throwable); } } } diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/Elasticsearch5TransportExperimentalAttributesExtractor.java b/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/Elasticsearch5TransportExperimentalAttributesExtractor.java new file mode 100644 index 000000000000..d7a7d4c032d3 --- /dev/null +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/Elasticsearch5TransportExperimentalAttributesExtractor.java @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_0; + +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticTransportRequest; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticsearchTransportExperimentalAttributesExtractor; +import org.elasticsearch.action.DocumentRequest; + +public class Elasticsearch5TransportExperimentalAttributesExtractor + extends ElasticsearchTransportExperimentalAttributesExtractor { + + @Override + protected void onStart(AttributesBuilder attributes, ElasticTransportRequest transportRequest) { + super.onStart(attributes, transportRequest); + + Object request = transportRequest.getRequest(); + if (request instanceof DocumentRequest) { + DocumentRequest req = (DocumentRequest) request; + attributes.put("elasticsearch.request.write.type", req.type()); + attributes.put("elasticsearch.request.write.routing", req.routing()); + } + } +} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/Elasticsearch5TransportSingletons.java b/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/Elasticsearch5TransportSingletons.java new file mode 100644 index 000000000000..bde95679c6ad --- /dev/null +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/Elasticsearch5TransportSingletons.java @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_0; + +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticTransportNetAttributesExtractor; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticTransportRequest; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticsearchTransportInstrumenterFactory; +import org.elasticsearch.action.ActionResponse; + +public final class Elasticsearch5TransportSingletons { + + private static final Instrumenter INSTRUMENTER = + ElasticsearchTransportInstrumenterFactory.create( + "io.opentelemetry.elasticsearch-transport-5.0", + new Elasticsearch5TransportExperimentalAttributesExtractor(), + new ElasticTransportNetAttributesExtractor()); + + public static Instrumenter instrumenter() { + return INSTRUMENTER; + } + + private Elasticsearch5TransportSingletons() {} +} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/TransportActionListener.java b/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/TransportActionListener.java deleted file mode 100644 index 62c1cfff6ace..000000000000 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_0/TransportActionListener.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_0; - -import static io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticsearchTransportClientTracer.tracer; - -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.config.Config; -import io.opentelemetry.instrumentation.api.tracer.net.NetPeerAttributes; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; -import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.ActionRequest; -import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.action.DocumentRequest; -import org.elasticsearch.action.IndicesRequest; -import org.elasticsearch.action.bulk.BulkShardResponse; -import org.elasticsearch.action.get.GetResponse; -import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.action.support.broadcast.BroadcastResponse; -import org.elasticsearch.action.support.nodes.BaseNodesResponse; -import org.elasticsearch.action.support.replication.ReplicationResponse; - -public class TransportActionListener implements ActionListener { - - private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES = - Config.get() - .getBoolean("otel.instrumentation.elasticsearch.experimental-span-attributes", false); - - private final ActionListener listener; - private final Context context; - private final Context parentContext; - - public TransportActionListener( - ActionRequest actionRequest, - ActionListener listener, - Context context, - Context parentContext) { - this.listener = listener; - this.context = context; - this.parentContext = parentContext; - onRequest(actionRequest); - } - - private void onRequest(ActionRequest request) { - if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) { - Span span = Span.fromContext(context); - - if (request instanceof IndicesRequest) { - IndicesRequest req = (IndicesRequest) request; - String[] indices = req.indices(); - if (indices != null && indices.length > 0) { - span.setAttribute("elasticsearch.request.indices", String.join(",", indices)); - } - } - if (request instanceof SearchRequest) { - SearchRequest req = (SearchRequest) request; - String[] types = req.types(); - if (types != null && types.length > 0) { - span.setAttribute("elasticsearch.request.search.types", String.join(",", types)); - } - } - if (request instanceof DocumentRequest) { - DocumentRequest req = (DocumentRequest) request; - span.setAttribute("elasticsearch.request.write.type", req.type()); - span.setAttribute("elasticsearch.request.write.routing", req.routing()); - } - } - } - - @Override - public void onResponse(T response) { - Span span = Span.fromContext(context); - - if (response.remoteAddress() != null) { - NetPeerAttributes.INSTANCE.setNetPeer( - span, response.remoteAddress().getHost(), response.remoteAddress().getAddress()); - span.setAttribute( - SemanticAttributes.NET_PEER_PORT, (long) response.remoteAddress().getPort()); - } - - if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) { - if (response instanceof GetResponse) { - GetResponse resp = (GetResponse) response; - span.setAttribute("elasticsearch.type", resp.getType()); - span.setAttribute("elasticsearch.id", resp.getId()); - span.setAttribute("elasticsearch.version", resp.getVersion()); - } - - if (response instanceof BroadcastResponse) { - BroadcastResponse resp = (BroadcastResponse) response; - span.setAttribute("elasticsearch.shard.broadcast.total", resp.getTotalShards()); - span.setAttribute("elasticsearch.shard.broadcast.successful", resp.getSuccessfulShards()); - span.setAttribute("elasticsearch.shard.broadcast.failed", resp.getFailedShards()); - } - - if (response instanceof ReplicationResponse) { - ReplicationResponse resp = (ReplicationResponse) response; - span.setAttribute("elasticsearch.shard.replication.total", resp.getShardInfo().getTotal()); - span.setAttribute( - "elasticsearch.shard.replication.successful", resp.getShardInfo().getSuccessful()); - span.setAttribute( - "elasticsearch.shard.replication.failed", resp.getShardInfo().getFailed()); - } - - if (response instanceof IndexResponse) { - span.setAttribute( - "elasticsearch.response.status", ((IndexResponse) response).status().getStatus()); - } - - if (response instanceof BulkShardResponse) { - BulkShardResponse resp = (BulkShardResponse) response; - span.setAttribute("elasticsearch.shard.bulk.id", resp.getShardId().getId()); - span.setAttribute("elasticsearch.shard.bulk.index", resp.getShardId().getIndexName()); - } - - if (response instanceof BaseNodesResponse) { - BaseNodesResponse resp = (BaseNodesResponse) response; - if (resp.hasFailures()) { - span.setAttribute("elasticsearch.node.failures", resp.failures().size()); - } - span.setAttribute("elasticsearch.node.cluster.name", resp.getClusterName().value()); - } - } - - tracer().end(context); - try (Scope ignored = parentContext.makeCurrent()) { - listener.onResponse(response); - } - } - - @Override - public void onFailure(Exception e) { - tracer().endExceptionally(context, e); - try (Scope ignored = parentContext.makeCurrent()) { - listener.onFailure(e); - } - } -} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/test/groovy/Elasticsearch5NodeClientTest.groovy b/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/test/groovy/Elasticsearch5NodeClientTest.groovy index 7229a8b708cb..5289a6aba14f 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/test/groovy/Elasticsearch5NodeClientTest.groovy +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.0/javaagent/src/test/groovy/Elasticsearch5NodeClientTest.groovy @@ -243,7 +243,7 @@ class Elasticsearch5NodeClientTest extends AbstractElasticsearchNodeClientTest { } } } - trace(3, 2) { + trace(3, 1) { span(0) { name "IndexAction" kind CLIENT @@ -260,17 +260,6 @@ class Elasticsearch5NodeClientTest extends AbstractElasticsearchNodeClientTest { "elasticsearch.shard.replication.failed" 0 } } - span(1) { - name "PutMappingAction" - kind CLIENT - childOf span(0) - attributes { - "${SemanticAttributes.DB_SYSTEM.key}" "elasticsearch" - "${SemanticAttributes.DB_OPERATION.key}" "PutMappingAction" - "elasticsearch.action" "PutMappingAction" - "elasticsearch.request" "PutMappingRequest" - } - } } trace(4, 1) { span(0) { diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/AbstractClientInstrumentation.java b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/AbstractClientInstrumentation.java index 999c1c534b69..894534576a32 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/AbstractClientInstrumentation.java +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/AbstractClientInstrumentation.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_3; import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext; -import static io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticsearchTransportClientTracer.tracer; +import static io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_3.Elasticsearch53TransportSingletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; @@ -15,6 +15,8 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticTransportRequest; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.TransportActionListener; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -51,27 +53,38 @@ public static void onEnter( @Advice.Argument(1) ActionRequest actionRequest, @Advice.Local("otelContext") Context context, @Advice.Local("otelScope") Scope scope, + @Advice.Local("otelRequest") ElasticTransportRequest transportRequest, @Advice.Argument(value = 2, readOnly = false) ActionListener actionListener) { + transportRequest = ElasticTransportRequest.create(action, actionRequest); Context parentContext = currentContext(); - context = tracer().startSpan(parentContext, null, action); + if (!instrumenter().shouldStart(parentContext, transportRequest)) { + return; + } + + context = instrumenter().start(parentContext, transportRequest); scope = context.makeCurrent(); - tracer().onRequest(context, action.getClass(), actionRequest.getClass()); actionListener = - new TransportActionListener<>(actionRequest, actionListener, context, parentContext); + new TransportActionListener<>( + instrumenter(), transportRequest, actionListener, context, parentContext); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( @Advice.Thrown Throwable throwable, @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { + @Advice.Local("otelScope") Scope scope, + @Advice.Local("otelRequest") ElasticTransportRequest transportRequest) { + if (scope == null) { + return; + } + scope.close(); if (throwable != null) { - tracer().endExceptionally(context, throwable); + instrumenter().end(context, transportRequest, null, throwable); } } } diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/Elasticsearch53TransportExperimentalAttributesExtractor.java b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/Elasticsearch53TransportExperimentalAttributesExtractor.java new file mode 100644 index 000000000000..4d6750fda8dc --- /dev/null +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/Elasticsearch53TransportExperimentalAttributesExtractor.java @@ -0,0 +1,28 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_3; + +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticTransportRequest; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticsearchTransportExperimentalAttributesExtractor; +import org.elasticsearch.action.DocWriteRequest; + +public class Elasticsearch53TransportExperimentalAttributesExtractor + extends ElasticsearchTransportExperimentalAttributesExtractor { + + @Override + protected void onStart(AttributesBuilder attributes, ElasticTransportRequest transportRequest) { + super.onStart(attributes, transportRequest); + + Object request = transportRequest.getRequest(); + if (request instanceof DocWriteRequest) { + DocWriteRequest req = (DocWriteRequest) request; + attributes.put("elasticsearch.request.write.type", req.type()); + attributes.put("elasticsearch.request.write.routing", req.routing()); + attributes.put("elasticsearch.request.write.version", req.version()); + } + } +} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/Elasticsearch53TransportSingletons.java b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/Elasticsearch53TransportSingletons.java new file mode 100644 index 000000000000..e4244824eff9 --- /dev/null +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/Elasticsearch53TransportSingletons.java @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_3; + +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticTransportNetAttributesExtractor; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticTransportRequest; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticsearchTransportInstrumenterFactory; +import org.elasticsearch.action.ActionResponse; + +public final class Elasticsearch53TransportSingletons { + + private static final Instrumenter INSTRUMENTER = + ElasticsearchTransportInstrumenterFactory.create( + "io.opentelemetry.elasticsearch-transport-5.3", + new Elasticsearch53TransportExperimentalAttributesExtractor(), + new ElasticTransportNetAttributesExtractor()); + + public static Instrumenter instrumenter() { + return INSTRUMENTER; + } + + private Elasticsearch53TransportSingletons() {} +} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/TransportActionListener.java b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/TransportActionListener.java deleted file mode 100644 index 45e7c66e3c0e..000000000000 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v5_3/TransportActionListener.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_3; - -import static io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticsearchTransportClientTracer.tracer; - -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.config.Config; -import io.opentelemetry.instrumentation.api.tracer.net.NetPeerAttributes; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; -import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.ActionRequest; -import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.action.DocWriteRequest; -import org.elasticsearch.action.IndicesRequest; -import org.elasticsearch.action.bulk.BulkShardResponse; -import org.elasticsearch.action.get.GetResponse; -import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.action.support.broadcast.BroadcastResponse; -import org.elasticsearch.action.support.nodes.BaseNodesResponse; -import org.elasticsearch.action.support.replication.ReplicationResponse; - -public class TransportActionListener implements ActionListener { - - private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES = - Config.get() - .getBoolean("otel.instrumentation.elasticsearch.experimental-span-attributes", false); - - private final ActionListener listener; - private final Context context; - private final Context parentContext; - - public TransportActionListener( - ActionRequest actionRequest, - ActionListener listener, - Context context, - Context parentContext) { - this.listener = listener; - this.context = context; - this.parentContext = parentContext; - onRequest(actionRequest); - } - - private void onRequest(ActionRequest request) { - if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) { - Span span = Span.fromContext(context); - - if (request instanceof IndicesRequest) { - IndicesRequest req = (IndicesRequest) request; - String[] indices = req.indices(); - if (indices != null && indices.length > 0) { - span.setAttribute("elasticsearch.request.indices", String.join(",", indices)); - } - } - if (request instanceof SearchRequest) { - SearchRequest req = (SearchRequest) request; - String[] types = req.types(); - if (types != null && types.length > 0) { - span.setAttribute("elasticsearch.request.search.types", String.join(",", types)); - } - } - if (request instanceof DocWriteRequest) { - DocWriteRequest req = (DocWriteRequest) request; - span.setAttribute("elasticsearch.request.write.type", req.type()); - span.setAttribute("elasticsearch.request.write.routing", req.routing()); - span.setAttribute("elasticsearch.request.write.version", req.version()); - } - } - } - - @Override - public void onResponse(T response) { - Span span = Span.fromContext(context); - - if (response.remoteAddress() != null) { - NetPeerAttributes.INSTANCE.setNetPeer( - span, response.remoteAddress().getHost(), response.remoteAddress().getAddress()); - span.setAttribute( - SemanticAttributes.NET_PEER_PORT, (long) response.remoteAddress().getPort()); - } - - if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) { - if (response instanceof GetResponse) { - GetResponse resp = (GetResponse) response; - span.setAttribute("elasticsearch.type", resp.getType()); - span.setAttribute("elasticsearch.id", resp.getId()); - span.setAttribute("elasticsearch.version", resp.getVersion()); - } - - if (response instanceof BroadcastResponse) { - BroadcastResponse resp = (BroadcastResponse) response; - span.setAttribute("elasticsearch.shard.broadcast.total", resp.getTotalShards()); - span.setAttribute("elasticsearch.shard.broadcast.successful", resp.getSuccessfulShards()); - span.setAttribute("elasticsearch.shard.broadcast.failed", resp.getFailedShards()); - } - - if (response instanceof ReplicationResponse) { - ReplicationResponse resp = (ReplicationResponse) response; - span.setAttribute("elasticsearch.shard.replication.total", resp.getShardInfo().getTotal()); - span.setAttribute( - "elasticsearch.shard.replication.successful", resp.getShardInfo().getSuccessful()); - span.setAttribute( - "elasticsearch.shard.replication.failed", resp.getShardInfo().getFailed()); - } - - if (response instanceof IndexResponse) { - span.setAttribute( - "elasticsearch.response.status", ((IndexResponse) response).status().getStatus()); - } - - if (response instanceof BulkShardResponse) { - BulkShardResponse resp = (BulkShardResponse) response; - span.setAttribute("elasticsearch.shard.bulk.id", resp.getShardId().getId()); - span.setAttribute("elasticsearch.shard.bulk.index", resp.getShardId().getIndexName()); - } - - if (response instanceof BaseNodesResponse) { - BaseNodesResponse resp = (BaseNodesResponse) response; - if (resp.hasFailures()) { - span.setAttribute("elasticsearch.node.failures", resp.failures().size()); - } - span.setAttribute("elasticsearch.node.cluster.name", resp.getClusterName().value()); - } - } - - tracer().end(context); - try (Scope ignored = parentContext.makeCurrent()) { - listener.onResponse(response); - } - } - - @Override - public void onFailure(Exception e) { - tracer().endExceptionally(context, e); - try (Scope ignored = parentContext.makeCurrent()) { - listener.onFailure(e); - } - } -} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/Elasticsearch53NodeClientTest.groovy b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/Elasticsearch53NodeClientTest.groovy index d3e5a6543e0d..238c8c94344a 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/Elasticsearch53NodeClientTest.groovy +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/Elasticsearch53NodeClientTest.groovy @@ -246,7 +246,7 @@ class Elasticsearch53NodeClientTest extends AbstractElasticsearchNodeClientTest } } } - trace(3, 2) { + trace(3, 1) { span(0) { name "IndexAction" kind CLIENT @@ -264,17 +264,6 @@ class Elasticsearch53NodeClientTest extends AbstractElasticsearchNodeClientTest "elasticsearch.shard.replication.failed" 0 } } - span(1) { - name "PutMappingAction" - kind CLIENT - childOf span(0) - attributes { - "${SemanticAttributes.DB_SYSTEM.key}" "elasticsearch" - "${SemanticAttributes.DB_OPERATION.key}" "PutMappingAction" - "elasticsearch.action" "PutMappingAction" - "elasticsearch.request" "PutMappingRequest" - } - } } trace(4, 1) { span(0) { diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/springdata/Elasticsearch53SpringRepositoryTest.groovy b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/springdata/Elasticsearch53SpringRepositoryTest.groovy index 0b1fc6955b6a..7d07427c0c50 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/springdata/Elasticsearch53SpringRepositoryTest.groovy +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/springdata/Elasticsearch53SpringRepositoryTest.groovy @@ -115,7 +115,7 @@ class Elasticsearch53SpringRepositoryTest extends AgentInstrumentationSpecificat and: assertTraces(1) { - trace(0, 4) { + trace(0, 3) { span(0) { name "ElasticsearchRepository.index" kind INTERNAL @@ -141,17 +141,6 @@ class Elasticsearch53SpringRepositoryTest extends AgentInstrumentationSpecificat } } span(2) { - name "PutMappingAction" - kind CLIENT - childOf span(1) - attributes { - "${SemanticAttributes.DB_SYSTEM.key}" "elasticsearch" - "${SemanticAttributes.DB_OPERATION.key}" "PutMappingAction" - "elasticsearch.action" "PutMappingAction" - "elasticsearch.request" "PutMappingRequest" - } - } - span(3) { name "RefreshAction" kind CLIENT childOf span(0) diff --git a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/springdata/Elasticsearch53SpringTemplateTest.groovy b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/springdata/Elasticsearch53SpringTemplateTest.groovy index 406ae1672483..7e1b94a70f48 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/springdata/Elasticsearch53SpringTemplateTest.groovy +++ b/instrumentation/elasticsearch/elasticsearch-transport-5.3/javaagent/src/test/groovy/springdata/Elasticsearch53SpringTemplateTest.groovy @@ -180,7 +180,7 @@ class Elasticsearch53SpringTemplateTest extends AgentInstrumentationSpecificatio } } } - trace(3, 2) { + trace(3, 1) { span(0) { name "IndexAction" kind CLIENT @@ -198,17 +198,6 @@ class Elasticsearch53SpringTemplateTest extends AgentInstrumentationSpecificatio "elasticsearch.shard.replication.total" 2 } } - span(1) { - name "PutMappingAction" - kind CLIENT - childOf span(0) - attributes { - "${SemanticAttributes.DB_SYSTEM.key}" "elasticsearch" - "${SemanticAttributes.DB_OPERATION.key}" "PutMappingAction" - "elasticsearch.action" "PutMappingAction" - "elasticsearch.request" "PutMappingRequest" - } - } } trace(4, 1) { span(0) { diff --git a/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/AbstractClientInstrumentation.java b/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/AbstractClientInstrumentation.java index 48b74d137001..80cc636f6e9a 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/AbstractClientInstrumentation.java +++ b/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/AbstractClientInstrumentation.java @@ -6,7 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v6_0; import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext; -import static io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticsearchTransportClientTracer.tracer; +import static io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v6_0.Elasticsearch6TransportSingletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; @@ -16,6 +16,8 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticTransportRequest; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.TransportActionListener; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -55,26 +57,38 @@ public static void onEnter( @Advice.Argument(1) ActionRequest actionRequest, @Advice.Local("otelContext") Context context, @Advice.Local("otelScope") Scope scope, + @Advice.Local("otelRequest") ElasticTransportRequest transportRequest, @Advice.Argument(value = 2, readOnly = false) ActionListener actionListener) { + transportRequest = ElasticTransportRequest.create(action, actionRequest); Context parentContext = currentContext(); - context = tracer().startSpan(parentContext, null, action); + if (!instrumenter().shouldStart(parentContext, transportRequest)) { + return; + } + + context = instrumenter().start(parentContext, transportRequest); scope = context.makeCurrent(); - tracer().onRequest(context, action.getClass(), actionRequest.getClass()); + actionListener = - new TransportActionListener<>(actionRequest, actionListener, context, parentContext); + new TransportActionListener<>( + instrumenter(), transportRequest, actionListener, context, parentContext); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( @Advice.Thrown Throwable throwable, @Advice.Local("otelContext") Context context, - @Advice.Local("otelScope") Scope scope) { + @Advice.Local("otelScope") Scope scope, + @Advice.Local("otelRequest") ElasticTransportRequest transportRequest) { + if (scope == null) { + return; + } + scope.close(); if (throwable != null) { - tracer().endExceptionally(context, throwable); + instrumenter().end(context, transportRequest, null, throwable); } } } diff --git a/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/Elasticsearch6TransportExperimentalAttributesExtractor.java b/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/Elasticsearch6TransportExperimentalAttributesExtractor.java new file mode 100644 index 000000000000..be28a1a1c154 --- /dev/null +++ b/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/Elasticsearch6TransportExperimentalAttributesExtractor.java @@ -0,0 +1,28 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v6_0; + +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticTransportRequest; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticsearchTransportExperimentalAttributesExtractor; +import org.elasticsearch.action.DocWriteRequest; + +public class Elasticsearch6TransportExperimentalAttributesExtractor + extends ElasticsearchTransportExperimentalAttributesExtractor { + + @Override + protected void onStart(AttributesBuilder attributes, ElasticTransportRequest transportRequest) { + super.onStart(attributes, transportRequest); + + Object request = transportRequest.getRequest(); + if (request instanceof DocWriteRequest) { + DocWriteRequest req = (DocWriteRequest) request; + attributes.put("elasticsearch.request.write.type", req.type()); + attributes.put("elasticsearch.request.write.routing", req.routing()); + attributes.put("elasticsearch.request.write.version", req.version()); + } + } +} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/Elasticsearch6TransportNetAttributesExtractor.java b/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/Elasticsearch6TransportNetAttributesExtractor.java new file mode 100644 index 000000000000..324e4be35ad6 --- /dev/null +++ b/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/Elasticsearch6TransportNetAttributesExtractor.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v6_0; + +import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetAttributesExtractor; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticTransportRequest; +import java.net.InetSocketAddress; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.elasticsearch.action.ActionResponse; + +public class Elasticsearch6TransportNetAttributesExtractor + extends InetSocketAddressNetAttributesExtractor { + @Override + public @Nullable String transport(ElasticTransportRequest elasticTransportRequest) { + return null; + } + + @Override + public @Nullable InetSocketAddress getAddress( + ElasticTransportRequest elasticTransportRequest, @Nullable ActionResponse response) { + if (response != null && response.remoteAddress() != null) { + return response.remoteAddress().address(); + } + return null; + } +} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/Elasticsearch6TransportSingletons.java b/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/Elasticsearch6TransportSingletons.java new file mode 100644 index 000000000000..47ff83056ab7 --- /dev/null +++ b/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/Elasticsearch6TransportSingletons.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v6_0; + +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticTransportRequest; +import io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticsearchTransportInstrumenterFactory; +import org.elasticsearch.action.ActionResponse; + +public final class Elasticsearch6TransportSingletons { + + private static final Instrumenter INSTRUMENTER = + ElasticsearchTransportInstrumenterFactory.create( + "io.opentelemetry.elasticsearch-transport-6.0", + new Elasticsearch6TransportExperimentalAttributesExtractor(), + new Elasticsearch6TransportNetAttributesExtractor()); + + public static Instrumenter instrumenter() { + return INSTRUMENTER; + } + + private Elasticsearch6TransportSingletons() {} +} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/TransportActionListener.java b/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/TransportActionListener.java deleted file mode 100644 index 2dcaa74d36e0..000000000000 --- a/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/v6_0/TransportActionListener.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v6_0; - -import static io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.ElasticsearchTransportClientTracer.tracer; - -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; -import io.opentelemetry.instrumentation.api.config.Config; -import io.opentelemetry.instrumentation.api.tracer.net.NetPeerAttributes; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; -import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.ActionRequest; -import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.action.DocWriteRequest; -import org.elasticsearch.action.IndicesRequest; -import org.elasticsearch.action.bulk.BulkShardResponse; -import org.elasticsearch.action.get.GetResponse; -import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.action.support.broadcast.BroadcastResponse; -import org.elasticsearch.action.support.nodes.BaseNodesResponse; -import org.elasticsearch.action.support.replication.ReplicationResponse; - -/** - * Most of this class is identical to version 5's instrumentation, but they changed an interface to - * an abstract class, so the bytecode isn't directly compatible. - */ -public class TransportActionListener implements ActionListener { - - private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES = - Config.get() - .getBoolean("otel.instrumentation.elasticsearch.experimental-span-attributes", false); - - private final ActionListener listener; - private final Context context; - private final Context parentContext; - - public TransportActionListener( - ActionRequest actionRequest, - ActionListener listener, - Context context, - Context parentContext) { - this.listener = listener; - this.context = context; - this.parentContext = parentContext; - onRequest(actionRequest); - } - - private void onRequest(ActionRequest request) { - if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) { - Span span = Span.fromContext(context); - if (request instanceof IndicesRequest) { - IndicesRequest req = (IndicesRequest) request; - String[] indices = req.indices(); - if (indices != null && indices.length > 0) { - span.setAttribute("elasticsearch.request.indices", String.join(",", indices)); - } - } - if (request instanceof SearchRequest) { - SearchRequest req = (SearchRequest) request; - String[] types = req.types(); - if (types != null && types.length > 0) { - span.setAttribute("elasticsearch.request.search.types", String.join(",", types)); - } - } - if (request instanceof DocWriteRequest) { - DocWriteRequest req = (DocWriteRequest) request; - span.setAttribute("elasticsearch.request.write.type", req.type()); - span.setAttribute("elasticsearch.request.write.routing", req.routing()); - span.setAttribute("elasticsearch.request.write.version", req.version()); - } - } - } - - @Override - public void onResponse(T response) { - Span span = Span.fromContext(context); - - if (response.remoteAddress() != null) { - NetPeerAttributes.INSTANCE.setNetPeer( - span, - response.remoteAddress().address().getHostName(), - response.remoteAddress().getAddress()); - span.setAttribute( - SemanticAttributes.NET_PEER_PORT, (long) response.remoteAddress().getPort()); - } - - if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) { - if (response instanceof GetResponse) { - GetResponse resp = (GetResponse) response; - span.setAttribute("elasticsearch.type", resp.getType()); - span.setAttribute("elasticsearch.id", resp.getId()); - span.setAttribute("elasticsearch.version", resp.getVersion()); - } - - if (response instanceof BroadcastResponse) { - BroadcastResponse resp = (BroadcastResponse) response; - span.setAttribute("elasticsearch.shard.broadcast.total", resp.getTotalShards()); - span.setAttribute("elasticsearch.shard.broadcast.successful", resp.getSuccessfulShards()); - span.setAttribute("elasticsearch.shard.broadcast.failed", resp.getFailedShards()); - } - - if (response instanceof ReplicationResponse) { - ReplicationResponse resp = (ReplicationResponse) response; - span.setAttribute("elasticsearch.shard.replication.total", resp.getShardInfo().getTotal()); - span.setAttribute( - "elasticsearch.shard.replication.successful", resp.getShardInfo().getSuccessful()); - span.setAttribute( - "elasticsearch.shard.replication.failed", resp.getShardInfo().getFailed()); - } - - if (response instanceof IndexResponse) { - span.setAttribute( - "elasticsearch.response.status", ((IndexResponse) response).status().getStatus()); - } - - if (response instanceof BulkShardResponse) { - BulkShardResponse resp = (BulkShardResponse) response; - span.setAttribute("elasticsearch.shard.bulk.id", resp.getShardId().getId()); - span.setAttribute("elasticsearch.shard.bulk.index", resp.getShardId().getIndexName()); - } - - if (response instanceof BaseNodesResponse) { - BaseNodesResponse resp = (BaseNodesResponse) response; - if (resp.hasFailures()) { - span.setAttribute("elasticsearch.node.failures", resp.failures().size()); - } - span.setAttribute("elasticsearch.node.cluster.name", resp.getClusterName().value()); - } - } - - tracer().end(context); - try (Scope ignored = parentContext.makeCurrent()) { - listener.onResponse(response); - } - } - - @Override - public void onFailure(Exception e) { - tracer().endExceptionally(context, e); - try (Scope ignored = parentContext.makeCurrent()) { - listener.onFailure(e); - } - } -} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/test/groovy/Elasticsearch6NodeClientTest.groovy b/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/test/groovy/Elasticsearch6NodeClientTest.groovy index 74e769851b63..760e7e144560 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/test/groovy/Elasticsearch6NodeClientTest.groovy +++ b/instrumentation/elasticsearch/elasticsearch-transport-6.0/javaagent/src/test/groovy/Elasticsearch6NodeClientTest.groovy @@ -227,7 +227,7 @@ class Elasticsearch6NodeClientTest extends AbstractElasticsearchNodeClientTest { } } } - trace(2, 2) { + trace(2, 1) { span(0) { name "IndexAction" kind CLIENT @@ -245,17 +245,6 @@ class Elasticsearch6NodeClientTest extends AbstractElasticsearchNodeClientTest { "elasticsearch.shard.replication.failed" 0 } } - span(1) { - name ~/(Auto)?PutMappingAction/ - kind CLIENT - childOf span(0) - attributes { - "${SemanticAttributes.DB_SYSTEM.key}" "elasticsearch" - "${SemanticAttributes.DB_OPERATION.key}" ~/(Auto)?PutMappingAction/ - "elasticsearch.action" ~/(Auto)?PutMappingAction/ - "elasticsearch.request" "PutMappingRequest" - } - } } trace(3, 1) { span(0) { diff --git a/instrumentation/elasticsearch/elasticsearch-transport-common/library/build.gradle.kts b/instrumentation/elasticsearch/elasticsearch-transport-common/library/build.gradle.kts index bc6096a35ef7..4de4f968d2f4 100644 --- a/instrumentation/elasticsearch/elasticsearch-transport-common/library/build.gradle.kts +++ b/instrumentation/elasticsearch/elasticsearch-transport-common/library/build.gradle.kts @@ -2,4 +2,9 @@ plugins { id("otel.library-instrumentation") } -// No dependencies, elasticsearch library not actually used here. +dependencies { + compileOnly("org.elasticsearch.client:transport:5.0.0") + + compileOnly("com.google.auto.value:auto-value-annotations") + annotationProcessor("com.google.auto.value:auto-value") +} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticTransportNetAttributesExtractor.java b/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticTransportNetAttributesExtractor.java new file mode 100644 index 000000000000..ec60e438038c --- /dev/null +++ b/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticTransportNetAttributesExtractor.java @@ -0,0 +1,45 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport; + +import io.opentelemetry.instrumentation.api.instrumenter.net.NetAttributesExtractor; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.elasticsearch.action.ActionResponse; + +public class ElasticTransportNetAttributesExtractor + extends NetAttributesExtractor { + @Override + public @Nullable String transport(ElasticTransportRequest elasticTransportRequest) { + return null; + } + + @Override + public @Nullable String peerName( + ElasticTransportRequest elasticTransportRequest, @Nullable ActionResponse response) { + if (response != null && response.remoteAddress() != null) { + return response.remoteAddress().getHost(); + } + return null; + } + + @Override + public @Nullable Integer peerPort( + ElasticTransportRequest elasticTransportRequest, @Nullable ActionResponse response) { + if (response != null && response.remoteAddress() != null) { + return response.remoteAddress().getPort(); + } + return null; + } + + @Override + public @Nullable String peerIp( + ElasticTransportRequest elasticTransportRequest, @Nullable ActionResponse response) { + if (response != null && response.remoteAddress() != null) { + return response.remoteAddress().getAddress(); + } + return null; + } +} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticTransportRequest.java b/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticTransportRequest.java new file mode 100644 index 000000000000..75b5c9d79100 --- /dev/null +++ b/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticTransportRequest.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport; + +import com.google.auto.value.AutoValue; + +@AutoValue +public abstract class ElasticTransportRequest { + + public static ElasticTransportRequest create(Object action, Object request) { + return new AutoValue_ElasticTransportRequest(action, request); + } + + public abstract Object getAction(); + + public abstract Object getRequest(); +} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportAttributesExtractor.java b/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportAttributesExtractor.java new file mode 100644 index 000000000000..75f887e1d131 --- /dev/null +++ b/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportAttributesExtractor.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport; + +import io.opentelemetry.instrumentation.api.instrumenter.db.DbAttributesExtractor; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.elasticsearch.action.ActionResponse; + +final class ElasticsearchTransportAttributesExtractor + extends DbAttributesExtractor { + @Override + protected String system(ElasticTransportRequest s) { + return SemanticAttributes.DbSystemValues.ELASTICSEARCH; + } + + @Override + protected @Nullable String user(ElasticTransportRequest s) { + return null; + } + + @Override + protected @Nullable String name(ElasticTransportRequest s) { + return null; + } + + @Override + protected @Nullable String connectionString(ElasticTransportRequest s) { + return null; + } + + @Override + protected @Nullable String statement(ElasticTransportRequest s) { + return null; + } + + @Override + protected @Nullable String operation(ElasticTransportRequest action) { + return action.getAction().getClass().getSimpleName(); + } +} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportClientTracer.java b/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportClientTracer.java deleted file mode 100644 index 8b5cf3b2088d..000000000000 --- a/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportClientTracer.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport; - -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.config.Config; -import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer; -import io.opentelemetry.instrumentation.api.tracer.net.NetPeerAttributes; -import java.net.InetSocketAddress; - -public class ElasticsearchTransportClientTracer extends DatabaseClientTracer { - - private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES = - Config.get() - .getBoolean("otel.instrumentation.elasticsearch.experimental-span-attributes", false); - - private static final ElasticsearchTransportClientTracer TRACER = - new ElasticsearchTransportClientTracer(); - - private ElasticsearchTransportClientTracer() { - super(NetPeerAttributes.INSTANCE); - } - - public static ElasticsearchTransportClientTracer tracer() { - return TRACER; - } - - public void onRequest(Context context, Class action, Class request) { - if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) { - Span span = Span.fromContext(context); - span.setAttribute("elasticsearch.action", action.getSimpleName()); - span.setAttribute("elasticsearch.request", request.getSimpleName()); - } - } - - @Override - protected String sanitizeStatement(Object action) { - return action.getClass().getSimpleName(); - } - - @Override - protected String dbSystem(Void connection) { - return "elasticsearch"; - } - - @Override - protected InetSocketAddress peerAddress(Void connection) { - return null; - } - - @Override - protected String dbOperation(Void connection, Object action, String operation) { - return operation; - } - - @Override - protected String getInstrumentationName() { - return "io.opentelemetry.elasticsearch-transport-common"; - } -} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportExperimentalAttributesExtractor.java b/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportExperimentalAttributesExtractor.java new file mode 100644 index 000000000000..ccf056e4c5e0 --- /dev/null +++ b/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportExperimentalAttributesExtractor.java @@ -0,0 +1,92 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport; + +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.action.IndicesRequest; +import org.elasticsearch.action.bulk.BulkShardResponse; +import org.elasticsearch.action.get.GetResponse; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.support.broadcast.BroadcastResponse; +import org.elasticsearch.action.support.nodes.BaseNodesResponse; +import org.elasticsearch.action.support.replication.ReplicationResponse; + +public class ElasticsearchTransportExperimentalAttributesExtractor + extends AttributesExtractor { + @Override + protected void onStart(AttributesBuilder attributes, ElasticTransportRequest transportRequest) { + Object request = transportRequest.getRequest(); + attributes.put("elasticsearch.action", transportRequest.getAction().getClass().getSimpleName()); + attributes.put("elasticsearch.request", request.getClass().getSimpleName()); + + if (request instanceof IndicesRequest) { + IndicesRequest req = (IndicesRequest) request; + String[] indices = req.indices(); + if (indices != null && indices.length > 0) { + attributes.put("elasticsearch.request.indices", String.join(",", indices)); + } + } + if (request instanceof SearchRequest) { + SearchRequest req = (SearchRequest) request; + String[] types = req.types(); + if (types != null && types.length > 0) { + attributes.put("elasticsearch.request.search.types", String.join(",", types)); + } + } + } + + @Override + protected void onEnd( + AttributesBuilder attributes, + ElasticTransportRequest request, + ActionResponse response, + @Nullable Throwable error) { + if (response instanceof GetResponse) { + GetResponse resp = (GetResponse) response; + attributes.put("elasticsearch.type", resp.getType()); + attributes.put("elasticsearch.id", resp.getId()); + attributes.put("elasticsearch.version", resp.getVersion()); + } + + if (response instanceof BroadcastResponse) { + BroadcastResponse resp = (BroadcastResponse) response; + attributes.put("elasticsearch.shard.broadcast.total", resp.getTotalShards()); + attributes.put("elasticsearch.shard.broadcast.successful", resp.getSuccessfulShards()); + attributes.put("elasticsearch.shard.broadcast.failed", resp.getFailedShards()); + } + + if (response instanceof ReplicationResponse) { + ReplicationResponse resp = (ReplicationResponse) response; + attributes.put("elasticsearch.shard.replication.total", resp.getShardInfo().getTotal()); + attributes.put( + "elasticsearch.shard.replication.successful", resp.getShardInfo().getSuccessful()); + attributes.put("elasticsearch.shard.replication.failed", resp.getShardInfo().getFailed()); + } + + if (response instanceof IndexResponse) { + attributes.put( + "elasticsearch.response.status", ((IndexResponse) response).status().getStatus()); + } + + if (response instanceof BulkShardResponse) { + BulkShardResponse resp = (BulkShardResponse) response; + attributes.put("elasticsearch.shard.bulk.id", resp.getShardId().getId()); + attributes.put("elasticsearch.shard.bulk.index", resp.getShardId().getIndexName()); + } + + if (response instanceof BaseNodesResponse) { + BaseNodesResponse resp = (BaseNodesResponse) response; + if (resp.hasFailures()) { + attributes.put("elasticsearch.node.failures", resp.failures().size()); + } + attributes.put("elasticsearch.node.cluster.name", resp.getClusterName().value()); + } + } +} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportInstrumenterFactory.java b/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportInstrumenterFactory.java new file mode 100644 index 000000000000..e80a8f585dfc --- /dev/null +++ b/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/ElasticsearchTransportInstrumenterFactory.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.api.config.Config; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.db.DbSpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.NetAttributesExtractor; +import org.elasticsearch.action.ActionResponse; + +public final class ElasticsearchTransportInstrumenterFactory { + private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES = + Config.get() + .getBoolean("otel.instrumentation.elasticsearch.experimental-span-attributes", false); + + public static Instrumenter create( + String instrumentationName, + AttributesExtractor experimentalAttributesExtractor, + NetAttributesExtractor netAttributesExtractor) { + + ElasticsearchTransportAttributesExtractor attributesExtractor = + new ElasticsearchTransportAttributesExtractor(); + + InstrumenterBuilder instrumenterBuilder = + Instrumenter.newBuilder( + GlobalOpenTelemetry.get(), + instrumentationName, + DbSpanNameExtractor.create(attributesExtractor)) + .addAttributesExtractor(attributesExtractor) + .addAttributesExtractor(netAttributesExtractor); + + if (CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) { + instrumenterBuilder.addAttributesExtractor(experimentalAttributesExtractor); + } + + return instrumenterBuilder.newInstrumenter(SpanKindExtractor.alwaysClient()); + } + + private ElasticsearchTransportInstrumenterFactory() {} +} diff --git a/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/TransportActionListener.java b/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/TransportActionListener.java new file mode 100644 index 000000000000..52de03f8711c --- /dev/null +++ b/instrumentation/elasticsearch/elasticsearch-transport-common/library/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/transport/TransportActionListener.java @@ -0,0 +1,50 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.ActionResponse; + +public class TransportActionListener implements ActionListener { + + private final Instrumenter instrumenter; + private final ElasticTransportRequest actionRequest; + private final ActionListener listener; + private final Context context; + private final Context parentContext; + + public TransportActionListener( + Instrumenter instrumenter, + ElasticTransportRequest actionRequest, + ActionListener listener, + Context context, + Context parentContext) { + this.instrumenter = instrumenter; + this.actionRequest = actionRequest; + this.listener = listener; + this.context = context; + this.parentContext = parentContext; + } + + @Override + public void onResponse(T response) { + instrumenter.end(context, actionRequest, response, null); + try (Scope ignored = parentContext.makeCurrent()) { + listener.onResponse(response); + } + } + + @Override + public void onFailure(Exception e) { + instrumenter.end(context, actionRequest, null, e); + try (Scope ignored = parentContext.makeCurrent()) { + listener.onFailure(e); + } + } +}