Convert jsf to instrumenter api (#3838)

* Convert jsf to instrumenter api

* Review comments

* Use span name not null
This commit is contained in:
Lauri Tulmin 2021-08-16 20:06:04 +03:00 committed by GitHub
parent 1a9896543a
commit 912ce91b60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 215 additions and 141 deletions

View File

@ -0,0 +1,20 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.jsf;
import io.opentelemetry.instrumentation.api.instrumenter.ErrorCauseExtractor;
import javax.faces.FacesException;
public class JsfErrorCauseExtractor implements ErrorCauseExtractor {
@Override
public Throwable extractCause(Throwable error) {
while (error.getCause() != null && error instanceof FacesException) {
error = error.getCause();
}
return ErrorCauseExtractor.jdk().extractCause(error);
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.jsf;
import java.util.Objects;
import javax.faces.component.ActionSource2;
import javax.faces.event.ActionEvent;
public class JsfRequest {
private final String spanName;
public JsfRequest(ActionEvent event) {
this.spanName = getSpanName(event);
}
public String spanName() {
return Objects.requireNonNull(spanName);
}
public boolean shouldStartSpan() {
return spanName != null;
}
private static String getSpanName(ActionEvent event) {
// https://jakarta.ee/specifications/faces/2.3/apidocs/index.html?javax/faces/component/ActionSource2.html
// ActionSource2 was added in JSF 1.2 and is implemented by components that have an action
// attribute such as a button or a link
if (event.getComponent() instanceof ActionSource2) {
ActionSource2 actionSource = (ActionSource2) event.getComponent();
if (actionSource.getActionExpression() != null) {
// either an el expression in the form #{bean.method()} or navigation case name
String expressionString = actionSource.getActionExpression().getExpressionString();
// start span only if expression string is really an expression
if (expressionString.startsWith("#{") || expressionString.startsWith("${")) {
return expressionString;
}
}
}
return null;
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.jsf;
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTROLLER;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
public class JsfServerSpanNaming {
public static void updateServerSpanName(FacesContext facesContext) {
Context parentContext = Context.current();
ServerSpanNaming.updateServerSpanName(
parentContext, CONTROLLER, () -> getServerSpanName(parentContext, facesContext));
}
private static String getServerSpanName(Context context, FacesContext facesContext) {
UIViewRoot uiViewRoot = facesContext.getViewRoot();
if (uiViewRoot == null) {
return null;
}
// JSF spec 7.6.2
// view id is a context relative path to the web application resource that produces the view,
// such as a JSP page or a Facelets page.
String viewId = uiViewRoot.getViewId();
return ServletContextPath.prepend(context, viewId);
}
}

View File

@ -1,65 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.jsf;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
import io.opentelemetry.instrumentation.api.tracer.ServerSpan;
import javax.faces.FacesException;
import javax.faces.component.ActionSource2;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
public abstract class JsfTracer extends BaseTracer {
public Context startSpan(ActionEvent event) {
// https://jakarta.ee/specifications/faces/2.3/apidocs/index.html?javax/faces/component/ActionSource2.html
// ActionSource2 was added in JSF 1.2 and is implemented by components that have an action
// attribute such as a button or a link
if (event.getComponent() instanceof ActionSource2) {
ActionSource2 actionSource = (ActionSource2) event.getComponent();
if (actionSource.getActionExpression() != null) {
// either an el expression in the form #{bean.method()} or navigation case name
String expressionString = actionSource.getActionExpression().getExpressionString();
// start span only if expression string is really an expression
if (expressionString.startsWith("#{") || expressionString.startsWith("${")) {
return startSpan(expressionString);
}
}
}
return null;
}
public void updateServerSpanName(Context context, FacesContext facesContext) {
Span serverSpan = ServerSpan.fromContextOrNull(context);
if (serverSpan == null) {
return;
}
UIViewRoot uiViewRoot = facesContext.getViewRoot();
if (uiViewRoot == null) {
return;
}
// JSF spec 7.6.2
// view id is a context relative path to the web application resource that produces the view,
// such as a JSP page or a Facelets page.
String viewId = uiViewRoot.getViewId();
serverSpan.updateName(ServletContextPath.prepend(context, viewId));
}
@Override
protected Throwable unwrapThrowable(Throwable throwable) {
while (throwable.getCause() != null && throwable instanceof FacesException) {
throwable = throwable.getCause();
}
return super.unwrapThrowable(throwable);
}
}

View File

@ -5,13 +5,15 @@
package io.opentelemetry.javaagent.instrumentation.mojarra;
import static io.opentelemetry.javaagent.instrumentation.mojarra.MojarraTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.mojarra.MojarraSingletons.instrumenter;
import static net.bytebuddy.matcher.ElementMatchers.named;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.jsf.JsfRequest;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import javax.faces.event.ActionEvent;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
@ -37,28 +39,31 @@ public class ActionListenerImplInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(0) ActionEvent event,
@Advice.Local("otelRequest") JsfRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
context = tracer().startSpan(event);
if (context != null) {
scope = context.makeCurrent();
Context parentContext = Java8BytecodeBridge.currentContext();
request = new JsfRequest(event);
if (!request.shouldStartSpan() || !instrumenter().shouldStart(parentContext, request)) {
return;
}
context = instrumenter().start(parentContext, request);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") JsfRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
tracer().endExceptionally(context, throwable);
} else {
tracer().end(context);
}
instrumenter().end(context, request, null, throwable);
}
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.mojarra;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.jsf.JsfErrorCauseExtractor;
import io.opentelemetry.instrumentation.jsf.JsfRequest;
public class MojarraSingletons {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.mojarra-1.2";
private static final Instrumenter<JsfRequest, Void> INSTRUMENTER;
static {
INSTRUMENTER =
Instrumenter.<JsfRequest, Void>newBuilder(
GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, JsfRequest::spanName)
.setErrorCauseExtractor(new JsfErrorCauseExtractor())
.newInstrumenter();
}
public static Instrumenter<JsfRequest, Void> instrumenter() {
return INSTRUMENTER;
}
private MojarraSingletons() {}
}

View File

@ -1,21 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.mojarra;
import io.opentelemetry.instrumentation.jsf.JsfTracer;
public class MojarraTracer extends JsfTracer {
private static final MojarraTracer TRACER = new MojarraTracer();
public static MojarraTracer tracer() {
return TRACER;
}
@Override
protected String getInstrumentationName() {
return "io.opentelemetry.mojarra-1.2";
}
}

View File

@ -5,13 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.mojarra;
import static io.opentelemetry.javaagent.instrumentation.mojarra.MojarraTracer.tracer;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.opentelemetry.instrumentation.jsf.JsfServerSpanNaming;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import javax.faces.context.FacesContext;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
@ -36,7 +35,7 @@ public class RestoreViewPhaseInstrumentation implements TypeInstrumentation {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(@Advice.Argument(0) FacesContext facesContext) {
tracer().updateServerSpanName(Java8BytecodeBridge.currentContext(), facesContext);
JsfServerSpanNaming.updateServerSpanName(facesContext);
}
}
}

View File

@ -5,13 +5,15 @@
package io.opentelemetry.javaagent.instrumentation.myfaces;
import static io.opentelemetry.javaagent.instrumentation.myfaces.MyFacesTracer.tracer;
import static io.opentelemetry.javaagent.instrumentation.myfaces.MyFacesSingletons.instrumenter;
import static net.bytebuddy.matcher.ElementMatchers.named;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.jsf.JsfRequest;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import javax.faces.event.ActionEvent;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
@ -37,28 +39,31 @@ public class ActionListenerImplInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.Argument(0) ActionEvent event,
@Advice.Local("otelRequest") JsfRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
context = tracer().startSpan(event);
if (context != null) {
scope = context.makeCurrent();
Context parentContext = Java8BytecodeBridge.currentContext();
request = new JsfRequest(event);
if (!request.shouldStartSpan() || !instrumenter().shouldStart(parentContext, request)) {
return;
}
context = instrumenter().start(parentContext, request);
scope = context.makeCurrent();
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopSpan(
@Advice.Thrown Throwable throwable,
@Advice.Local("otelRequest") JsfRequest request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope == null) {
return;
}
scope.close();
if (throwable != null) {
tracer().endExceptionally(context, throwable);
} else {
tracer().end(context);
}
instrumenter().end(context, request, null, throwable);
}
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.myfaces;
import io.opentelemetry.instrumentation.jsf.JsfErrorCauseExtractor;
import javax.el.ELException;
public class MyFacesErrorCauseExtractor extends JsfErrorCauseExtractor {
@Override
public Throwable extractCause(Throwable error) {
error = super.extractCause(error);
while (error.getCause() != null && error instanceof ELException) {
error = error.getCause();
}
return error;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.myfaces;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.jsf.JsfRequest;
public class MyFacesSingletons {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.myfaces-1.2";
private static final Instrumenter<JsfRequest, Void> INSTRUMENTER;
static {
INSTRUMENTER =
Instrumenter.<JsfRequest, Void>newBuilder(
GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, JsfRequest::spanName)
.setErrorCauseExtractor(new MyFacesErrorCauseExtractor())
.newInstrumenter();
}
public static Instrumenter<JsfRequest, Void> instrumenter() {
return INSTRUMENTER;
}
private MyFacesSingletons() {}
}

View File

@ -1,31 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.myfaces;
import io.opentelemetry.instrumentation.jsf.JsfTracer;
import javax.el.ELException;
public class MyFacesTracer extends JsfTracer {
private static final MyFacesTracer TRACER = new MyFacesTracer();
public static MyFacesTracer tracer() {
return TRACER;
}
@Override
protected Throwable unwrapThrowable(Throwable throwable) {
throwable = super.unwrapThrowable(throwable);
while (throwable.getCause() != null && throwable instanceof ELException) {
throwable = throwable.getCause();
}
return throwable;
}
@Override
protected String getInstrumentationName() {
return "io.opentelemetry.myfaces-1.2";
}
}

View File

@ -5,13 +5,12 @@
package io.opentelemetry.javaagent.instrumentation.myfaces;
import static io.opentelemetry.javaagent.instrumentation.myfaces.MyFacesTracer.tracer;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.opentelemetry.instrumentation.jsf.JsfServerSpanNaming;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import javax.faces.context.FacesContext;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
@ -36,7 +35,7 @@ public class RestoreViewExecutorInstrumentation implements TypeInstrumentation {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void onExit(@Advice.Argument(0) FacesContext facesContext) {
tracer().updateServerSpanName(Java8BytecodeBridge.currentContext(), facesContext);
JsfServerSpanNaming.updateServerSpanName(facesContext);
}
}
}