Convert vaadin instrumentation to instrumenter api (#4588)

* Convert vaadin instrumentation to instrumenter api

* address review comments
This commit is contained in:
Lauri Tulmin 2021-11-04 21:00:14 +02:00 committed by GitHub
parent f3cbf25a89
commit 123de52170
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 438 additions and 189 deletions

View File

@ -52,6 +52,8 @@ tasks {
dependencies {
compileOnly("com.vaadin:flow-server:2.2.0")
compileOnly("com.google.auto.value:auto-value-annotations")
annotationProcessor("com.google.auto.value:auto-value")
add("vaadin16TestImplementation", "com.vaadin:vaadin-spring-boot-starter:16.0.0")
add("vaadin142TestImplementation", "com.vaadin:vaadin-spring-boot-starter:14.2.0")

View File

@ -0,0 +1,35 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.vaadin;
import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor;
import javax.annotation.Nullable;
public class ClientCallableCodeAttributesExtractor
extends CodeAttributesExtractor<VaadinClientCallableRequest, Void> {
@Override
protected Class<?> codeClass(VaadinClientCallableRequest request) {
return request.getComponentClass();
}
@Override
protected String methodName(VaadinClientCallableRequest request) {
return request.getMethodName();
}
@Override
@Nullable
protected String filePath(VaadinClientCallableRequest request) {
return null;
}
@Override
@Nullable
protected Long lineNumber(VaadinClientCallableRequest request) {
return null;
}
}

View File

@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.vaadin;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinSingletons.clientCallableInstrumenter;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
@ -13,6 +13,7 @@ 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 io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@ -44,21 +45,32 @@ public class ClientCallableRpcInstrumentation implements TypeInstrumentation {
public static void onEnter(
@Advice.Argument(1) Class<?> componentClass,
@Advice.Argument(2) String methodName,
@Advice.Local("otelRequest") VaadinClientCallableRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
context = tracer().startClientCallableSpan(componentClass, methodName);
Context parentContext = Java8BytecodeBridge.currentContext();
request = VaadinClientCallableRequest.create(componentClass, methodName);
if (!clientCallableInstrumenter().shouldStart(parentContext, request)) {
return;
}
context = clientCallableInstrumenter().start(parentContext, request);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") VaadinClientCallableRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
tracer().endSpan(context, throwable);
clientCallableInstrumenter().end(context, request, null, throwable);
}
}
}

View File

@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.vaadin;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.vaadin.flow.component.UI;
@ -34,7 +34,7 @@ public class JavaScriptBootstrapUiInstrumentation implements TypeInstrumentation
@Advice.OnMethodExit(suppress = Throwable.class)
public static void onExit(@Advice.This UI ui) {
tracer().updateServerSpanName(ui);
helper().updateServerSpanName(ui);
}
}
}

View File

@ -7,7 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.vaadin;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
@ -16,7 +16,6 @@ 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 java.lang.reflect.Method;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@ -50,11 +49,13 @@ public class RequestHandlerInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.This RequestHandler requestHandler,
@Advice.Origin Method method,
@Advice.Origin("#m") String methodName,
@Advice.Local("otelRequest") VaadinHandlerRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
context = tracer().startRequestHandlerSpan(requestHandler, method);
request = VaadinHandlerRequest.create(requestHandler.getClass(), methodName);
context = helper().startRequestHandlerSpan(request);
if (context != null) {
scope = context.makeCurrent();
}
@ -64,6 +65,7 @@ public class RequestHandlerInstrumentation implements TypeInstrumentation {
public static void onExit(
@Advice.Thrown Throwable throwable,
@Advice.Return boolean handled,
@Advice.Local("otelRequest") VaadinHandlerRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
@ -71,7 +73,7 @@ public class RequestHandlerInstrumentation implements TypeInstrumentation {
}
scope.close();
tracer().endRequestHandlerSpan(context, throwable, handled);
helper().endRequestHandlerSpan(context, request, throwable, handled);
}
}
}

View File

@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.vaadin;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
@ -44,7 +44,7 @@ public class RouterInstrumentation implements TypeInstrumentation {
@Advice.Argument(1) Location location,
@Advice.Argument(2) NavigationTrigger navigationTrigger) {
if (navigationTrigger == NavigationTrigger.PAGE_LOAD) {
tracer().updateServerSpanName(location);
helper().updateServerSpanName(location);
}
}
}

View File

