Convert Elasticsearch Transport to Instrumenter API (#4252)
* Convert Elasticsearch Transport to Instrumenter API * Don't set duplicate attributes * Code review comments
This commit is contained in:
parent
c11b96e4d0
commit
bfeb482465
|
@ -22,8 +22,15 @@ public abstract class NetAttributesExtractor<REQUEST, RESPONSE>
|
||||||
@Override
|
@Override
|
||||||
protected final void onStart(AttributesBuilder attributes, REQUEST request) {
|
protected final void onStart(AttributesBuilder attributes, REQUEST request) {
|
||||||
set(attributes, SemanticAttributes.NET_TRANSPORT, transport(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);
|
Integer peerPort = peerPort(request, null);
|
||||||
if (peerPort != null) {
|
if (peerPort != null) {
|
||||||
set(attributes, SemanticAttributes.NET_PEER_PORT, (long) peerPort);
|
set(attributes, SemanticAttributes.NET_PEER_PORT, (long) peerPort);
|
||||||
|
@ -36,8 +43,15 @@ public abstract class NetAttributesExtractor<REQUEST, RESPONSE>
|
||||||
REQUEST request,
|
REQUEST request,
|
||||||
@Nullable RESPONSE response,
|
@Nullable RESPONSE response,
|
||||||
@Nullable Throwable error) {
|
@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);
|
Integer peerPort = peerPort(request, response);
|
||||||
if (peerPort != null) {
|
if (peerPort != null) {
|
||||||
set(attributes, SemanticAttributes.NET_PEER_PORT, (long) peerPort);
|
set(attributes, SemanticAttributes.NET_PEER_PORT, (long) peerPort);
|
||||||
|
|
|
@ -87,4 +87,40 @@ class NetAttributesExtractorTest {
|
||||||
entry(SemanticAttributes.NET_PEER_PORT, 42L),
|
entry(SemanticAttributes.NET_PEER_PORT, 42L),
|
||||||
entry(SemanticAttributes.NET_PEER_IP, "4.3.2.1"));
|
entry(SemanticAttributes.NET_PEER_IP, "4.3.2.1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doesNotSetDuplicateAttributes() {
|
||||||
|
// given
|
||||||
|
Map<String, String> 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<String, String> 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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_0;
|
package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_0;
|
||||||
|
|
||||||
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
|
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.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
@ -15,6 +15,8 @@ import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.context.Scope;
|
import io.opentelemetry.context.Scope;
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
|
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.asm.Advice;
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
@ -51,27 +53,38 @@ public class AbstractClientInstrumentation implements TypeInstrumentation {
|
||||||
@Advice.Argument(1) ActionRequest actionRequest,
|
@Advice.Argument(1) ActionRequest actionRequest,
|
||||||
@Advice.Local("otelContext") Context context,
|
@Advice.Local("otelContext") Context context,
|
||||||
@Advice.Local("otelScope") Scope scope,
|
@Advice.Local("otelScope") Scope scope,
|
||||||
|
@Advice.Local("otelRequest") ElasticTransportRequest transportRequest,
|
||||||
@Advice.Argument(value = 2, readOnly = false)
|
@Advice.Argument(value = 2, readOnly = false)
|
||||||
ActionListener<ActionResponse> actionListener) {
|
ActionListener<ActionResponse> actionListener) {
|
||||||
|
|
||||||
|
transportRequest = ElasticTransportRequest.create(action, actionRequest);
|
||||||
Context parentContext = currentContext();
|
Context parentContext = currentContext();
|
||||||
context = tracer().startSpan(parentContext, null, action);
|
if (!instrumenter().shouldStart(parentContext, transportRequest)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context = instrumenter().start(parentContext, transportRequest);
|
||||||
scope = context.makeCurrent();
|
scope = context.makeCurrent();
|
||||||
|
|
||||||
tracer().onRequest(context, action.getClass(), actionRequest.getClass());
|
|
||||||
actionListener =
|
actionListener =
|
||||||
new TransportActionListener<>(actionRequest, actionListener, context, parentContext);
|
new TransportActionListener<>(
|
||||||
|
instrumenter(), transportRequest, actionListener, context, parentContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
public static void stopSpan(
|
public static void stopSpan(
|
||||||
@Advice.Thrown Throwable throwable,
|
@Advice.Thrown Throwable throwable,
|
||||||
@Advice.Local("otelContext") Context context,
|
@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();
|
scope.close();
|
||||||
|
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
tracer().endExceptionally(context, throwable);
|
instrumenter().end(context, transportRequest, null, throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<ElasticTransportRequest, ActionResponse> INSTRUMENTER =
|
||||||
|
ElasticsearchTransportInstrumenterFactory.create(
|
||||||
|
"io.opentelemetry.elasticsearch-transport-5.0",
|
||||||
|
new Elasticsearch5TransportExperimentalAttributesExtractor(),
|
||||||
|
new ElasticTransportNetAttributesExtractor());
|
||||||
|
|
||||||
|
public static Instrumenter<ElasticTransportRequest, ActionResponse> instrumenter() {
|
||||||
|
return INSTRUMENTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Elasticsearch5TransportSingletons() {}
|
||||||
|
}
|
|
@ -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<T extends ActionResponse> implements ActionListener<T> {
|
|
||||||
|
|
||||||
private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES =
|
|
||||||
Config.get()
|
|
||||||
.getBoolean("otel.instrumentation.elasticsearch.experimental-span-attributes", false);
|
|
||||||
|
|
||||||
private final ActionListener<T> listener;
|
|
||||||
private final Context context;
|
|
||||||
private final Context parentContext;
|
|
||||||
|
|
||||||
public TransportActionListener(
|
|
||||||
ActionRequest<?> actionRequest,
|
|
||||||
ActionListener<T> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -243,7 +243,7 @@ class Elasticsearch5NodeClientTest extends AbstractElasticsearchNodeClientTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trace(3, 2) {
|
trace(3, 1) {
|
||||||
span(0) {
|
span(0) {
|
||||||
name "IndexAction"
|
name "IndexAction"
|
||||||
kind CLIENT
|
kind CLIENT
|
||||||
|
@ -260,17 +260,6 @@ class Elasticsearch5NodeClientTest extends AbstractElasticsearchNodeClientTest {
|
||||||
"elasticsearch.shard.replication.failed" 0
|
"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) {
|
trace(4, 1) {
|
||||||
span(0) {
|
span(0) {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_3;
|
package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v5_3;
|
||||||
|
|
||||||
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
|
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.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
@ -15,6 +15,8 @@ import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.context.Scope;
|
import io.opentelemetry.context.Scope;
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
|
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.asm.Advice;
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
@ -51,27 +53,38 @@ public class AbstractClientInstrumentation implements TypeInstrumentation {
|
||||||
@Advice.Argument(1) ActionRequest actionRequest,
|
@Advice.Argument(1) ActionRequest actionRequest,
|
||||||
@Advice.Local("otelContext") Context context,
|
@Advice.Local("otelContext") Context context,
|
||||||
@Advice.Local("otelScope") Scope scope,
|
@Advice.Local("otelScope") Scope scope,
|
||||||
|
@Advice.Local("otelRequest") ElasticTransportRequest transportRequest,
|
||||||
@Advice.Argument(value = 2, readOnly = false)
|
@Advice.Argument(value = 2, readOnly = false)
|
||||||
ActionListener<ActionResponse> actionListener) {
|
ActionListener<ActionResponse> actionListener) {
|
||||||
|
|
||||||
|
transportRequest = ElasticTransportRequest.create(action, actionRequest);
|
||||||
Context parentContext = currentContext();
|
Context parentContext = currentContext();
|
||||||
context = tracer().startSpan(parentContext, null, action);
|
if (!instrumenter().shouldStart(parentContext, transportRequest)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context = instrumenter().start(parentContext, transportRequest);
|
||||||
scope = context.makeCurrent();
|
scope = context.makeCurrent();
|
||||||
|
|
||||||
tracer().onRequest(context, action.getClass(), actionRequest.getClass());
|
|
||||||
actionListener =
|
actionListener =
|
||||||
new TransportActionListener<>(actionRequest, actionListener, context, parentContext);
|
new TransportActionListener<>(
|
||||||
|
instrumenter(), transportRequest, actionListener, context, parentContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
public static void stopSpan(
|
public static void stopSpan(
|
||||||
@Advice.Thrown Throwable throwable,
|
@Advice.Thrown Throwable throwable,
|
||||||
@Advice.Local("otelContext") Context context,
|
@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();
|
scope.close();
|
||||||
|
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
tracer().endExceptionally(context, throwable);
|
instrumenter().end(context, transportRequest, null, throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<ElasticTransportRequest, ActionResponse> INSTRUMENTER =
|
||||||
|
ElasticsearchTransportInstrumenterFactory.create(
|
||||||
|
"io.opentelemetry.elasticsearch-transport-5.3",
|
||||||
|
new Elasticsearch53TransportExperimentalAttributesExtractor(),
|
||||||
|
new ElasticTransportNetAttributesExtractor());
|
||||||
|
|
||||||
|
public static Instrumenter<ElasticTransportRequest, ActionResponse> instrumenter() {
|
||||||
|
return INSTRUMENTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Elasticsearch53TransportSingletons() {}
|
||||||
|
}
|
|
@ -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<T extends ActionResponse> implements ActionListener<T> {
|
|
||||||
|
|
||||||
private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES =
|
|
||||||
Config.get()
|
|
||||||
.getBoolean("otel.instrumentation.elasticsearch.experimental-span-attributes", false);
|
|
||||||
|
|
||||||
private final ActionListener<T> listener;
|
|
||||||
private final Context context;
|
|
||||||
private final Context parentContext;
|
|
||||||
|
|
||||||
public TransportActionListener(
|
|
||||||
ActionRequest actionRequest,
|
|
||||||
ActionListener<T> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -246,7 +246,7 @@ class Elasticsearch53NodeClientTest extends AbstractElasticsearchNodeClientTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trace(3, 2) {
|
trace(3, 1) {
|
||||||
span(0) {
|
span(0) {
|
||||||
name "IndexAction"
|
name "IndexAction"
|
||||||
kind CLIENT
|
kind CLIENT
|
||||||
|
@ -264,17 +264,6 @@ class Elasticsearch53NodeClientTest extends AbstractElasticsearchNodeClientTest
|
||||||
"elasticsearch.shard.replication.failed" 0
|
"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) {
|
trace(4, 1) {
|
||||||
span(0) {
|
span(0) {
|
||||||
|
|
|
@ -115,7 +115,7 @@ class Elasticsearch53SpringRepositoryTest extends AgentInstrumentationSpecificat
|
||||||
|
|
||||||
and:
|
and:
|
||||||
assertTraces(1) {
|
assertTraces(1) {
|
||||||
trace(0, 4) {
|
trace(0, 3) {
|
||||||
span(0) {
|
span(0) {
|
||||||
name "ElasticsearchRepository.index"
|
name "ElasticsearchRepository.index"
|
||||||
kind INTERNAL
|
kind INTERNAL
|
||||||
|
@ -141,17 +141,6 @@ class Elasticsearch53SpringRepositoryTest extends AgentInstrumentationSpecificat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
span(2) {
|
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"
|
name "RefreshAction"
|
||||||
kind CLIENT
|
kind CLIENT
|
||||||
childOf span(0)
|
childOf span(0)
|
||||||
|
|
|
@ -180,7 +180,7 @@ class Elasticsearch53SpringTemplateTest extends AgentInstrumentationSpecificatio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trace(3, 2) {
|
trace(3, 1) {
|
||||||
span(0) {
|
span(0) {
|
||||||
name "IndexAction"
|
name "IndexAction"
|
||||||
kind CLIENT
|
kind CLIENT
|
||||||
|
@ -198,17 +198,6 @@ class Elasticsearch53SpringTemplateTest extends AgentInstrumentationSpecificatio
|
||||||
"elasticsearch.shard.replication.total" 2
|
"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) {
|
trace(4, 1) {
|
||||||
span(0) {
|
span(0) {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v6_0;
|
package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport.v6_0;
|
||||||
|
|
||||||
import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext;
|
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.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
|
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
|
||||||
|
@ -16,6 +16,8 @@ import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.context.Scope;
|
import io.opentelemetry.context.Scope;
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
|
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.asm.Advice;
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
@ -55,26 +57,38 @@ public class AbstractClientInstrumentation implements TypeInstrumentation {
|
||||||
@Advice.Argument(1) ActionRequest actionRequest,
|
@Advice.Argument(1) ActionRequest actionRequest,
|
||||||
@Advice.Local("otelContext") Context context,
|
@Advice.Local("otelContext") Context context,
|
||||||
@Advice.Local("otelScope") Scope scope,
|
@Advice.Local("otelScope") Scope scope,
|
||||||
|
@Advice.Local("otelRequest") ElasticTransportRequest transportRequest,
|
||||||
@Advice.Argument(value = 2, readOnly = false)
|
@Advice.Argument(value = 2, readOnly = false)
|
||||||
ActionListener<ActionResponse> actionListener) {
|
ActionListener<ActionResponse> actionListener) {
|
||||||
|
|
||||||
|
transportRequest = ElasticTransportRequest.create(action, actionRequest);
|
||||||
Context parentContext = currentContext();
|
Context parentContext = currentContext();
|
||||||
context = tracer().startSpan(parentContext, null, action);
|
if (!instrumenter().shouldStart(parentContext, transportRequest)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context = instrumenter().start(parentContext, transportRequest);
|
||||||
scope = context.makeCurrent();
|
scope = context.makeCurrent();
|
||||||
tracer().onRequest(context, action.getClass(), actionRequest.getClass());
|
|
||||||
actionListener =
|
actionListener =
|
||||||
new TransportActionListener<>(actionRequest, actionListener, context, parentContext);
|
new TransportActionListener<>(
|
||||||
|
instrumenter(), transportRequest, actionListener, context, parentContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
public static void stopSpan(
|
public static void stopSpan(
|
||||||
@Advice.Thrown Throwable throwable,
|
@Advice.Thrown Throwable throwable,
|
||||||
@Advice.Local("otelContext") Context context,
|
@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();
|
scope.close();
|
||||||
|
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
tracer().endExceptionally(context, throwable);
|
instrumenter().end(context, transportRequest, null, throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<ElasticTransportRequest, ActionResponse> {
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<ElasticTransportRequest, ActionResponse> INSTRUMENTER =
|
||||||
|
ElasticsearchTransportInstrumenterFactory.create(
|
||||||
|
"io.opentelemetry.elasticsearch-transport-6.0",
|
||||||
|
new Elasticsearch6TransportExperimentalAttributesExtractor(),
|
||||||
|
new Elasticsearch6TransportNetAttributesExtractor());
|
||||||
|
|
||||||
|
public static Instrumenter<ElasticTransportRequest, ActionResponse> instrumenter() {
|
||||||
|
return INSTRUMENTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Elasticsearch6TransportSingletons() {}
|
||||||
|
}
|
|
@ -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<T extends ActionResponse> implements ActionListener<T> {
|
|
||||||
|
|
||||||
private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES =
|
|
||||||
Config.get()
|
|
||||||
.getBoolean("otel.instrumentation.elasticsearch.experimental-span-attributes", false);
|
|
||||||
|
|
||||||
private final ActionListener<T> listener;
|
|
||||||
private final Context context;
|
|
||||||
private final Context parentContext;
|
|
||||||
|
|
||||||
public TransportActionListener(
|
|
||||||
ActionRequest actionRequest,
|
|
||||||
ActionListener<T> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -227,7 +227,7 @@ class Elasticsearch6NodeClientTest extends AbstractElasticsearchNodeClientTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trace(2, 2) {
|
trace(2, 1) {
|
||||||
span(0) {
|
span(0) {
|
||||||
name "IndexAction"
|
name "IndexAction"
|
||||||
kind CLIENT
|
kind CLIENT
|
||||||
|
@ -245,17 +245,6 @@ class Elasticsearch6NodeClientTest extends AbstractElasticsearchNodeClientTest {
|
||||||
"elasticsearch.shard.replication.failed" 0
|
"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) {
|
trace(3, 1) {
|
||||||
span(0) {
|
span(0) {
|
||||||
|
|
|
@ -2,4 +2,9 @@ plugins {
|
||||||
id("otel.library-instrumentation")
|
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")
|
||||||
|
}
|
||||||
|
|
|
@ -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<ElasticTransportRequest, ActionResponse> {
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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<ElasticTransportRequest, ActionResponse> {
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Void, Object, String> {
|
|
||||||
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<ElasticTransportRequest, ActionResponse> {
|
||||||
|
@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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<ElasticTransportRequest, ActionResponse> create(
|
||||||
|
String instrumentationName,
|
||||||
|
AttributesExtractor<ElasticTransportRequest, ActionResponse> experimentalAttributesExtractor,
|
||||||
|
NetAttributesExtractor<ElasticTransportRequest, ActionResponse> netAttributesExtractor) {
|
||||||
|
|
||||||
|
ElasticsearchTransportAttributesExtractor attributesExtractor =
|
||||||
|
new ElasticsearchTransportAttributesExtractor();
|
||||||
|
|
||||||
|
InstrumenterBuilder<ElasticTransportRequest, ActionResponse> instrumenterBuilder =
|
||||||
|
Instrumenter.<ElasticTransportRequest, ActionResponse>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() {}
|
||||||
|
}
|
|
@ -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<T extends ActionResponse> implements ActionListener<T> {
|
||||||
|
|
||||||
|
private final Instrumenter<ElasticTransportRequest, ActionResponse> instrumenter;
|
||||||
|
private final ElasticTransportRequest actionRequest;
|
||||||
|
private final ActionListener<T> listener;
|
||||||
|
private final Context context;
|
||||||
|
private final Context parentContext;
|
||||||
|
|
||||||
|
public TransportActionListener(
|
||||||
|
Instrumenter<ElasticTransportRequest, ActionResponse> instrumenter,
|
||||||
|
ElasticTransportRequest actionRequest,
|
||||||
|
ActionListener<T> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue