Convert vaadin instrumentation to instrumenter api (#4588)
* Convert vaadin instrumentation to instrumenter api * address review comments
This commit is contained in:
parent
f3cbf25a89
commit
123de52170
|
@ -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")
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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() {}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue