Skip to content

Commit

Permalink
server instrumentation with first tests
Browse files Browse the repository at this point in the history
  • Loading branch information
anosek-an committed Aug 25, 2021
1 parent f2605f9 commit 89e6485
Show file tree
Hide file tree
Showing 13 changed files with 612 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
plugins {
id("otel.javaagent-instrumentation")
}

muzzle {
pass {
group.set("org.restlet")
module.set("org.restlet")
versions.set("[1.0.0, 1.2.0)")
}
}

repositories {
mavenCentral()
maven("https://maven.restlet.talend.com/")
mavenLocal()
}

dependencies {
api(project(":instrumentation:restlet:restlet-core-1.1:library"))

library("org.restlet:org.restlet:1.1.5")
library("com.noelios.restlet:com.noelios.restlet:1.1.5")
implementation(project(":instrumentation:restlet:restlet-core-1.1:library"))
testImplementation(project(":instrumentation:restlet:restlet-core-1.1:testing"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.restlet.v1_1;

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-core", "restlet-core-1.1");
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return Arrays.asList(new ServerInstrumentation(), new RouteInstrumentation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.restlet.v1_1;

import static io.opentelemetry.instrumentation.restlet.v1_1.RestletHttpServerTracer.tracer;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import io.opentelemetry.api.trace.Span;
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.Route;
import org.restlet.data.Request;

public class RouteInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.restlet.Route");
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(named("beforeHandle"))
.and(takesArgument(0, named("org.restlet.data.Request")))
.and(takesArgument(1, named("org.restlet.data.Response"))),
this.getClass().getName() + "$RouteBeforeHandleAdvice");
}

@SuppressWarnings("unused")
public static class RouteBeforeHandleAdvice {

@Advice.OnMethodEnter(suppress = Throwable.class)
public static void getRouteInfo(@Advice.This Route route, @Advice.Argument(0) Request request) {
String pattern = route.getTemplate().getPattern();
if (pattern == null || pattern.equals("")) {
return;
}

Span serverSpan = tracer().getServerSpan(request);

if (serverSpan == null) {
return;
}

serverSpan.updateName(pattern);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.restlet.v1_1;

import static io.opentelemetry.instrumentation.restlet.v1_1.RestletHttpServerTracer.tracer;
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.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.data.Request;
import org.restlet.data.Response;

public class ServerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.restlet.Server");
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(named("handle"))
.and(takesArgument(0, named("org.restlet.data.Request")))
.and(takesArgument(1, named("org.restlet.data.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("otelScope") Scope scope) {

Context serverContext = tracer().getServerContext(request);

if (serverContext != null) {
return;
}

serverContext =
tracer().startSpan(request, request, request, request.getResourceRef().getPath());
scope = serverContext.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("otelScope") Scope scope) {

Context serverContext = tracer().getServerContext(request);

if (scope == null) {
return;
}

scope.close();

if (serverContext == null) {
return;
}

if (exception != null) {
tracer().endExceptionally(serverContext, exception, response);
return;
}

// Restlet suppresses exceptions and sets the throwable in status
Throwable statusThrowable = response.getStatus().getThrowable();

if (statusThrowable != null) {
tracer().endExceptionally(serverContext, statusThrowable, response);
return;
}

tracer().end(serverContext, response);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

import io.opentelemetry.instrumentation.restlet.v1_1.AbstractRestletServerTest
import io.opentelemetry.instrumentation.test.AgentTestTrait
import org.restlet.Restlet

class RestletServerTest extends AbstractRestletServerTest implements AgentTestTrait {

@Override
Restlet wrapRestlet(Restlet restlet, String path){
return restlet
}

}
17 changes: 17 additions & 0 deletions instrumentation/restlet/restlet-core-1.1/library/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
plugins {
id("otel.library-instrumentation")
}

repositories {
mavenCentral()
maven("https://maven.restlet.talend.com/")
mavenLocal()
}

dependencies {

library("org.restlet:org.restlet:1.1.5")
library("com.noelios.restlet:com.noelios.restlet:1.1.5")

testImplementation(project(":instrumentation:restlet:restlet-core-1.1:testing"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.restlet.v1_1;

import java.util.Locale;
import org.restlet.data.Form;
import org.restlet.data.Request;

final class HeadersAdapter {

static Form getHeaders(Request request) {
return (Form) request.getAttributes().get("org.restlet.http.headers");
}

static String getValue(Request request, String name) {
Form headers = getHeaders(request);
String value = headers.getFirstValue(name);
if (value != null) {
return value;
}
return headers.getFirstValue(name.toLowerCase(Locale.ROOT));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.restlet.v1_1;

import io.opentelemetry.context.propagation.TextMapGetter;
import org.restlet.data.Request;

final class RestletExtractAdapter implements TextMapGetter<Request> {

static final RestletExtractAdapter GETTER = new RestletExtractAdapter();

@Override
public Iterable<String> keys(Request carrier) {
return HeadersAdapter.getHeaders(carrier).getNames();
}

@Override
public String get(Request carrier, String key) {
return HeadersAdapter.getValue(carrier, key);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.restlet.v1_1;

import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapGetter;
import io.opentelemetry.instrumentation.api.tracer.HttpServerTracer;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.restlet.data.Request;
import org.restlet.data.Response;

public class RestletHttpServerTracer extends HttpServerTracer<Request, Response, Request, Request> {

private static final RestletHttpServerTracer TRACER = new RestletHttpServerTracer();

public static RestletHttpServerTracer tracer() {
return TRACER;
}

@Override
protected String getInstrumentationName() {
return "io.opentelemetry.restlet-core-1.1";
}

@Override
public @Nullable Context getServerContext(Request request) {
Object context = request.getAttributes().get(CONTEXT_ATTRIBUTE);
return context instanceof Context ? (Context) context : null;
}

@Override
protected @Nullable Integer peerPort(Request request) {
return request.getClientInfo().getPort();
}

@Override
protected @Nullable String peerHostIp(Request request) {
return request.getClientInfo().getAddress();
}

@Override
protected String flavor(Request connection, Request request) {
return (String) request.getAttributes().get("org.restlet.http.version");
}

@Override
protected TextMapGetter<Request> getGetter() {
return RestletExtractAdapter.GETTER;
}

@Override
protected String url(Request request) {
return request.getOriginalRef().toString();
}

@Override
protected String method(Request request) {
return request.getMethod().getName();
}

@Override
protected @Nullable String requestHeader(Request request, String name) {
return HeadersAdapter.getValue(request, name);
}

@Override
protected int responseStatus(Response response) {
return response.getStatus().getCode();
}

@Override
protected void attachServerContext(Context context, Request request) {
request.getAttributes().put(CONTEXT_ATTRIBUTE, context);
}
}
Loading

0 comments on commit 89e6485

Please sign in to comment.