Skip to content

Commit 6e4e36e

Browse files
pepeshoreotelbot[bot]lauritsteverao
authored
Add support for JFinal (#15216)
Co-authored-by: otelbot <197425009+otelbot@users.noreply.github.com> Co-authored-by: Lauri Tulmin <ltulmin@splunk.com> Co-authored-by: Steve Rao <raozihao.rzh@alibaba-inc.com> Co-authored-by: Lauri Tulmin <tulmin@gmail.com>
1 parent 509955b commit 6e4e36e

File tree

14 files changed

+588
-7
lines changed

14 files changed

+588
-7
lines changed

.fossa.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ targets:
187187
- type: gradle
188188
path: ./
189189
target: ':instrumentation:jdbc:library'
190+
- type: gradle
191+
path: ./
192+
target: ':instrumentation:jfinal-3.2:javaagent'
190193
- type: gradle
191194
path: ./
192195
target: ':instrumentation:jmx-metrics:javaagent'

docs/supported-libraries.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ These are the supported libraries and frameworks:
9696
| [JBoss Log Manager](https://github.com/jboss-logging/jboss-logmanager) | 1.1+ | N/A | none |
9797
| [JDBC](https://docs.oracle.com/javase/8/docs/api/java/sql/package-summary.html) | Java 8+ | [opentelemetry-jdbc](../instrumentation/jdbc/library) | [Database Client Spans], [Database Client Metrics]&nbsp;[6] |
9898
| [Jedis](https://github.com/xetorthio/jedis) | 1.4+ | N/A | [Database Client Spans], [Database Client Metrics]&nbsp;[6] |
99+
| [JFinal](https://github.com/jfinal/jfinal) | 3.2+ | N/A | Provides `http.route` [2], Controller Spans [3] |
99100
| [JMS](https://javaee.github.io/javaee-spec/javadocs/javax/jms/package-summary.html) | 1.1+ | N/A | [Messaging Spans] |
100101
| [Jodd Http](https://http.jodd.org/) | 4.2+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] |
101102
| [JSP](https://javaee.github.io/javaee-spec/javadocs/javax/servlet/jsp/package-summary.html) | 2.3.x only | N/A | Controller Spans [3] |
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
plugins {
2+
id("otel.javaagent-instrumentation")
3+
}
4+
5+
muzzle {
6+
pass {
7+
group.set("com.jfinal")
8+
module.set("jfinal")
9+
versions.set("[3.2,)")
10+
assertInverse.set(true)
11+
}
12+
}
13+
14+
if (!(findProperty("testLatestDeps") as Boolean)) {
15+
otelJava {
16+
// jfinal 3.2 doesn't work with Java 9+
17+
maxJavaVersionForTests.set(JavaVersion.VERSION_1_8)
18+
}
19+
}
20+
21+
dependencies {
22+
library("com.jfinal:jfinal:3.2")
23+
testLibrary("com.jfinal:jetty-server:2019.3")
24+
testInstrumentation(project(":instrumentation:jetty:jetty-8.0:javaagent"))
25+
testInstrumentation(project(":instrumentation:jetty:jetty-11.0:javaagent"))
26+
testInstrumentation(project(":instrumentation:jetty:jetty-common:javaagent"))
27+
}
28+
29+
tasks.withType<Test>().configureEach {
30+
jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true")
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.jfinal.v3_2;
7+
8+
import static net.bytebuddy.matcher.ElementMatchers.named;
9+
10+
import com.jfinal.core.Action;
11+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
12+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
13+
import net.bytebuddy.asm.Advice;
14+
import net.bytebuddy.description.type.TypeDescription;
15+
import net.bytebuddy.matcher.ElementMatcher;
16+
17+
public class ActionMappingInstrumentation implements TypeInstrumentation {
18+
19+
@Override
20+
public ElementMatcher<TypeDescription> typeMatcher() {
21+
return named("com.jfinal.core.ActionMapping");
22+
}
23+
24+
@Override
25+
public void transform(TypeTransformer transformer) {
26+
transformer.applyAdviceToMethod(
27+
named("getAction"), this.getClass().getName() + "$GetActionAdvice");
28+
}
29+
30+
@SuppressWarnings("unused")
31+
public static class GetActionAdvice {
32+
33+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
34+
public static void exitGetAction(@Advice.Return Action action) {
35+
JFinalSingletons.updateRoute(action);
36+
}
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.jfinal.v3_2;
7+
8+
import static io.opentelemetry.javaagent.instrumentation.jfinal.v3_2.JFinalSingletons.instrumenter;
9+
import static net.bytebuddy.matcher.ElementMatchers.named;
10+
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;
11+
12+
import com.jfinal.aop.Invocation;
13+
import com.jfinal.core.Action;
14+
import io.opentelemetry.context.Context;
15+
import io.opentelemetry.context.Scope;
16+
import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMethod;
17+
import io.opentelemetry.javaagent.bootstrap.CallDepth;
18+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
19+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
20+
import javax.annotation.Nullable;
21+
import net.bytebuddy.asm.Advice;
22+
import net.bytebuddy.description.type.TypeDescription;
23+
import net.bytebuddy.matcher.ElementMatcher;
24+
25+
public class InvocationInstrumentation implements TypeInstrumentation {
26+
27+
@Override
28+
public ElementMatcher<TypeDescription> typeMatcher() {
29+
return named("com.jfinal.aop.Invocation");
30+
}
31+
32+
@Override
33+
public void transform(TypeTransformer transformer) {
34+
transformer.applyAdviceToMethod(
35+
named("invoke").and(takesNoArguments()), this.getClass().getName() + "$InvokeAdvice");
36+
}
37+
38+
@SuppressWarnings("unused")
39+
public static class InvokeAdvice {
40+
41+
public static class AdviceScope {
42+
private final CallDepth callDepth;
43+
private final ClassAndMethod request;
44+
private final Context context;
45+
private final Scope scope;
46+
47+
public AdviceScope(
48+
CallDepth callDepth, ClassAndMethod request, Context context, Scope scope) {
49+
this.callDepth = callDepth;
50+
this.request = request;
51+
this.context = context;
52+
this.scope = scope;
53+
}
54+
55+
public static AdviceScope start(CallDepth callDepth, Action action) {
56+
if (callDepth.getAndIncrement() > 0 || action == null) {
57+
return new AdviceScope(callDepth, null, null, null);
58+
}
59+
60+
Context parentContext = Context.current();
61+
ClassAndMethod request =
62+
ClassAndMethod.create(action.getControllerClass(), action.getMethodName());
63+
if (!instrumenter().shouldStart(parentContext, request)) {
64+
return new AdviceScope(callDepth, null, null, null);
65+
}
66+
67+
Context context = instrumenter().start(parentContext, request);
68+
return new AdviceScope(callDepth, request, context, context.makeCurrent());
69+
}
70+
71+
public void end(@Nullable Throwable throwable) {
72+
if (callDepth.decrementAndGet() > 0 || scope == null) {
73+
return;
74+
}
75+
scope.close();
76+
instrumenter().end(context, request, null, throwable);
77+
}
78+
}
79+
80+
@Advice.OnMethodEnter(suppress = Throwable.class)
81+
public static AdviceScope onEnter(@Advice.FieldValue("action") Action action) {
82+
CallDepth callDepth = CallDepth.forClass(Invocation.class);
83+
return AdviceScope.start(callDepth, action);
84+
}
85+
86+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
87+
public static void stopTraceOnResponse(
88+
@Advice.Thrown @Nullable Throwable throwable, @Advice.Enter AdviceScope actionScope) {
89+
actionScope.end(throwable);
90+
}
91+
}
92+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.jfinal.v3_2;
7+
8+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
9+
import static java.util.Arrays.asList;
10+
import static net.bytebuddy.matcher.ElementMatchers.not;
11+
12+
import com.google.auto.service.AutoService;
13+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
14+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
15+
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
16+
import java.util.List;
17+
import net.bytebuddy.matcher.ElementMatcher;
18+
19+
@AutoService(InstrumentationModule.class)
20+
public class JFinalInstrumentationModule extends InstrumentationModule
21+
implements ExperimentalInstrumentationModule {
22+
23+
public JFinalInstrumentationModule() {
24+
super("jfinal", "jfinal-3.2");
25+
}
26+
27+
@Override
28+
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
29+
// In version 3.2, TypeConverter is moved from com.jfinal.core
30+
// to com.jfinal.core.converter
31+
return not(hasClassesNamed("com.jfinal.core.TypeConverter"));
32+
}
33+
34+
@Override
35+
public boolean isIndyReady() {
36+
return true;
37+
}
38+
39+
@Override
40+
public List<TypeInstrumentation> typeInstrumentations() {
41+
return asList(new ActionMappingInstrumentation(), new InvocationInstrumentation());
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.jfinal.v3_2;
7+
8+
import com.jfinal.core.Action;
9+
import com.jfinal.render.JsonRender;
10+
import io.opentelemetry.api.GlobalOpenTelemetry;
11+
import io.opentelemetry.context.Context;
12+
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesExtractor;
13+
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;
14+
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeSpanNameExtractor;
15+
import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMethod;
16+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
17+
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRoute;
18+
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource;
19+
import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig;
20+
21+
public final class JFinalSingletons {
22+
23+
private static final Instrumenter<ClassAndMethod, Void> INSTRUMENTER;
24+
25+
static {
26+
// see
27+
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/11465#issuecomment-2137294837
28+
excludeOtAttrs();
29+
30+
CodeAttributesGetter<ClassAndMethod> codedAttributesGetter =
31+
ClassAndMethod.codeAttributesGetter();
32+
INSTRUMENTER =
33+
Instrumenter.<ClassAndMethod, Void>builder(
34+
GlobalOpenTelemetry.get(),
35+
"io.opentelemetry.jfinal-3.2",
36+
CodeSpanNameExtractor.create(codedAttributesGetter))
37+
.setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled())
38+
.addAttributesExtractor(CodeAttributesExtractor.create(codedAttributesGetter))
39+
.buildInstrumenter();
40+
}
41+
42+
public static Instrumenter<ClassAndMethod, Void> instrumenter() {
43+
return INSTRUMENTER;
44+
}
45+
46+
public static void updateRoute(Action action) {
47+
if (action == null) {
48+
return;
49+
}
50+
String route = action.getActionKey();
51+
Context context = Context.current();
52+
if (route != null) {
53+
HttpServerRoute.update(context, HttpServerRouteSource.CONTROLLER, route);
54+
}
55+
}
56+
57+
private static void excludeOtAttrs() {
58+
JsonRender.addExcludedAttrs(
59+
"io.opentelemetry.javaagent.instrumentation.servlet.ServletHelper.Context",
60+
"trace_id",
61+
"span_id");
62+
}
63+
64+
private JFinalSingletons() {}
65+
}

0 commit comments

Comments
 (0)