@ -7,7 +7,7 @@ package io.opentelemetry.javaagent.instrumentation.vaadin;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinSingletons.rpcInstrumenter;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
@ -17,6 +17,7 @@ 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 io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import java.lang.reflect.Method;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
@ -53,21 +54,32 @@ public class RpcInvocationHandlerInstrumentation implements TypeInstrumentation
@Advice.This RpcInvocationHandler rpcInvocationHandler,
@Advice.Origin Method method,
@Advice.Argument(1) JsonObject jsonObject,
@Advice.Local("otelRequest") VaadinRpcRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
context = tracer().startRpcInvocationHandlerSpan(rpcInvocationHandler, method, jsonObject);
Context parentContext = Java8BytecodeBridge.currentContext();
request = VaadinRpcRequest.create(rpcInvocationHandler, method, jsonObject);
if (!rpcInstrumenter().shouldStart(parentContext, request)) {
return;
}
context = rpcInstrumenter().start(parentContext, request);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") VaadinRpcRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
tracer().endSpan(context, throwable);
rpcInstrumenter().end(context, request, null, throwable);
}
}
}

View File

@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.vaadin;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
@ -38,7 +38,7 @@ public class UiInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(@Advice.Argument(0) UI ui) {
tracer().updateServerSpanName(ui);
helper().updateServerSpanName(ui);
}
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.vaadin;
import com.google.auto.value.AutoValue;
@AutoValue
public abstract class VaadinClientCallableRequest {
public static VaadinClientCallableRequest create(Class<?> componentClass, String methodName) {
return new AutoValue_VaadinClientCallableRequest(componentClass, methodName);
}
abstract Class<?> getComponentClass();
abstract String getMethodName();
}

View File

