Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework mybatis instrumentation #1

Merged
merged 1 commit into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading