Skip to content

Commit

Permalink
Rework mybatis instrumentation (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
laurit committed Feb 9, 2024
1 parent c584f76 commit 762d064
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 98 deletions.
3 changes: 1 addition & 2 deletions docs/supported-libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ These are the supported libraries and frameworks:
| [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 |
| [Micrometer](https://micrometer.io/) | 1.5+ | [opentelemetry-micrometer-1.5](../instrumentation/micrometer/micrometer-1.5/library) | none |
| [MongoDB Driver](https://mongodb.github.io/mongo-java-driver/) | 3.1+ | [opentelemetry-mongo-3.1](../instrumentation/mongo/mongo-3.1/library) | [Database Client Spans] |
| [MyBatis](https://mybatis.org/mybatis-3/) | 3.2.0+ | N/A | none |
| [MyBatis](https://mybatis.org/mybatis-3/) | 3.2+ | N/A | none |
| [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] |
| [OkHttp](https://github.com/square/okhttp/) | 2.2+ | [opentelemetry-okhttp-3.0](../instrumentation/okhttp/okhttp-3.0/library) | [HTTP Client Spans], [HTTP Client Metrics] |
| [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] |
Expand Down Expand Up @@ -145,7 +145,6 @@ These are the supported libraries and frameworks:
| [Vert.x Web](https://vertx.io/docs/vertx-web/java/) | 3.0+ | N/A | Provides `http.route` [2] |
| [Vibur DBCP](https://www.vibur.org/) | 11.0+ | [opentelemetry-vibur-dbcp-11.0](../instrumentation/vibur-dbcp-11.0/library) | [Database Pool Metrics] |
| [ZIO](https://zio.dev/) | 2.0+ | N/A | Context propagation |
| [MyBatis](https://mybatis.org/mybatis-3/) | 3.2.0+ | N/A | none |

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

Expand Down
7 changes: 2 additions & 5 deletions instrumentation/mybatis-3.2/javaagent/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,14 @@ muzzle {
}

dependencies {
compileOnly("com.google.auto.value:auto-value-annotations")
annotationProcessor("com.google.auto.value:auto-value")

library("org.mybatis:mybatis:3.2.0")

testImplementation("org.mockito:mockito-core")
testImplementation("org.mockito:mockito-junit-jupiter")
testImplementation("com.h2database:h2:1.4.191")
}

tasks.withType<Test>().configureEach {
jvmArgs("-Dotel.instrumentation.mybatis.enabled=true")

// required on jdk17
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,20 @@
package io.opentelemetry.javaagent.instrumentation.mybatis.v3_2;

import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.instrumentation.mybatis.v3_2.MyBatisSingletons.mapperInstrumenter;
import static io.opentelemetry.javaagent.instrumentation.mybatis.v3_2.MyBatisSingletons.instrumenter;
import static net.bytebuddy.matcher.ElementMatchers.named;

import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMethod;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.ibatis.binding.MapperMethod.SqlCommand;

public class MyBatisExecuteInstrumentation implements TypeInstrumentation {
public class MapperMethodInstrumentation implements TypeInstrumentation {

@Override
public ElementMatcher<TypeDescription> typeMatcher() {
Expand All @@ -28,7 +29,7 @@ public ElementMatcher<TypeDescription> typeMatcher() {
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("execute"), MyBatisExecuteInstrumentation.class.getName() + "$ExecuteAdvice");
named("execute"), MapperMethodInstrumentation.class.getName() + "$ExecuteAdvice");
}

@SuppressWarnings("unused")
Expand All @@ -37,27 +38,33 @@ public static class ExecuteAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void getMapperInfo(
@Advice.FieldValue("command") SqlCommand command,
@Advice.Local("otelRequest") MapperMethodRequest request,
@Advice.Local("otelRequest") ClassAndMethod request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (command == null) {
return;
}
request = SqlCommandUtil.getClassAndMethod(command);
if (request == null) {
return;
}
Context parentContext = currentContext();
if (command == null || !mapperInstrumenter().shouldStart(parentContext, request)) {
if (!instrumenter().shouldStart(parentContext, request)) {
return;
}
request = MapperMethodRequest.create(command.getName());
context = mapperInstrumenter().start(parentContext, request);
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") MapperMethodRequest request,
@Advice.Local("otelRequest") ClassAndMethod request,
@Advice.Local("otelContext") Context context,
@Advice.Local("otelScope") Scope scope) {
if (scope != null) {
scope.close();
mapperInstrumenter().end(context, request, null, throwable);
instrumenter().end(context, request, null, throwable);
}
}
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@

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

import static java.util.Collections.singletonList;
import static java.util.Arrays.asList;

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import java.util.List;

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

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new MyBatisExecuteInstrumentation());
return asList(new MapperMethodInstrumentation(), new SqlCommandInstrumentation());
}

@Override
public boolean defaultEnabled(ConfigProperties config) {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,32 @@
package io.opentelemetry.javaagent.instrumentation.mybatis.v3_2;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesExtractor;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeSpanNameExtractor;
import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMethod;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;

public final class MyBatisSingletons {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.mybatis-3.2";

private static final Instrumenter<MapperMethodRequest, Void> MAPPER_INSTRUMENTER;
private static final Instrumenter<ClassAndMethod, Void> INSTRUMENTER;

static {
SpanNameExtractor<MapperMethodRequest> spanNameExtractor = new MyBatisSpanNameExtractor();

MAPPER_INSTRUMENTER =
Instrumenter.<MapperMethodRequest, Void>builder(
GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, spanNameExtractor)
CodeAttributesGetter<ClassAndMethod> codeAttributesGetter =
ClassAndMethod.codeAttributesGetter();

INSTRUMENTER =
Instrumenter.<ClassAndMethod, Void>builder(
GlobalOpenTelemetry.get(),
INSTRUMENTATION_NAME,
CodeSpanNameExtractor.create(codeAttributesGetter))
.addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter))
.buildInstrumenter(SpanKindExtractor.alwaysInternal());
}

public static Instrumenter<MapperMethodRequest, Void> mapperInstrumenter() {
return MAPPER_INSTRUMENTER;
public static Instrumenter<ClassAndMethod, Void> instrumenter() {
return INSTRUMENTER;
}

private MyBatisSingletons() {}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

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

import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

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;
import org.apache.ibatis.binding.MapperMethod.SqlCommand;

public class SqlCommandInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.apache.ibatis.binding.MapperMethod$SqlCommand");
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isConstructor().and(takesArgument(1, Class.class)).and(takesArgument(2, Method.class)),
SqlCommandInstrumentation.class.getName() + "$ConstructorAdvice");
}

@SuppressWarnings("unused")
public static class ConstructorAdvice {

@Advice.OnMethodExit(suppress = Throwable.class)
public static void onExit(
@Advice.This SqlCommand command,
@Advice.Argument(1) Class<?> mapperInterface,
@Advice.Argument(2) Method method) {
SqlCommandUtil.setClassAndMethod(command, mapperInterface, method);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

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

import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMethod;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import java.lang.reflect.Method;
import org.apache.ibatis.binding.MapperMethod.SqlCommand;

public final class SqlCommandUtil {
private static final VirtualField<SqlCommand, ClassAndMethod> field =
VirtualField.find(SqlCommand.class, ClassAndMethod.class);

public static void setClassAndMethod(SqlCommand command, Class<?> clazz, Method method) {
if (clazz == null || method == null || method.getName() == null) {
return;
}
field.set(command, ClassAndMethod.create(clazz, method.getName()));
}

public static ClassAndMethod getClassAndMethod(SqlCommand command) {
return field.get(command);
}

private SqlCommandUtil() {}
}
Loading

0 comments on commit 762d064

Please sign in to comment.