Skip to content

Commit 762d064

Browse files
authored
Rework mybatis instrumentation (#1)
1 parent c584f76 commit 762d064

File tree

12 files changed

+162
-98
lines changed

12 files changed

+162
-98
lines changed

docs/supported-libraries.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ These are the supported libraries and frameworks:
9494
| [Logback](http://logback.qos.ch/) | 1.0+ | [opentelemetry-logback-appender-1.0](../instrumentation/logback/logback-appender-1.0/library),<br>[opentelemetry-logback-mdc-1.0](../instrumentation/logback/logback-mdc-1.0/library) | none |
9595
| [Micrometer](https://micrometer.io/) | 1.5+ | [opentelemetry-micrometer-1.5](../instrumentation/micrometer/micrometer-1.5/library) | none |
9696
| [MongoDB Driver](https://mongodb.github.io/mongo-java-driver/) | 3.1+ | [opentelemetry-mongo-3.1](../instrumentation/mongo/mongo-3.1/library) | [Database Client Spans] |
97-
| [MyBatis](https://mybatis.org/mybatis-3/) | 3.2.0+ | N/A | none |
97+
| [MyBatis](https://mybatis.org/mybatis-3/) | 3.2+ | N/A | none |
9898
| [Netty](https://github.com/netty/netty) | 3.8+ | [opentelemetry-netty-4.1](../instrumentation/netty/netty-4.1/library) | [HTTP Client Spans], [HTTP Client Metrics], [HTTP Server Spans], [HTTP Server Metrics] |
9999
| [OkHttp](https://github.com/square/okhttp/) | 2.2+ | [opentelemetry-okhttp-3.0](../instrumentation/okhttp/okhttp-3.0/library) | [HTTP Client Spans], [HTTP Client Metrics] |
100100
| [Oracle UCP](https://docs.oracle.com/database/121/JJUCP/) | 11.2+ | [opentelemetry-oracle-ucp-11.2](../instrumentation/oracle-ucp-11.2/library) | [Database Pool Metrics] |
@@ -145,7 +145,6 @@ These are the supported libraries and frameworks:
145145
| [Vert.x Web](https://vertx.io/docs/vertx-web/java/) | 3.0+ | N/A | Provides `http.route` [2] |
146146
| [Vibur DBCP](https://www.vibur.org/) | 11.0+ | [opentelemetry-vibur-dbcp-11.0](../instrumentation/vibur-dbcp-11.0/library) | [Database Pool Metrics] |
147147
| [ZIO](https://zio.dev/) | 2.0+ | N/A | Context propagation |
148-
| [MyBatis](https://mybatis.org/mybatis-3/) | 3.2.0+ | N/A | none |
149148

150149
**[1]** Standalone library instrumentation refers to instrumentation that can be used without the Java agent.
151150

instrumentation/mybatis-3.2/javaagent/build.gradle.kts

+2-5
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,14 @@ muzzle {
1212
}
1313

1414
dependencies {
15-
compileOnly("com.google.auto.value:auto-value-annotations")
16-
annotationProcessor("com.google.auto.value:auto-value")
17-
1815
library("org.mybatis:mybatis:3.2.0")
1916

20-
testImplementation("org.mockito:mockito-core")
21-
testImplementation("org.mockito:mockito-junit-jupiter")
2217
testImplementation("com.h2database:h2:1.4.191")
2318
}
2419

2520
tasks.withType<Test>().configureEach {
21+
jvmArgs("-Dotel.instrumentation.mybatis.enabled=true")
22+
2623
// required on jdk17
2724
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
2825
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,20 @@
66
package io.opentelemetry.javaagent.instrumentation.mybatis.v3_2;
77

88
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
9-
import static io.opentelemetry.javaagent.instrumentation.mybatis.v3_2.MyBatisSingletons.mapperInstrumenter;
9+
import static io.opentelemetry.javaagent.instrumentation.mybatis.v3_2.MyBatisSingletons.instrumenter;
1010
import static net.bytebuddy.matcher.ElementMatchers.named;
1111

1212
import io.opentelemetry.context.Context;
1313
import io.opentelemetry.context.Scope;
14+
import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMethod;
1415
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
1516
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
1617
import net.bytebuddy.asm.Advice;
1718
import net.bytebuddy.description.type.TypeDescription;
1819
import net.bytebuddy.matcher.ElementMatcher;
1920
import org.apache.ibatis.binding.MapperMethod.SqlCommand;
2021

21-
public class MyBatisExecuteInstrumentation implements TypeInstrumentation {
22+
public class MapperMethodInstrumentation implements TypeInstrumentation {
2223

2324
@Override
2425
public ElementMatcher<TypeDescription> typeMatcher() {
@@ -28,7 +29,7 @@ public ElementMatcher<TypeDescription> typeMatcher() {
2829
@Override
2930
public void transform(TypeTransformer transformer) {
3031
transformer.applyAdviceToMethod(
31-
named("execute"), MyBatisExecuteInstrumentation.class.getName() + "$ExecuteAdvice");
32+
named("execute"), MapperMethodInstrumentation.class.getName() + "$ExecuteAdvice");
3233
}
3334

3435
@SuppressWarnings("unused")
@@ -37,27 +38,33 @@ public static class ExecuteAdvice {
3738
@Advice.OnMethodEnter(suppress = Throwable.class)
3839
public static void getMapperInfo(
3940
@Advice.FieldValue("command") SqlCommand command,
40-
@Advice.Local("otelRequest") MapperMethodRequest request,
41+
@Advice.Local("otelRequest") ClassAndMethod request,
4142
@Advice.Local("otelContext") Context context,
4243
@Advice.Local("otelScope") Scope scope) {
44+
if (command == null) {
45+
return;
46+
}
47+
request = SqlCommandUtil.getClassAndMethod(command);
48+
if (request == null) {
49+
return;
50+
}
4351
Context parentContext = currentContext();
44-
if (command == null || !mapperInstrumenter().shouldStart(parentContext, request)) {
52+
if (!instrumenter().shouldStart(parentContext, request)) {
4553
return;
4654
}
47-
request = MapperMethodRequest.create(command.getName());
48-
context = mapperInstrumenter().start(parentContext, request);
55+
context = instrumenter().start(parentContext, request);
4956
scope = context.makeCurrent();
5057
}
5158

5259
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
5360
public static void stopSpan(
5461
@Advice.Thrown Throwable throwable,
55-
@Advice.Local("otelRequest") MapperMethodRequest request,
62+
@Advice.Local("otelRequest") ClassAndMethod request,
5663
@Advice.Local("otelContext") Context context,
5764
@Advice.Local("otelScope") Scope scope) {
5865
if (scope != null) {
5966
scope.close();
60-
mapperInstrumenter().end(context, request, null, throwable);
67+
instrumenter().end(context, request, null, throwable);
6168
}
6269
}
6370
}

instrumentation/mybatis-3.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mybatis/v3_2/MapperMethodRequest.java

-18
This file was deleted.

instrumentation/mybatis-3.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mybatis/v3_2/MyBatisInstrumentationModule.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55

66
package io.opentelemetry.javaagent.instrumentation.mybatis.v3_2;
77

8-
import static java.util.Collections.singletonList;
8+
import static java.util.Arrays.asList;
99

1010
import com.google.auto.service.AutoService;
1111
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
1212
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
13+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
1314
import java.util.List;
1415

1516
@AutoService(InstrumentationModule.class)
@@ -21,6 +22,11 @@ public MyBatisInstrumentationModule() {
2122

2223
@Override
2324
public List<TypeInstrumentation> typeInstrumentations() {
24-
return singletonList(new MyBatisExecuteInstrumentation());
25+
return asList(new MapperMethodInstrumentation(), new SqlCommandInstrumentation());
26+
}
27+
28+
@Override
29+
public boolean defaultEnabled(ConfigProperties config) {
30+
return false;
2531
}
2632
}

instrumentation/mybatis-3.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mybatis/v3_2/MyBatisSingletons.java

+16-10
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,32 @@
66
package io.opentelemetry.javaagent.instrumentation.mybatis.v3_2;
77

88
import io.opentelemetry.api.GlobalOpenTelemetry;
9+
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesExtractor;
10+
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;
11+
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeSpanNameExtractor;
12+
import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMethod;
913
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
1014
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
11-
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
1215

1316
public final class MyBatisSingletons {
1417
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.mybatis-3.2";
15-
16-
private static final Instrumenter<MapperMethodRequest, Void> MAPPER_INSTRUMENTER;
18+
private static final Instrumenter<ClassAndMethod, Void> INSTRUMENTER;
1719

1820
static {
19-
SpanNameExtractor<MapperMethodRequest> spanNameExtractor = new MyBatisSpanNameExtractor();
20-
21-
MAPPER_INSTRUMENTER =
22-
Instrumenter.<MapperMethodRequest, Void>builder(
23-
GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, spanNameExtractor)
21+
CodeAttributesGetter<ClassAndMethod> codeAttributesGetter =
22+
ClassAndMethod.codeAttributesGetter();
23+
24+
INSTRUMENTER =
25+
Instrumenter.<ClassAndMethod, Void>builder(
26+
GlobalOpenTelemetry.get(),
27+
INSTRUMENTATION_NAME,
28+
CodeSpanNameExtractor.create(codeAttributesGetter))
29+
.addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter))
2430
.buildInstrumenter(SpanKindExtractor.alwaysInternal());
2531
}
2632

27-
public static Instrumenter<MapperMethodRequest, Void> mapperInstrumenter() {
28-
return MAPPER_INSTRUMENTER;
33+
public static Instrumenter<ClassAndMethod, Void> instrumenter() {
34+
return INSTRUMENTER;
2935
}
3036

3137
private MyBatisSingletons() {}

instrumentation/mybatis-3.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mybatis/v3_2/MyBatisSpanNameExtractor.java

-23
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.mybatis.v3_2;
7+
8+
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
9+
import static net.bytebuddy.matcher.ElementMatchers.named;
10+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
11+
12+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
13+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
14+
import java.lang.reflect.Method;
15+
import net.bytebuddy.asm.Advice;
16+
import net.bytebuddy.description.type.TypeDescription;
17+
import net.bytebuddy.matcher.ElementMatcher;
18+
import org.apache.ibatis.binding.MapperMethod.SqlCommand;
19+
20+
public class SqlCommandInstrumentation implements TypeInstrumentation {
21+
@Override
22+
public ElementMatcher<TypeDescription> typeMatcher() {
23+
return named("org.apache.ibatis.binding.MapperMethod$SqlCommand");
24+
}
25+
26+
@Override
27+
public void transform(TypeTransformer transformer) {
28+
transformer.applyAdviceToMethod(
29+
isConstructor().and(takesArgument(1, Class.class)).and(takesArgument(2, Method.class)),
30+
SqlCommandInstrumentation.class.getName() + "$ConstructorAdvice");
31+
}
32+
33+
@SuppressWarnings("unused")
34+
public static class ConstructorAdvice {
35+
36+
@Advice.OnMethodExit(suppress = Throwable.class)
37+
public static void onExit(
38+
@Advice.This SqlCommand command,
39+
@Advice.Argument(1) Class<?> mapperInterface,
40+
@Advice.Argument(2) Method method) {
41+
SqlCommandUtil.setClassAndMethod(command, mapperInterface, method);
42+
}
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.mybatis.v3_2;
7+
8+
import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMethod;
9+
import io.opentelemetry.instrumentation.api.util.VirtualField;
10+
import java.lang.reflect.Method;
11+
import org.apache.ibatis.binding.MapperMethod.SqlCommand;
12+
13+
public final class SqlCommandUtil {
14+
private static final VirtualField<SqlCommand, ClassAndMethod> field =
15+
VirtualField.find(SqlCommand.class, ClassAndMethod.class);
16+
17+
public static void setClassAndMethod(SqlCommand command, Class<?> clazz, Method method) {
18+
if (clazz == null || method == null || method.getName() == null) {
19+
return;
20+
}
21+
field.set(command, ClassAndMethod.create(clazz, method.getName()));
22+
}
23+
24+
public static ClassAndMethod getClassAndMethod(SqlCommand command) {
25+
return field.get(command);
26+
}
27+
28+
private SqlCommandUtil() {}
29+
}

0 commit comments

Comments
 (0)