@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.vaadin;
import com.google.auto.value.AutoValue;
import io.opentelemetry.instrumentation.api.tracer.SpanNames;
@AutoValue
public abstract class VaadinHandlerRequest {
public static VaadinHandlerRequest create(Class<?> handlerClass, String methodName) {
return new AutoValue_VaadinHandlerRequest(handlerClass, methodName);
}
abstract Class<?> getHandlerClass();
abstract String getMethodName();
String getSpanName() {
return SpanNames.fromMethod(getHandlerClass(), getMethodName());
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.vaadin;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinSingletons.REQUEST_HANDLER_CONTEXT_KEY;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinSingletons.SERVICE_CONTEXT_KEY;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.router.Location;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
import javax.annotation.Nullable;
public class VaadinHelper {
private final Instrumenter<VaadinHandlerRequest, Void> requestHandlerInstrumenter;
private final Instrumenter<VaadinServiceRequest, Void> serviceInstrumenter;
public VaadinHelper(
Instrumenter<VaadinHandlerRequest, Void> requestHandlerInstrumenter,
Instrumenter<VaadinServiceRequest, Void> serviceInstrumenter) {
this.requestHandlerInstrumenter = requestHandlerInstrumenter;
this.serviceInstrumenter = serviceInstrumenter;
}
@Nullable
public Context startVaadinServiceSpan(VaadinServiceRequest request) {
Context parentContext = Context.current();
if (!serviceInstrumenter.shouldStart(parentContext, request)) {
return null;
}
return serviceInstrumenter.start(parentContext, request);
}
public void endVaadinServiceSpan(
Context context, VaadinServiceRequest request, Throwable throwable) {
serviceInstrumenter.end(context, request, null, throwable);
ServerSpanNaming.updateServerSpanName(
context,
ServerSpanNaming.Source.CONTROLLER,
(c, req) -> getSpanNameForVaadinServiceContext(c, req),
request);
}
private static String getSpanNameForVaadinServiceContext(
Context context, VaadinServiceRequest request) {
VaadinServiceContext vaadinServiceContext = context.get(SERVICE_CONTEXT_KEY);
// None of the request handlers processed the request, set span name to main request processing
// method name that calls request handlers.
if (!vaadinServiceContext.isRequestHandled()) {
return request.getSpanName();
}
// Name of the request handler that handled the request.
return vaadinServiceContext.getSpanNameCandidate();
}
@Nullable
public Context startRequestHandlerSpan(VaadinHandlerRequest request) {
Context parentContext = Context.current();
// ignore nested request handlers
if (parentContext.get(REQUEST_HANDLER_CONTEXT_KEY) != null) {
return null;
}
VaadinServiceContext vaadinServiceContext = parentContext.get(SERVICE_CONTEXT_KEY);
if (vaadinServiceContext != null && !vaadinServiceContext.isRequestHandled()) {
// We don't really know whether this request handler is going to be the one that processes
// the request, if it isn't then next handler will also update server span name candidate.
vaadinServiceContext.setSpanNameCandidate(request.getSpanName());
}
if (!requestHandlerInstrumenter.shouldStart(parentContext, request)) {
return null;
}
return requestHandlerInstrumenter.start(parentContext, request);
}
public void endRequestHandlerSpan(
Context context, VaadinHandlerRequest request, Throwable throwable, boolean handled) {
requestHandlerInstrumenter.end(context, request, null, throwable);
// request handler returns true when it processes the request, if that is the case then
// mark request as handled
if (handled) {
VaadinServiceContext vaadinServiceContext = context.get(SERVICE_CONTEXT_KEY);
if (vaadinServiceContext != null) {
vaadinServiceContext.setRequestHandled();
}
}
}
public void updateServerSpanName(UI ui) {
if (ui != null) {
Location location = ui.getInternals().getActiveViewLocation();
updateServerSpanName(location);
}
}
public void updateServerSpanName(Location location) {
Context context = Context.current();
ServerSpanNaming.updateServerSpanName(
context,
ServerSpanNaming.Source.NESTED_CONTROLLER,
(c, loc) -> ServletContextPath.prepend(c, getSpanNameForLocation(loc)),
location);
}
private static String getSpanNameForLocation(Location location) {
String path = location.getPath();
if (!path.isEmpty()) {
path = "/" + path;
}
return path;
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.vaadin;
import com.google.auto.value.AutoValue;
import com.vaadin.flow.server.communication.rpc.RpcInvocationHandler;
import elemental.json.JsonObject;
import java.lang.reflect.Method;
@AutoValue
public abstract class VaadinRpcRequest {
public static VaadinRpcRequest create(
RpcInvocationHandler rpcInvocationHandler, Method method, JsonObject jsonObject) {
return new AutoValue_VaadinRpcRequest(rpcInvocationHandler, method, jsonObject);
}
abstract RpcInvocationHandler getRpcInvocationHandler();
abstract Method getMethod();
abstract JsonObject getJsonObject();
}

View File

@ -0,0 +1,27 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.vaadin;
public final class VaadinServiceContext {
private boolean requestHandled;
private String spanNameCandidate;
void setRequestHandled() {
requestHandled = true;
}
boolean isRequestHandled() {
return requestHandled;
}
void setSpanNameCandidate(String spanNameCandidate) {
this.spanNameCandidate = spanNameCandidate;
}
String getSpanNameCandidate() {
return spanNameCandidate;
}
}

View File

@ -5,7 +5,7 @@
package io.opentelemetry.javaagent.instrumentation.vaadin;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.vaadin.VaadinSingletons.helper;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
@ -14,7 +14,6 @@ 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 java.lang.reflect.Method;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@ -42,21 +41,30 @@ public class VaadinServiceInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.This VaadinService vaadinService,
@Advice.Origin Method method,
@Advice.Origin("#m") String methodName,
@Advice.Local("otelRequest") VaadinServiceRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
context = tracer().startVaadinServiceSpan(vaadinService, method);
scope = context.makeCurrent();
request = VaadinServiceRequest.create(vaadinService.getClass(), methodName);
context = helper().startVaadinServiceSpan(request);
if (context != null) {
scope = context.makeCurrent();
}
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") VaadinServiceRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
tracer().endVaadinServiceSpan(context, throwable);
helper().endVaadinServiceSpan(context, request, throwable);
}
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.vaadin;
import com.google.auto.value.AutoValue;
import io.opentelemetry.instrumentation.api.tracer.SpanNames;
@AutoValue
public abstract class VaadinServiceRequest {
public static VaadinServiceRequest create(Class<?> serviceClass, String methodName) {
return new AutoValue_VaadinServiceRequest(serviceClass, methodName);
}
abstract Class<?> getServiceClass();
abstract String getMethodName();
String getSpanName() {
return SpanNames.fromMethod(getServiceClass(), getMethodName());
}
}

View File

@ -0,0 +1,98 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.vaadin;
import com.vaadin.flow.server.communication.rpc.RpcInvocationHandler;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.instrumentation.api.config.ExperimentalConfig;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.tracer.SpanNames;
public class VaadinSingletons {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.vaadin-14.2";
static final ContextKey<VaadinServiceContext> SERVICE_CONTEXT_KEY =
ContextKey.named("opentelemetry-vaadin-service-context-key");
static final ContextKey<Object> REQUEST_HANDLER_CONTEXT_KEY =
ContextKey.named("opentelemetry-vaadin-request-handler-context-key");
private static final Instrumenter<VaadinClientCallableRequest, Void> CLIENT_CALLABLE_INSTRUMENTER;
private static final Instrumenter<VaadinHandlerRequest, Void> REQUEST_HANDLER_INSTRUMENTER;
private static final Instrumenter<VaadinRpcRequest, Void> RPC_INSTRUMENTER;
private static final Instrumenter<VaadinServiceRequest, Void> SERVICE_INSTRUMENTER;
private static final VaadinHelper HELPER;
static {
CLIENT_CALLABLE_INSTRUMENTER =
Instrumenter.<VaadinClientCallableRequest, Void>builder(
GlobalOpenTelemetry.get(),
INSTRUMENTATION_NAME,
VaadinSingletons::clientCallableSpanName)
.setDisabled(ExperimentalConfig.get().suppressControllerSpans())
.addAttributesExtractor(new ClientCallableCodeAttributesExtractor())
.newInstrumenter();
REQUEST_HANDLER_INSTRUMENTER =
Instrumenter.<VaadinHandlerRequest, Void>builder(
GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, VaadinHandlerRequest::getSpanName)
.setDisabled(ExperimentalConfig.get().suppressControllerSpans())
// add context for tracking nested request handler calls
.addContextCustomizer(
(context, vaadinHandlerRequest, startAttributes) ->
context.with(REQUEST_HANDLER_CONTEXT_KEY, Boolean.TRUE))
.newInstrumenter();
RPC_INSTRUMENTER =
Instrumenter.<VaadinRpcRequest, Void>builder(
GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, VaadinSingletons::rpcSpanName)
.setDisabled(ExperimentalConfig.get().suppressControllerSpans())
.newInstrumenter();
SERVICE_INSTRUMENTER =
Instrumenter.<VaadinServiceRequest, Void>builder(
GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, VaadinServiceRequest::getSpanName)
.setDisabled(ExperimentalConfig.get().suppressControllerSpans())
// add context for tracking whether any request handler handled the request
.addContextCustomizer(
(context, vaadinServiceRequest, startAttributes) ->
context.with(SERVICE_CONTEXT_KEY, new VaadinServiceContext()))
.newInstrumenter();
HELPER = new VaadinHelper(REQUEST_HANDLER_INSTRUMENTER, SERVICE_INSTRUMENTER);
}
public static Instrumenter<VaadinClientCallableRequest, Void> clientCallableInstrumenter() {
return CLIENT_CALLABLE_INSTRUMENTER;
}
public static Instrumenter<VaadinRpcRequest, Void> rpcInstrumenter() {
return RPC_INSTRUMENTER;
}
public static VaadinHelper helper() {
return HELPER;
}
private static String clientCallableSpanName(VaadinClientCallableRequest request) {
return SpanNames.fromMethod(request.getComponentClass(), request.getMethodName());
}
private static String rpcSpanName(VaadinRpcRequest rpcRequest) {
RpcInvocationHandler rpcInvocationHandler = rpcRequest.getRpcInvocationHandler();
String spanName = SpanNames.fromMethod(rpcInvocationHandler.getClass(), rpcRequest.getMethod());
if ("event".equals(rpcInvocationHandler.getRpcType())) {
String eventType = rpcRequest.getJsonObject().getString("event");
if (eventType != null) {
// append event type to make span name more descriptive
spanName += "/" + eventType;
}
}
return spanName;
}
private VaadinSingletons() {}
}

View File

@ -1,166 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.vaadin;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.router.Location;
import com.vaadin.flow.server.RequestHandler;
import com.vaadin.flow.server.VaadinService;
import com.vaadin.flow.server.communication.rpc.RpcInvocationHandler;
import elemental.json.JsonObject;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import io.opentelemetry.instrumentation.api.tracer.SpanNames;
import java.lang.reflect.Method;
import javax.annotation.Nullable;
public class VaadinTracer extends BaseTracer {
private static final ContextKey<VaadinServiceContext> SERVICE_CONTEXT_KEY =
ContextKey.named("opentelemetry-vaadin-service-context-key");
private static final ContextKey<Object> REQUEST_HANDLER_CONTEXT_KEY =
ContextKey.named("opentelemetry-vaadin-request-handler-context-key");
private static final VaadinTracer TRACER = new VaadinTracer();
public static VaadinTracer tracer() {
return TRACER;
}
private VaadinTracer() {
super(GlobalOpenTelemetry.get());
}
public Context startVaadinServiceSpan(VaadinService vaadinService, Method method) {
String spanName = SpanNames.fromMethod(vaadinService.getClass(), method);
Context context = super.startSpan(spanName);
return context.with(SERVICE_CONTEXT_KEY, new VaadinServiceContext(spanName));
}
public void endSpan(Context context, Throwable throwable) {
if (throwable != null) {
endExceptionally(context, throwable);
} else {
end(context);
}
}
public void endVaadinServiceSpan(Context context, Throwable throwable) {
endSpan(context, throwable);
VaadinServiceContext vaadinServiceContext = context.get(SERVICE_CONTEXT_KEY);
if (!vaadinServiceContext.isRequestHandled()) {
// none of the request handlers processed the request
// as we update server span name on call to each request handler currently server span name
// is set based on the last request handler even when it didn't process the request, set
// server span name to main request processing method name
Span span = ServerSpan.fromContextOrNull(context);
if (span != null) {
span.updateName(vaadinServiceContext.vaadinServiceSpanName);
}
}
}
@Nullable
public Context startRequestHandlerSpan(RequestHandler requestHandler, Method method) {
Context current = Context.current();
// ignore nested request handlers
if (current.get(REQUEST_HANDLER_CONTEXT_KEY) != null) {
return null;
}
String spanName = SpanNames.fromMethod(requestHandler.getClass(), method);
VaadinServiceContext vaadinServiceContext = current.get(SERVICE_CONTEXT_KEY);
if (vaadinServiceContext != null && !vaadinServiceContext.isRequestHandled()) {
Span span = ServerSpan.fromContextOrNull(current);
if (span != null) {
// set server span name to request handler name
// we don't really know whether this request handler is going to be the one
// that process the request, if it isn't then next handler will also update
// server span name
span.updateName(spanName);
}
}
Context context = super.startSpan(spanName);
return context.with(REQUEST_HANDLER_CONTEXT_KEY, Boolean.TRUE);
}
public void endRequestHandlerSpan(Context context, Throwable throwable, boolean handled) {
endSpan(context, throwable);
// request handler returns true when it processes the request, if that is the case then
// mark request as handled
if (handled) {
VaadinServiceContext vaadinServiceContext = context.get(SERVICE_CONTEXT_KEY);
if (vaadinServiceContext != null) {
vaadinServiceContext.setRequestHandled();
}
}
}
public void updateServerSpanName(UI ui) {
if (ui != null) {
Location location = ui.getInternals().getActiveViewLocation();
updateServerSpanName(location);
}
}
public void updateServerSpanName(Location location) {
Context context = Context.current();
Span span = ServerSpan.fromContextOrNull(context);
if (span != null) {
String path = location.getPath();
if (!path.isEmpty()) {
path = "/" + path;
}
span.updateName(ServletContextPath.prepend(context, path));
}
}
public Context startClientCallableSpan(Class<?> componentClass, String methodName) {
return super.startSpan(SpanNames.fromMethod(componentClass, methodName));
}
public Context startRpcInvocationHandlerSpan(
RpcInvocationHandler rpcInvocationHandler, Method method, JsonObject jsonObject) {
String spanName = SpanNames.fromMethod(rpcInvocationHandler.getClass(), method);
if ("event".equals(rpcInvocationHandler.getRpcType())) {
String eventType = jsonObject.getString("event");
if (eventType != null) {
// append event type to make span name more descriptive
spanName += "/" + eventType;
}
}
return super.startSpan(spanName);
}
@Override
protected String getInstrumentationName() {
return "io.opentelemetry.vaadin-14.2";
}
private static class VaadinServiceContext {
final String vaadinServiceSpanName;
boolean requestHandled;
VaadinServiceContext(String vaadinServiceSpanName) {
this.vaadinServiceSpanName = vaadinServiceSpanName;
}
void setRequestHandled() {
requestHandled = true;
}
boolean isRequestHandled() {
return requestHandled;
}
}
}