From 92f4ae21a0f3c541369805e3eaeb9c7dca48f390 Mon Sep 17 00:00:00 2001 From: anosek Date: Mon, 20 Sep 2021 10:23:46 +0200 Subject: [PATCH 1/3] add restlet 2.0 instrumentation --- .../restlet-2.0/javaagent/build.gradle.kts | 42 ++++ .../v2_0/RestletInstrumentationModule.java | 25 +++ .../restlet/v2_0/RestletSingletons.java | 24 +++ .../restlet/v2_0/RouteInstrumentation.java | 52 +++++ .../restlet/v2_0/ServerInstrumentation.java | 94 +++++++++ .../restlet/v2_0/RestletServerTest.groovy | 13 ++ .../restlet-2.0/library/build.gradle.kts | 29 +++ .../restlet/v2_0/RestletHeadersGetter.java | 36 ++++ .../v2_0/RestletHttpAttributesExtractor.java | 122 +++++++++++ .../v2_0/RestletNetAttributesExtractor.java | 36 ++++ .../restlet/v2_0/RestletTracing.java | 47 +++++ .../restlet/v2_0/RestletTracingBuilder.java | 83 ++++++++ .../restlet/v2_0/TracingFilter.java | 66 ++++++ .../internal/RestletServerSpanNaming.java | 23 +++ .../restlet/v2_0/RestletServerTest.groovy | 31 +++ .../restlet-2.0/testing/build.gradle.kts | 19 ++ .../v2_0/AbstractRestletServerTest.groovy | 195 ++++++++++++++++++ settings.gradle.kts | 3 + 18 files changed, 940 insertions(+) create mode 100644 instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts create mode 100644 instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletInstrumentationModule.java create mode 100644 instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java create mode 100644 instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RouteInstrumentation.java create mode 100644 instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/ServerInstrumentation.java create mode 100644 instrumentation/restlet/restlet-2.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletServerTest.groovy create mode 100644 instrumentation/restlet/restlet-2.0/library/build.gradle.kts create mode 100644 instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHeadersGetter.java create mode 100644 instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHttpAttributesExtractor.java create mode 100644 instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletNetAttributesExtractor.java create mode 100644 instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracing.java create mode 100644 instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracingBuilder.java create mode 100644 instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/TracingFilter.java create mode 100644 instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletServerSpanNaming.java create mode 100644 instrumentation/restlet/restlet-2.0/library/src/test/groovy/io/opentelemetry/instrumentation/restlet/v2_0/RestletServerTest.groovy create mode 100644 instrumentation/restlet/restlet-2.0/testing/build.gradle.kts create mode 100644 instrumentation/restlet/restlet-2.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v2_0/AbstractRestletServerTest.groovy diff --git a/instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts b/instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts new file mode 100644 index 000000000000..0600ea91ac17 --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts @@ -0,0 +1,42 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +muzzle { + pass { + group.set("org.restlet") + module.set("org.restlet.jse") + versions.set("[2.0.0, 2.5-M1]") + assertInverse.set(true) + } +} + +repositories { + mavenCentral() + maven("https://maven.restlet.talend.com/") + mavenLocal() +} + +dependencies { + api(project(":instrumentation:restlet:restlet-2.0:library")) + + library("org.restlet.jse:org.restlet:2.0.2") + + implementation(project(":instrumentation:restlet:restlet-2.0:library")) + + testImplementation(project(":instrumentation:restlet:restlet-2.0:testing")) + testImplementation("org.restlet.jse:org.restlet.ext.jetty:2.0.2") +} + +// restlet registers the first engine that is present on classpath, so we need to enforce the appropriate version +if (findProperty("testLatestDeps") as Boolean) { + configurations.configureEach { + resolutionStrategy { + eachDependency { + if (requested.group == "org.restlet.jse") { + useVersion("2.+") + } + } + } + } +} diff --git a/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletInstrumentationModule.java b/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletInstrumentationModule.java new file mode 100644 index 000000000000..6e3e5c8c1371 --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletInstrumentationModule.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.restlet.v2_0; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import java.util.Arrays; +import java.util.List; + +@AutoService(InstrumentationModule.class) +public class RestletInstrumentationModule extends InstrumentationModule { + + public RestletInstrumentationModule() { + super("restlet", "restlet-2.0"); + } + + @Override + public List typeInstrumentations() { + return Arrays.asList(new ServerInstrumentation(), new RouteInstrumentation()); + } +} diff --git a/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java b/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java new file mode 100644 index 000000000000..edf729bea309 --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java @@ -0,0 +1,24 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.restlet.v2_0; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.restlet.v2_0.RestletTracing; +import org.restlet.Request; +import org.restlet.Response; + +public final class RestletSingletons { + + private static final Instrumenter INSTRUMENTER = + RestletTracing.create(GlobalOpenTelemetry.get()).getServerInstrumenter(); + + public static Instrumenter instrumenter() { + return INSTRUMENTER; + } + + private RestletSingletons() {} +} diff --git a/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RouteInstrumentation.java b/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RouteInstrumentation.java new file mode 100644 index 000000000000..462828665441 --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RouteInstrumentation.java @@ -0,0 +1,52 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.restlet.v2_0; + +import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTROLLER; +import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming; +import io.opentelemetry.instrumentation.restlet.v2_0.internal.RestletServerSpanNaming; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.restlet.Request; +import org.restlet.routing.TemplateRoute; + +public class RouteInstrumentation implements TypeInstrumentation { + @Override + public ElementMatcher typeMatcher() { + return named("org.restlet.routing.TemplateRoute").or(named("org.restlet.routing.Route")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod() + .and(named("beforeHandle")) + .and(takesArgument(0, named("org.restlet.Request"))) + .and(takesArgument(1, named("org.restlet.Response"))), + this.getClass().getName() + "$RouteBeforeHandleAdvice"); + } + + @SuppressWarnings("unused") + public static class RouteBeforeHandleAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void getRouteInfo( + @Advice.This TemplateRoute route, @Advice.Argument(0) Request request) { + String pattern = route.getTemplate().getPattern(); + + ServerSpanNaming.updateServerSpanName( + currentContext(), CONTROLLER, RestletServerSpanNaming.SERVER_SPAN_NAME, pattern); + } + } +} diff --git a/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/ServerInstrumentation.java b/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/ServerInstrumentation.java new file mode 100644 index 000000000000..fbe37ef23819 --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/ServerInstrumentation.java @@ -0,0 +1,94 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.restlet.v2_0; + +import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTROLLER; +import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext; +import static io.opentelemetry.javaagent.instrumentation.restlet.v2_0.RestletSingletons.instrumenter; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming; +import io.opentelemetry.instrumentation.restlet.v2_0.internal.RestletServerSpanNaming; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.Status; + +public class ServerInstrumentation implements TypeInstrumentation { + @Override + public ElementMatcher typeMatcher() { + return named("org.restlet.Server"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod() + .and(named("handle")) + .and(takesArgument(0, named("org.restlet.Request"))) + .and(takesArgument(1, named("org.restlet.Response"))), + this.getClass().getName() + "$ServerHandleAdvice"); + } + + @SuppressWarnings("unused") + public static class ServerHandleAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void beginRequest( + @Advice.Argument(0) Request request, + @Advice.Argument(1) Response response, + @Advice.Local("otelContext") Context context, + @Advice.Local("otelScope") Scope scope) { + + Context parentContext = currentContext(); + + if (!instrumenter().shouldStart(parentContext, request)) { + return; + } + + context = instrumenter().start(parentContext, request); + scope = context.makeCurrent(); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void finishRequest( + @Advice.Argument(0) Request request, + @Advice.Argument(1) Response response, + @Advice.Thrown Throwable exception, + @Advice.Local("otelContext") Context context, + @Advice.Local("otelScope") Scope scope) { + + if (scope == null) { + return; + } + + scope.close(); + + if (Status.CLIENT_ERROR_NOT_FOUND.equals(response.getStatus())) { + ServerSpanNaming.updateServerSpanName( + context, CONTROLLER, RestletServerSpanNaming.SERVER_SPAN_NAME, "/*"); + } + + if (exception != null) { + instrumenter().end(context, request, response, exception); + return; + } + + // Restlet suppresses exceptions and sets the throwable in status + Throwable statusThrowable = response.getStatus().getThrowable(); + + instrumenter().end(context, request, response, statusThrowable); + } + } +} diff --git a/instrumentation/restlet/restlet-2.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletServerTest.groovy b/instrumentation/restlet/restlet-2.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletServerTest.groovy new file mode 100644 index 000000000000..cc6b39f94168 --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletServerTest.groovy @@ -0,0 +1,13 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.restlet.v2_0 + +import io.opentelemetry.instrumentation.restlet.v2_0.AbstractRestletServerTest +import io.opentelemetry.instrumentation.test.AgentTestTrait + +class RestletServerTest extends AbstractRestletServerTest implements AgentTestTrait { + +} diff --git a/instrumentation/restlet/restlet-2.0/library/build.gradle.kts b/instrumentation/restlet/restlet-2.0/library/build.gradle.kts new file mode 100644 index 000000000000..76d6539ba579 --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/library/build.gradle.kts @@ -0,0 +1,29 @@ +plugins { + id("otel.library-instrumentation") +} + +repositories { + mavenCentral() + maven("https://maven.restlet.talend.com/") + mavenLocal() +} + +dependencies { + + library("org.restlet.jse:org.restlet:2.0.2") + + testImplementation(project(":instrumentation:restlet:restlet-2.0:testing")) + testImplementation("org.restlet.jse:org.restlet.ext.jetty:2.0.2") +} +// restlet registers the first engine that is present on classpath, so we need to enforce the appropriate version +if (findProperty("testLatestDeps") as Boolean) { + configurations.configureEach { + resolutionStrategy { + eachDependency { + if (requested.group == "org.restlet.jse") { + useVersion("2.+") + } + } + } + } +} diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHeadersGetter.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHeadersGetter.java new file mode 100644 index 000000000000..88672daaacc3 --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHeadersGetter.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.restlet.v2_0; + +import io.opentelemetry.context.propagation.TextMapGetter; +import java.util.Locale; +import org.restlet.Message; +import org.restlet.Request; +import org.restlet.util.Series; + +final class RestletHeadersGetter implements TextMapGetter { + + @Override + public Iterable keys(Request carrier) { + return getHeaders(carrier).getNames(); + } + + @Override + public String get(Request carrier, String key) { + + Series headers = getHeaders(carrier); + + String value = headers.getFirstValue(key); + if (value != null) { + return value; + } + return headers.getFirstValue(key.toLowerCase(Locale.ROOT)); + } + + static Series getHeaders(Message carrier) { + return (Series) carrier.getAttributes().get("org.restlet.http.headers"); + } +} diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHttpAttributesExtractor.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHttpAttributesExtractor.java new file mode 100644 index 000000000000..79b4d6bc06a7 --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHttpAttributesExtractor.java @@ -0,0 +1,122 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.restlet.v2_0; + +import static io.opentelemetry.instrumentation.restlet.v2_0.RestletHeadersGetter.getHeaders; + +import io.opentelemetry.instrumentation.api.instrumenter.http.CapturedHttpHeaders; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractor; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nullable; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.Reference; +import org.restlet.util.Series; + +final class RestletHttpAttributesExtractor + extends HttpServerAttributesExtractor { + + RestletHttpAttributesExtractor(CapturedHttpHeaders capturedHttpHeaders) { + super(capturedHttpHeaders); + } + + @Override + protected String method(Request request) { + return request.getMethod().toString(); + } + + @Override + @Nullable + protected String target(Request request) { + Reference ref = request.getOriginalRef(); + String path = ref.getPath(); + return ref.hasQuery() ? path + "?" + ref.getQuery() : path; + } + + @Override + @Nullable + protected String route(Request request) { + return null; + } + + @Override + @Nullable + protected String scheme(Request request) { + return request.getOriginalRef().getScheme(); + } + + @Override + protected List requestHeader(Request request, String name) { + Series headers = getHeaders(request); + if (headers == null) { + return Collections.emptyList(); + } + return Arrays.asList(headers.getValuesArray(name, true)); + } + + @Override + @Nullable + protected Long requestContentLength(Request request, @Nullable Response response) { + return null; + } + + @Override + @Nullable + protected Long requestContentLengthUncompressed(Request request, @Nullable Response response) { + return null; + } + + @Override + @Nullable + protected String flavor(Request request) { + switch (request.getProtocol().toString()) { + case "HTTP/1.0": + return SemanticAttributes.HttpFlavorValues.HTTP_1_0; + case "HTTP/1.1": + return SemanticAttributes.HttpFlavorValues.HTTP_1_1; + case "HTTP/2.0": + return SemanticAttributes.HttpFlavorValues.HTTP_2_0; + default: + // fall through + } + return null; + } + + @Override + @Nullable + protected String serverName(Request request, @Nullable Response response) { + return null; + } + + @Override + protected Integer statusCode(Request request, Response response) { + return response.getStatus().getCode(); + } + + @Override + @Nullable + protected Long responseContentLength(Request request, Response response) { + return null; + } + + @Override + @Nullable + protected Long responseContentLengthUncompressed(Request request, Response response) { + return null; + } + + @Override + protected List responseHeader(Request request, Response response, String name) { + Series headers = getHeaders(response); + if (headers == null) { + return Collections.emptyList(); + } + return Arrays.asList(headers.getValuesArray(name, true)); + } +} diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletNetAttributesExtractor.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletNetAttributesExtractor.java new file mode 100644 index 000000000000..7f78d8c0d27e --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletNetAttributesExtractor.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.restlet.v2_0; + +import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import javax.annotation.Nullable; +import org.restlet.Request; +import org.restlet.Response; + +final class RestletNetAttributesExtractor extends NetServerAttributesExtractor { + @Override + public String transport(Request request) { + return SemanticAttributes.NetTransportValues.IP_TCP; + } + + @Override + @Nullable + public String peerName(Request request) { + return null; + } + + @Override + public Integer peerPort(Request request) { + return request.getClientInfo().getPort(); + } + + @Override + @Nullable + public String peerIp(Request request) { + return request.getClientInfo().getAddress(); + } +} diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracing.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracing.java new file mode 100644 index 000000000000..6dc16d5dfd72 --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracing.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.restlet.v2_0; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.routing.Filter; + +/** Entrypoint for tracing Restlet servers. */ +public final class RestletTracing { + + /** Returns a new {@link RestletTracing} configured with the given {@link OpenTelemetry}. */ + public static RestletTracing create(OpenTelemetry openTelemetry) { + return builder(openTelemetry).build(); + } + + /** + * Returns a new {@link RestletTracingBuilder} configured with the given {@link OpenTelemetry}. + */ + public static RestletTracingBuilder builder(OpenTelemetry openTelemetry) { + return new RestletTracingBuilder(openTelemetry); + } + + private final Instrumenter serverInstrumenter; + + RestletTracing(Instrumenter serverInstrumenter) { + this.serverInstrumenter = serverInstrumenter; + } + + /** + * Returns a new {@link Filter} which can be used to wrap {@link org.restlet.Restlet} + * implementations. + */ + public Filter newFilter(String path) { + return new TracingFilter(serverInstrumenter, path); + } + + /** Returns a server {@link Instrumenter}. */ + public Instrumenter getServerInstrumenter() { + return serverInstrumenter; + } +} diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracingBuilder.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracingBuilder.java new file mode 100644 index 000000000000..4bfbfa49c930 --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracingBuilder.java @@ -0,0 +1,83 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.restlet.v2_0; + +import io.opentelemetry.api.OpenTelemetry; +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.SpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.http.CapturedHttpHeaders; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; +import java.util.ArrayList; +import java.util.List; +import org.restlet.Request; +import org.restlet.Response; + +/** A builder of {@link RestletTracing}. */ +public final class RestletTracingBuilder { + + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.restlet-2.0"; + + private final OpenTelemetry openTelemetry; + private final List> additionalExtractors = + new ArrayList<>(); + private CapturedHttpHeaders capturedHttpHeaders = CapturedHttpHeaders.server(Config.get()); + + RestletTracingBuilder(OpenTelemetry openTelemetry) { + this.openTelemetry = openTelemetry; + } + + /** + * Adds an additional {@link AttributesExtractor} to invoke to set attributes to instrumented + * items. + */ + public RestletTracingBuilder addAttributesExtractor( + AttributesExtractor attributesExtractor) { + additionalExtractors.add(attributesExtractor); + return this; + } + + /** + * Configure the instrumentation to capture chosen HTTP request and response headers as span + * attributes. + * + * @param capturedHttpHeaders An instance of {@link CapturedHttpHeaders} containing the configured + * HTTP request and response names. + */ + public RestletTracingBuilder captureHttpHeaders(CapturedHttpHeaders capturedHttpHeaders) { + this.capturedHttpHeaders = capturedHttpHeaders; + return this; + } + + /** + * Returns a new {@link RestletTracing} with the settings of this {@link RestletTracingBuilder}. + */ + public RestletTracing build() { + RestletHttpAttributesExtractor httpAttributesExtractor = + new RestletHttpAttributesExtractor(capturedHttpHeaders); + SpanNameExtractor spanNameExtractor = + HttpSpanNameExtractor.create(httpAttributesExtractor); + SpanStatusExtractor spanStatusExtractor = + HttpSpanStatusExtractor.create(httpAttributesExtractor); + NetServerAttributesExtractor netAttributesExtractor = + new RestletNetAttributesExtractor(); + + Instrumenter instrumenter = + Instrumenter.builder( + openTelemetry, INSTRUMENTATION_NAME, spanNameExtractor) + .setSpanStatusExtractor(spanStatusExtractor) + .addAttributesExtractor(httpAttributesExtractor) + .addAttributesExtractor(netAttributesExtractor) + .addAttributesExtractors(additionalExtractors) + .newServerInstrumenter(new RestletHeadersGetter()); + + return new RestletTracing(instrumenter); + } +} diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/TracingFilter.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/TracingFilter.java new file mode 100644 index 000000000000..eba8d77c18c5 --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/TracingFilter.java @@ -0,0 +1,66 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.restlet.v2_0; + +import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTROLLER; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming; +import io.opentelemetry.instrumentation.restlet.v2_0.internal.RestletServerSpanNaming; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.routing.Filter; + +final class TracingFilter extends Filter { + + private final Instrumenter instrumenter; + private final String path; + + public TracingFilter(Instrumenter instrumenter, String path) { + this.instrumenter = instrumenter; + this.path = path; + } + + @Override + public int doHandle(Request request, Response response) { + + Context parentContext = Context.current(); + Context context = parentContext; + + Scope scope = null; + + if (instrumenter.shouldStart(parentContext, request)) { + context = instrumenter.start(parentContext, request); + scope = context.makeCurrent(); + } + + ServerSpanNaming.updateServerSpanName( + context, CONTROLLER, RestletServerSpanNaming.SERVER_SPAN_NAME, path); + + Throwable statusThrowable = null; + try { + super.doHandle(request, response); + } catch (Throwable t) { + statusThrowable = t; + } + + if (scope == null) { + return CONTINUE; + } + + scope.close(); + + if (response.getStatus() != null && response.getStatus().isError()) { + statusThrowable = response.getStatus().getThrowable(); + } + + instrumenter.end(context, request, response, statusThrowable); + + return CONTINUE; + } +} diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletServerSpanNaming.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletServerSpanNaming.java new file mode 100644 index 000000000000..87dcc9a2e78a --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletServerSpanNaming.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.restlet.v2_0.internal; + +import io.opentelemetry.instrumentation.api.servlet.ServerSpanNameSupplier; +import io.opentelemetry.instrumentation.api.servlet.ServletContextPath; + +public final class RestletServerSpanNaming { + + public static final ServerSpanNameSupplier SERVER_SPAN_NAME = + (context, pattern) -> { + if (pattern == null || pattern.equals("")) { + return null; + } + + return ServletContextPath.prepend(context, pattern); + }; + + private RestletServerSpanNaming() {} +} diff --git a/instrumentation/restlet/restlet-2.0/library/src/test/groovy/io/opentelemetry/instrumentation/restlet/v2_0/RestletServerTest.groovy b/instrumentation/restlet/restlet-2.0/library/src/test/groovy/io/opentelemetry/instrumentation/restlet/v2_0/RestletServerTest.groovy new file mode 100644 index 000000000000..1c4547ade22f --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/library/src/test/groovy/io/opentelemetry/instrumentation/restlet/v2_0/RestletServerTest.groovy @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.restlet.v2_0 + +import io.opentelemetry.instrumentation.test.LibraryTestTrait +import org.restlet.Restlet +import org.restlet.engine.application.StatusFilter +import org.restlet.service.StatusService + +class RestletServerTest extends AbstractRestletServerTest implements LibraryTestTrait { + + @Override + Restlet wrapRestlet(Restlet restlet, String path) { + + RestletTracing tracing = RestletTracing.builder(openTelemetry) + .captureHttpHeaders(capturedHttpHeadersForTesting()) + .build() + + def tracingFilter = tracing.newFilter(path) + def statusFilter = new StatusFilter(component.getContext(), new StatusService()) + + tracingFilter.setNext(statusFilter) + statusFilter.setNext(restlet) + + return tracingFilter + } + +} diff --git a/instrumentation/restlet/restlet-2.0/testing/build.gradle.kts b/instrumentation/restlet/restlet-2.0/testing/build.gradle.kts new file mode 100644 index 000000000000..4e5f440e5ddd --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/testing/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + id("otel.java-conventions") +} + +repositories { + mavenCentral() + maven("https://maven.restlet.talend.com/") + mavenLocal() +} + +dependencies { + api(project(":testing-common")) + + implementation("org.restlet.jse:org.restlet:2.0.2") + + implementation("org.codehaus.groovy:groovy-all") + implementation("io.opentelemetry:opentelemetry-api") + implementation("org.spockframework:spock-core") +} diff --git a/instrumentation/restlet/restlet-2.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v2_0/AbstractRestletServerTest.groovy b/instrumentation/restlet/restlet-2.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v2_0/AbstractRestletServerTest.groovy new file mode 100644 index 000000000000..31a1bec1814f --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/testing/src/main/groovy/io/opentelemetry/instrumentation/restlet/v2_0/AbstractRestletServerTest.groovy @@ -0,0 +1,195 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.restlet.v2_0 + +import io.opentelemetry.api.common.AttributeKey +import io.opentelemetry.instrumentation.test.base.HttpServerTest +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes +import org.restlet.Component +import org.restlet.Context +import org.restlet.Request +import org.restlet.Response +import org.restlet.Restlet +import org.restlet.Server +import org.restlet.data.Form +import org.restlet.data.MediaType +import org.restlet.data.Protocol +import org.restlet.data.Status +import org.restlet.routing.Redirector +import org.restlet.routing.Router +import org.restlet.routing.Template +import org.restlet.routing.VirtualHost + +import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.CAPTURE_HEADERS +import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.ERROR +import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.EXCEPTION +import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.INDEXED_CHILD +import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.NOT_FOUND +import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.PATH_PARAM +import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM +import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.REDIRECT +import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.SUCCESS + +abstract class AbstractRestletServerTest extends HttpServerTest { + + Component component + VirtualHost host + + @Override + Server startServer(int port) { + + component = new Component() + host = component.getDefaultHost() + def server = setupServer(component) + setupRouting() + + component.start() + + return server + } + + @Override + void stopServer(Server server) { + component.stop() + } + + def attachAndWrap(path, restlet) { + host.attach(path, wrapRestlet(restlet, path)) + } + + Server setupServer(Component component) { + return component.getServers().add(Protocol.HTTP, port) + } + + void setupRouting() { + + def defaultRouter = wrapRestlet(new Router(host.getContext()), "/*") + host.attach("/", defaultRouter).setMatchingMode(Template.MODE_STARTS_WITH) + + attachAndWrap(SUCCESS.path, new Restlet() { + @Override + void handle(Request request, Response response) { + controller(SUCCESS) { + response.setEntity(SUCCESS.body, MediaType.TEXT_PLAIN) + response.setStatus(Status.valueOf(SUCCESS.status), SUCCESS.body) + } + } + }) + + attachAndWrap(REDIRECT.path, new Redirector(Context.getCurrent(), REDIRECT.body, Redirector.MODE_CLIENT_FOUND) { + @Override + void handle(Request request, Response response) { + super.handle(request, response) + controller(REDIRECT) { + } + } + }) + + attachAndWrap(ERROR.path, new Restlet() { + @Override + void handle(Request request, Response response) { + controller(ERROR) { + response.setStatus(Status.valueOf(ERROR.getStatus()), ERROR.getBody()) + } + } + }) + + attachAndWrap(EXCEPTION.path, new Restlet() { + @Override + void handle(Request request, Response response) { + controller(EXCEPTION) { + throw new Exception(EXCEPTION.getBody()) + } + } + }) + + attachAndWrap(QUERY_PARAM.path, new Restlet() { + @Override + void handle(Request request, Response response) { + controller(QUERY_PARAM) { + response.setEntity(QUERY_PARAM.getBody(), MediaType.TEXT_PLAIN) + response.setStatus(Status.valueOf(QUERY_PARAM.getStatus()), QUERY_PARAM.getBody()) + } + } + }) + + attachAndWrap("/path/{id}/param", new Restlet() { + @Override + void handle(Request request, Response response) { + controller(PATH_PARAM) { + response.setEntity(PATH_PARAM.getBody(), MediaType.TEXT_PLAIN) + response.setStatus(Status.valueOf(PATH_PARAM.getStatus()), PATH_PARAM.getBody()) + } + } + }) + + attachAndWrap("/captureHeaders", new Restlet() { + @Override + void handle(Request request, Response response) { + controller(CAPTURE_HEADERS) { + + def requestHeaders = request.getAttributes().get("org.restlet.http.headers") + def responseHeaders + + try { + def headerClass = Class.forName("org.restlet.data.Header") + def seriesClass = Class.forName("org.restlet.util.Series") + //to avoid constructor error (Series is abstract in 2.0.x) + responseHeaders = response.getAttributes().computeIfAbsent("org.restlet.http.headers", { seriesClass.newInstance(headerClass) }) + + } catch (ClassNotFoundException | NoClassDefFoundError e) { + + responseHeaders = response.getAttributes().computeIfAbsent("org.restlet.http.headers", { new Form() }) + } + + responseHeaders.add("X-Test-Response", requestHeaders.getValues("X-Test-Request")) + + response.setEntity(CAPTURE_HEADERS.getBody(), MediaType.TEXT_PLAIN) + response.setStatus(Status.valueOf(CAPTURE_HEADERS.getStatus()), CAPTURE_HEADERS.getBody()) + } + } + }) + + attachAndWrap(INDEXED_CHILD.path, new Restlet() { + @Override + void handle(Request request, Response response) { + controller(INDEXED_CHILD) { + INDEXED_CHILD.collectSpanAttributes { request.getOriginalRef().getQueryAsForm().getFirst(it).getValue() } + response.setStatus(Status.valueOf(INDEXED_CHILD.status)) + } + } + }) + + } + + @Override + List> extraAttributes() { + [ + SemanticAttributes.NET_TRANSPORT + ] + } + + @Override + boolean testPathParam() { + true + } + + @Override + String expectedServerSpanName(ServerEndpoint endpoint) { + switch (endpoint) { + case PATH_PARAM: + return getContextPath() + "/path/{id}/param" + case NOT_FOUND: + return getContextPath() + "/*" + default: + return endpoint.resolvePath(address).path + } + } + + Restlet wrapRestlet(Restlet restlet, String path) { + return restlet + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 6c7a533a2563..7ce7bdcd9ee0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -292,6 +292,9 @@ include(":instrumentation:redisson-3.0:javaagent") include(":instrumentation:restlet:restlet-1.0:javaagent") include(":instrumentation:restlet:restlet-1.0:library") include(":instrumentation:restlet:restlet-1.0:testing") +include(":instrumentation:restlet:restlet-2.0:javaagent") +include(":instrumentation:restlet:restlet-2.0:library") +include(":instrumentation:restlet:restlet-2.0:testing") include(":instrumentation:rmi:bootstrap") include(":instrumentation:rmi:javaagent") include(":instrumentation:rocketmq-client-4.8:javaagent") From 58badc1a73b1398f8bc947d68d68efb53ffbc12e Mon Sep 17 00:00:00 2001 From: anosek Date: Thu, 4 Nov 2021 16:11:28 +0100 Subject: [PATCH 2/3] add restlet 2.0 instrumentation --- .../restlet-2.0/javaagent/build.gradle.kts | 2 +- .../restlet/v2_0/RestletHeadersGetter.java | 9 +------ .../restlet/v2_0/RestletTracingBuilder.java | 2 ++ .../restlet/v2_0/TracingFilter.java | 25 ++++++++++++------- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts b/instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts index 0600ea91ac17..2f7309394fa3 100644 --- a/instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts +++ b/instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts @@ -6,7 +6,7 @@ muzzle { pass { group.set("org.restlet") module.set("org.restlet.jse") - versions.set("[2.0.0, 2.5-M1]") + versions.set("[2.0.0,)") assertInverse.set(true) } } diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHeadersGetter.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHeadersGetter.java index 88672daaacc3..4425e0b774fb 100644 --- a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHeadersGetter.java +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHeadersGetter.java @@ -6,7 +6,6 @@ package io.opentelemetry.instrumentation.restlet.v2_0; import io.opentelemetry.context.propagation.TextMapGetter; -import java.util.Locale; import org.restlet.Message; import org.restlet.Request; import org.restlet.util.Series; @@ -20,14 +19,8 @@ public Iterable keys(Request carrier) { @Override public String get(Request carrier, String key) { - Series headers = getHeaders(carrier); - - String value = headers.getFirstValue(key); - if (value != null) { - return value; - } - return headers.getFirstValue(key.toLowerCase(Locale.ROOT)); + return headers.getFirstValue(key, /* ignoreCase = */ true); } static Series getHeaders(Message carrier) { diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracingBuilder.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracingBuilder.java index 4bfbfa49c930..970e886d7703 100644 --- a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracingBuilder.java +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracingBuilder.java @@ -12,6 +12,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.CapturedHttpHeaders; +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 io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; @@ -76,6 +77,7 @@ public RestletTracing build() { .addAttributesExtractor(httpAttributesExtractor) .addAttributesExtractor(netAttributesExtractor) .addAttributesExtractors(additionalExtractors) + .addRequestMetrics(HttpServerMetrics.get()) .newServerInstrumenter(new RestletHeadersGetter()); return new RestletTracing(instrumenter); diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/TracingFilter.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/TracingFilter.java index eba8d77c18c5..0b2eb0ae01ef 100644 --- a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/TracingFilter.java +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/TracingFilter.java @@ -14,6 +14,7 @@ import io.opentelemetry.instrumentation.restlet.v2_0.internal.RestletServerSpanNaming; import org.restlet.Request; import org.restlet.Response; +import org.restlet.resource.ResourceException; import org.restlet.routing.Filter; final class TracingFilter extends Filter { @@ -46,20 +47,26 @@ public int doHandle(Request request, Response response) { try { super.doHandle(request, response); } catch (Throwable t) { + statusThrowable = t; - } - if (scope == null) { - return CONTINUE; - } + if (t instanceof Error || t instanceof RuntimeException) { + throw t; + } else { + throw new ResourceException(t); + } - scope.close(); + } finally { - if (response.getStatus() != null && response.getStatus().isError()) { - statusThrowable = response.getStatus().getThrowable(); - } + if (scope != null) { - instrumenter.end(context, request, response, statusThrowable); + scope.close(); + if (response.getStatus() != null && response.getStatus().isError()) { + statusThrowable = response.getStatus().getThrowable(); + } + instrumenter.end(context, request, response, statusThrowable); + } + } return CONTINUE; } From 0bcb9ba74b6325c1948e429a39eed71ffa688561 Mon Sep 17 00:00:00 2001 From: anosek Date: Tue, 9 Nov 2021 16:10:42 +0100 Subject: [PATCH 3/3] revies: testLibrary, create RestletInstrumenterFactory --- .../restlet-2.0/javaagent/build.gradle.kts | 2 +- .../restlet/v2_0/RestletSingletons.java | 4 +- .../restlet-2.0/library/build.gradle.kts | 2 +- .../restlet/v2_0/RestletTracing.java | 5 -- .../restlet/v2_0/RestletTracingBuilder.java | 31 ++-------- .../{ => internal}/RestletHeadersGetter.java | 2 +- .../RestletHttpAttributesExtractor.java | 4 +- .../internal/RestletInstrumenterFactory.java | 57 +++++++++++++++++++ .../RestletNetAttributesExtractor.java | 2 +- 9 files changed, 70 insertions(+), 39 deletions(-) rename instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/{ => internal}/RestletHeadersGetter.java (91%) rename instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/{ => internal}/RestletHttpAttributesExtractor.java (94%) create mode 100644 instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletInstrumenterFactory.java rename instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/{ => internal}/RestletNetAttributesExtractor.java (93%) diff --git a/instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts b/instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts index 2f7309394fa3..fede6dbabafd 100644 --- a/instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts +++ b/instrumentation/restlet/restlet-2.0/javaagent/build.gradle.kts @@ -25,7 +25,7 @@ dependencies { implementation(project(":instrumentation:restlet:restlet-2.0:library")) testImplementation(project(":instrumentation:restlet:restlet-2.0:testing")) - testImplementation("org.restlet.jse:org.restlet.ext.jetty:2.0.2") + testLibrary("org.restlet.jse:org.restlet.ext.jetty:2.0.2") } // restlet registers the first engine that is present on classpath, so we need to enforce the appropriate version diff --git a/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java b/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java index edf729bea309..2ab53a864832 100644 --- a/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java +++ b/instrumentation/restlet/restlet-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/restlet/v2_0/RestletSingletons.java @@ -7,14 +7,14 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.restlet.v2_0.RestletTracing; +import io.opentelemetry.instrumentation.restlet.v2_0.internal.RestletInstrumenterFactory; import org.restlet.Request; import org.restlet.Response; public final class RestletSingletons { private static final Instrumenter INSTRUMENTER = - RestletTracing.create(GlobalOpenTelemetry.get()).getServerInstrumenter(); + RestletInstrumenterFactory.newServerInstrumenter(GlobalOpenTelemetry.get()); public static Instrumenter instrumenter() { return INSTRUMENTER; diff --git a/instrumentation/restlet/restlet-2.0/library/build.gradle.kts b/instrumentation/restlet/restlet-2.0/library/build.gradle.kts index 76d6539ba579..2c5dc577f6e8 100644 --- a/instrumentation/restlet/restlet-2.0/library/build.gradle.kts +++ b/instrumentation/restlet/restlet-2.0/library/build.gradle.kts @@ -13,7 +13,7 @@ dependencies { library("org.restlet.jse:org.restlet:2.0.2") testImplementation(project(":instrumentation:restlet:restlet-2.0:testing")) - testImplementation("org.restlet.jse:org.restlet.ext.jetty:2.0.2") + testLibrary("org.restlet.jse:org.restlet.ext.jetty:2.0.2") } // restlet registers the first engine that is present on classpath, so we need to enforce the appropriate version if (findProperty("testLatestDeps") as Boolean) { diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracing.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracing.java index 6dc16d5dfd72..2140efb4eb29 100644 --- a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracing.java +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracing.java @@ -39,9 +39,4 @@ public static RestletTracingBuilder builder(OpenTelemetry openTelemetry) { public Filter newFilter(String path) { return new TracingFilter(serverInstrumenter, path); } - - /** Returns a server {@link Instrumenter}. */ - public Instrumenter getServerInstrumenter() { - return serverInstrumenter; - } } diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracingBuilder.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracingBuilder.java index 970e886d7703..b11d7b5e16bb 100644 --- a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracingBuilder.java +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletTracingBuilder.java @@ -9,13 +9,8 @@ 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.SpanNameExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor; import io.opentelemetry.instrumentation.api.instrumenter.http.CapturedHttpHeaders; -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 io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; +import io.opentelemetry.instrumentation.restlet.v2_0.internal.RestletInstrumenterFactory; import java.util.ArrayList; import java.util.List; import org.restlet.Request; @@ -24,8 +19,6 @@ /** A builder of {@link RestletTracing}. */ public final class RestletTracingBuilder { - private static final String INSTRUMENTATION_NAME = "io.opentelemetry.restlet-2.0"; - private final OpenTelemetry openTelemetry; private final List> additionalExtractors = new ArrayList<>(); @@ -61,25 +54,11 @@ public RestletTracingBuilder captureHttpHeaders(CapturedHttpHeaders capturedHttp * Returns a new {@link RestletTracing} with the settings of this {@link RestletTracingBuilder}. */ public RestletTracing build() { - RestletHttpAttributesExtractor httpAttributesExtractor = - new RestletHttpAttributesExtractor(capturedHttpHeaders); - SpanNameExtractor spanNameExtractor = - HttpSpanNameExtractor.create(httpAttributesExtractor); - SpanStatusExtractor spanStatusExtractor = - HttpSpanStatusExtractor.create(httpAttributesExtractor); - NetServerAttributesExtractor netAttributesExtractor = - new RestletNetAttributesExtractor(); - Instrumenter instrumenter = - Instrumenter.builder( - openTelemetry, INSTRUMENTATION_NAME, spanNameExtractor) - .setSpanStatusExtractor(spanStatusExtractor) - .addAttributesExtractor(httpAttributesExtractor) - .addAttributesExtractor(netAttributesExtractor) - .addAttributesExtractors(additionalExtractors) - .addRequestMetrics(HttpServerMetrics.get()) - .newServerInstrumenter(new RestletHeadersGetter()); + Instrumenter serverInstrumenter = + RestletInstrumenterFactory.newServerInstrumenter( + openTelemetry, capturedHttpHeaders, additionalExtractors); - return new RestletTracing(instrumenter); + return new RestletTracing(serverInstrumenter); } } diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHeadersGetter.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletHeadersGetter.java similarity index 91% rename from instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHeadersGetter.java rename to instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletHeadersGetter.java index 4425e0b774fb..8000838585c3 100644 --- a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHeadersGetter.java +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletHeadersGetter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.restlet.v2_0; +package io.opentelemetry.instrumentation.restlet.v2_0.internal; import io.opentelemetry.context.propagation.TextMapGetter; import org.restlet.Message; diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHttpAttributesExtractor.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletHttpAttributesExtractor.java similarity index 94% rename from instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHttpAttributesExtractor.java rename to instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletHttpAttributesExtractor.java index 79b4d6bc06a7..158a21ebd5d4 100644 --- a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletHttpAttributesExtractor.java +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletHttpAttributesExtractor.java @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.restlet.v2_0; +package io.opentelemetry.instrumentation.restlet.v2_0.internal; -import static io.opentelemetry.instrumentation.restlet.v2_0.RestletHeadersGetter.getHeaders; +import static io.opentelemetry.instrumentation.restlet.v2_0.internal.RestletHeadersGetter.getHeaders; import io.opentelemetry.instrumentation.api.instrumenter.http.CapturedHttpHeaders; import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractor; diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletInstrumenterFactory.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletInstrumenterFactory.java new file mode 100644 index 000000000000..c02ae785dd95 --- /dev/null +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletInstrumenterFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.restlet.v2_0.internal; + +import io.opentelemetry.api.OpenTelemetry; +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.SpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.http.CapturedHttpHeaders; +import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractor; +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 io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; +import java.util.Collections; +import java.util.List; +import org.restlet.Request; +import org.restlet.Response; + +public class RestletInstrumenterFactory { + + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.restlet-2.0"; + + public static Instrumenter newServerInstrumenter(OpenTelemetry openTelemetry) { + return newServerInstrumenter( + openTelemetry, CapturedHttpHeaders.server(Config.get()), Collections.emptyList()); + } + + public static Instrumenter newServerInstrumenter( + OpenTelemetry openTelemetry, + CapturedHttpHeaders capturedHttpHeaders, + List> additionalExtractors) { + + HttpServerAttributesExtractor httpAttributesExtractor = + new RestletHttpAttributesExtractor(capturedHttpHeaders); + SpanNameExtractor spanNameExtractor = + HttpSpanNameExtractor.create(httpAttributesExtractor); + SpanStatusExtractor spanStatusExtractor = + HttpSpanStatusExtractor.create(httpAttributesExtractor); + NetServerAttributesExtractor netAttributesExtractor = + new RestletNetAttributesExtractor(); + + return Instrumenter.builder( + openTelemetry, INSTRUMENTATION_NAME, spanNameExtractor) + .setSpanStatusExtractor(spanStatusExtractor) + .addAttributesExtractor(httpAttributesExtractor) + .addAttributesExtractor(netAttributesExtractor) + .addAttributesExtractors(additionalExtractors) + .addRequestMetrics(HttpServerMetrics.get()) + .newServerInstrumenter(new RestletHeadersGetter()); + } +} diff --git a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletNetAttributesExtractor.java b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletNetAttributesExtractor.java similarity index 93% rename from instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletNetAttributesExtractor.java rename to instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletNetAttributesExtractor.java index 7f78d8c0d27e..157771f0c556 100644 --- a/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/RestletNetAttributesExtractor.java +++ b/instrumentation/restlet/restlet-2.0/library/src/main/java/io/opentelemetry/instrumentation/restlet/v2_0/internal/RestletNetAttributesExtractor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.restlet.v2_0; +package io.opentelemetry.instrumentation.restlet.v2_0.internal; import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesExtractor; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;