Migrate netty-3.8 server instrumentation to Instrumenter API (#4368)

This commit is contained in:
Mateusz Rzeszutek 2021-10-14 17:59:04 +02:00 committed by GitHub
parent abe26caeb7
commit 0825da7ae4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 278 additions and 167 deletions

View File

@ -5,7 +5,6 @@
package io.opentelemetry.javaagent.instrumentation.netty.v3_8;
import static io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.NettyHttpServerTracer.tracer;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
@ -13,6 +12,7 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.NettyServerErrorHandler;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@ -39,7 +39,7 @@ public class DefaultChannelPipelineInstrumentation implements TypeInstrumentatio
@Advice.OnMethodEnter
public static void onEnter(@Advice.Argument(1) Throwable throwable) {
if (throwable != null) {
tracer().onException(Java8BytecodeBridge.currentContext(), throwable);
NettyServerErrorHandler.onError(Java8BytecodeBridge.currentContext(), throwable);
}
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.netty.v3_8.server;
import com.google.auto.value.AutoValue;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.handler.codec.http.HttpRequest;
@AutoValue
abstract class HttpRequestAndChannel {
public static HttpRequestAndChannel create(HttpRequest request, Channel channel) {
return new AutoValue_HttpRequestAndChannel(request, channel);
}
abstract HttpRequest request();
abstract Channel channel();
}

View File

@ -5,12 +5,11 @@
package io.opentelemetry.javaagent.instrumentation.netty.v3_8.server;
import static io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.NettyHttpServerTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.NettyServerSingletons.instrumenter;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.field.VirtualField;
import io.opentelemetry.javaagent.instrumentation.netty.v3_8.NettyRequestContexts;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
@ -19,35 +18,40 @@ import org.jboss.netty.handler.codec.http.HttpRequest;
public class HttpServerRequestTracingHandler extends SimpleChannelUpstreamHandler {
private static final VirtualField<Channel, NettyRequestContexts> requestContextsField =
VirtualField.find(Channel.class, NettyRequestContexts.class);
private static final VirtualField<Channel, NettyServerRequestAndContext> requestAndContextField =
VirtualField.find(Channel.class, NettyServerRequestAndContext.class);
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) {
Object message = event.getMessage();
if (!(message instanceof HttpRequest)) {
NettyRequestContexts requestContexts = requestContextsField.get(ctx.getChannel());
if (requestContexts == null) {
NettyServerRequestAndContext requestAndContext = requestAndContextField.get(ctx.getChannel());
if (requestAndContext == null) {
ctx.sendUpstream(event);
} else {
try (Scope ignored = requestContexts.context().makeCurrent()) {
try (Scope ignored = requestAndContext.context().makeCurrent()) {
ctx.sendUpstream(event);
}
}
return;
}
HttpRequest request = (HttpRequest) message;
Context parentContext = Context.current();
HttpRequestAndChannel request =
HttpRequestAndChannel.create((HttpRequest) message, ctx.getChannel());
if (!instrumenter().shouldStart(parentContext, request)) {
ctx.sendUpstream(event);
}
Context context =
tracer().startSpan(request, ctx.getChannel(), null, "HTTP " + request.getMethod());
requestContextsField.set(ctx.getChannel(), NettyRequestContexts.create(null, context));
Context context = instrumenter().start(parentContext, request);
requestAndContextField.set(
ctx.getChannel(), NettyServerRequestAndContext.create(request, context));
try (Scope ignored = context.makeCurrent()) {
ctx.sendUpstream(event);
// the span is ended normally in HttpServerResponseTracingHandler
} catch (Throwable throwable) {
tracer().endExceptionally(context, throwable);
instrumenter().end(context, request, null, throwable);
throw throwable;
}
}

View File

@ -5,12 +5,11 @@
package io.opentelemetry.javaagent.instrumentation.netty.v3_8.server;
import static io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.NettyHttpServerTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.netty.v3_8.server.NettyServerSingletons.instrumenter;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.field.VirtualField;
import io.opentelemetry.javaagent.instrumentation.netty.v3_8.NettyRequestContexts;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
@ -19,24 +18,27 @@ import org.jboss.netty.handler.codec.http.HttpResponse;
public class HttpServerResponseTracingHandler extends SimpleChannelDownstreamHandler {
private static final VirtualField<Channel, NettyRequestContexts> requestContextsField =
VirtualField.find(Channel.class, NettyRequestContexts.class);
private static final VirtualField<Channel, NettyServerRequestAndContext> requestAndContextField =
VirtualField.find(Channel.class, NettyServerRequestAndContext.class);
@Override
public void writeRequested(ChannelHandlerContext ctx, MessageEvent msg) {
NettyRequestContexts requestContexts = requestContextsField.get(ctx.getChannel());
NettyServerRequestAndContext requestAndContext = requestAndContextField.get(ctx.getChannel());
if (requestContexts == null || !(msg.getMessage() instanceof HttpResponse)) {
if (requestAndContext == null || !(msg.getMessage() instanceof HttpResponse)) {
ctx.sendDownstream(msg);
return;
}
Context context = requestContexts.context();
Context context = requestAndContext.context();
HttpRequestAndChannel request = requestAndContext.request();
HttpResponse response = (HttpResponse) msg.getMessage();
try (Scope ignored = context.makeCurrent()) {
ctx.sendDownstream(msg);
tracer().end(context, (HttpResponse) msg.getMessage());
instrumenter().end(context, request, response, null);
} catch (Throwable throwable) {
tracer().endExceptionally(context, throwable);
instrumenter().end(context, request, response, throwable);
throw throwable;
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.netty.v3_8.server;
import io.opentelemetry.context.propagation.TextMapGetter;
import javax.annotation.Nullable;
final class NettyHeadersGetter implements TextMapGetter<HttpRequestAndChannel> {
@Override
public Iterable<String> keys(HttpRequestAndChannel requestAndChannel) {
return requestAndChannel.request().headers().names();
}
@Nullable
@Override
public String get(@Nullable HttpRequestAndChannel requestAndChannel, String s) {
return requestAndChannel.request().headers().get(s);
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.netty.v3_8.server;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractor;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jboss.netty.handler.codec.http.HttpResponse;
final class NettyHttpServerAttributesExtractor
extends HttpServerAttributesExtractor<HttpRequestAndChannel, HttpResponse> {
@Override
protected String method(HttpRequestAndChannel requestAndChannel) {
return requestAndChannel.request().getMethod().getName();
}
@Override
protected List<String> requestHeader(HttpRequestAndChannel requestAndChannel, String name) {
return requestAndChannel.request().headers().getAll(name);
}
@Override
protected @Nullable Long requestContentLength(
HttpRequestAndChannel requestAndChannel, @Nullable HttpResponse response) {
return null;
}
@Override
protected @Nullable Long requestContentLengthUncompressed(
HttpRequestAndChannel requestAndChannel, @Nullable HttpResponse response) {
return null;
}
@Override
protected Integer statusCode(HttpRequestAndChannel requestAndChannel, HttpResponse response) {
return response.getStatus().getCode();
}
@Override
protected @Nullable Long responseContentLength(
HttpRequestAndChannel requestAndChannel, HttpResponse response) {
return null;
}
@Override
protected @Nullable Long responseContentLengthUncompressed(
HttpRequestAndChannel requestAndChannel, HttpResponse response) {
return null;
}
@Override
protected List<String> responseHeader(
HttpRequestAndChannel requestAndChannel, HttpResponse response, String name) {
return response.headers().getAll(name);
}
@Override
protected String flavor(HttpRequestAndChannel requestAndChannel) {
String flavor = requestAndChannel.request().getProtocolVersion().toString();
if (flavor.startsWith("HTTP/")) {
flavor = flavor.substring("HTTP/".length());
}
return flavor;
}
@Override
protected String target(HttpRequestAndChannel requestAndChannel) {
return requestAndChannel.request().getUri();
}
@Override
protected @Nullable String route(HttpRequestAndChannel requestAndChannel) {
return null;
}
@Override
protected @Nullable String scheme(HttpRequestAndChannel requestAndChannel) {
return null;
}
@Override
protected @Nullable String serverName(
HttpRequestAndChannel requestAndChannel, @Nullable HttpResponse response) {
return null;
}
}

View File

@ -1,111 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.netty.v3_8.server;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.HOST;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapGetter;
import io.opentelemetry.instrumentation.api.tracer.HttpServerTracer;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
public class NettyHttpServerTracer
extends HttpServerTracer<HttpRequest, HttpResponse, Channel, Void> {
private static final NettyHttpServerTracer TRACER = new NettyHttpServerTracer();
public static NettyHttpServerTracer tracer() {
return TRACER;
}
@Override
protected String method(HttpRequest httpRequest) {
return httpRequest.getMethod().getName();
}
@Override
protected String requestHeader(HttpRequest httpRequest, String name) {
return httpRequest.headers().get(name);
}
@Override
protected int responseStatus(HttpResponse httpResponse) {
return httpResponse.getStatus().getCode();
}
@Override
protected void attachServerContext(Context context, Void unused) {}
@Override
public Context getServerContext(Void unused) {
return null;
}
@Override
protected String url(HttpRequest request) {
String uri = request.getUri();
if (isRelativeUrl(uri) && request.headers().contains(HOST)) {
return "http://" + request.headers().get(HOST) + request.getUri();
} else {
return uri;
}
}
@Override
@Nullable
protected String scheme(HttpRequest request) {
return null;
}
@Override
@Nullable
protected String host(HttpRequest request) {
return null;
}
@Override
@Nullable
protected String target(HttpRequest request) {
return null;
}
@Override
protected String peerHostIp(Channel channel) {
SocketAddress socketAddress = channel.getRemoteAddress();
if (socketAddress instanceof InetSocketAddress) {
return ((InetSocketAddress) socketAddress).getAddress().getHostAddress();
}
return null;
}
@Override
protected String flavor(Channel channel, HttpRequest request) {
return request.getProtocolVersion().toString();
}
@Override
protected TextMapGetter<HttpRequest> getGetter() {
return NettyRequestExtractAdapter.GETTER;
}
@Override
protected String getInstrumentationName() {
return "io.opentelemetry.netty-3.8";
}
@Override
protected Integer peerPort(Channel channel) {
SocketAddress socketAddress = channel.getRemoteAddress();
if (socketAddress instanceof InetSocketAddress) {
return ((InetSocketAddress) socketAddress).getPort();
}
return null;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.netty.v3_8.server;
import io.opentelemetry.instrumentation.api.instrumenter.net.InetSocketAddressNetServerAttributesExtractor;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jboss.netty.handler.codec.http.HttpResponse;
final class NettyNetServerAttributesExtractor
extends InetSocketAddressNetServerAttributesExtractor<HttpRequestAndChannel, HttpResponse> {
@Override
public @Nullable String transport(HttpRequestAndChannel requestAndChannel) {
return null;
}
@Override
public @Nullable InetSocketAddress getAddress(HttpRequestAndChannel requestAndChannel) {
SocketAddress address = requestAndChannel.channel().getRemoteAddress();
if (address instanceof InetSocketAddress) {
return (InetSocketAddress) address;
}
return null;
}
}

View File

@ -1,24 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.netty.v3_8.server;
import io.opentelemetry.context.propagation.TextMapGetter;
import org.jboss.netty.handler.codec.http.HttpRequest;
public class NettyRequestExtractAdapter implements TextMapGetter<HttpRequest> {
public static final NettyRequestExtractAdapter GETTER = new NettyRequestExtractAdapter();
@Override
public Iterable<String> keys(HttpRequest request) {
return request.headers().names();
}
@Override
public String get(HttpRequest request, String key) {
return request.headers().get(key);
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.netty.v3_8.server;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.ErrorCauseExtractor;
public final class NettyServerErrorHandler {
// copied from BaseTracer#onException()
public static void onError(Context context, Throwable error) {
Span span = Span.fromContext(context);
span.setStatus(StatusCode.ERROR);
span.recordException(ErrorCauseExtractor.jdk().extractCause(error));
}
private NettyServerErrorHandler() {}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.netty.v3_8.server;
import com.google.auto.value.AutoValue;
import io.opentelemetry.context.Context;
@AutoValue
abstract class NettyServerRequestAndContext {
static NettyServerRequestAndContext create(HttpRequestAndChannel request, Context context) {
return new AutoValue_NettyServerRequestAndContext(request, context);
}
abstract HttpRequestAndChannel request();
abstract Context context();
}

View File

@ -0,0 +1,40 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.netty.v3_8.server;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
import org.jboss.netty.handler.codec.http.HttpResponse;
final class NettyServerSingletons {
private static final Instrumenter<HttpRequestAndChannel, HttpResponse> INSTRUMENTER;
static {
NettyHttpServerAttributesExtractor httpServerAttributesExtractor =
new NettyHttpServerAttributesExtractor();
INSTRUMENTER =
Instrumenter.<HttpRequestAndChannel, HttpResponse>newBuilder(
GlobalOpenTelemetry.get(),
"io.opentelemetry.netty-3.8",
HttpSpanNameExtractor.create(httpServerAttributesExtractor))
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpServerAttributesExtractor))
.addAttributesExtractor(httpServerAttributesExtractor)
.addAttributesExtractor(new NettyNetServerAttributesExtractor())
.addRequestMetrics(HttpServerMetrics.get())
.newServerInstrumenter(new NettyHeadersGetter());
}
public static Instrumenter<HttpRequestAndChannel, HttpResponse> instrumenter() {
return INSTRUMENTER;
}
private NettyServerSingletons() {}
}

View File

@ -3,10 +3,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import io.opentelemetry.api.common.AttributeKey
import io.opentelemetry.instrumentation.test.AgentTestTrait
import io.opentelemetry.instrumentation.test.base.HttpServerTest
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import org.jboss.netty.bootstrap.ServerBootstrap
import org.jboss.netty.buffer.ChannelBuffer
import org.jboss.netty.buffer.ChannelBuffers
@ -167,11 +165,4 @@ class Netty38ServerTest extends HttpServerTest<ServerBootstrap> implements Agent
boolean testConcurrency() {
return true
}
@Override
List<AttributeKey<?>> extraAttributes() {
return [
SemanticAttributes.HTTP_URL
]
}
}