Merge pull request #769 from DataDog/willgittoes-dd/hibernate-3.5
Support Hibernate 3.5
This commit is contained in:
commit
143abcbcf8
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Integration for Hibernate between 3.5 and 4.
|
||||||
|
* Has the same logic as the Hibernate 4+ integration, but is copied rather than sharing a codebase. This is because
|
||||||
|
* the root interface for Session/StatelessSession - SharedSessionContract - isn't present before version 4. So the
|
||||||
|
* instrumentation isn't able to reference it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Set properties before any plugins get loaded
|
||||||
|
project.ext {
|
||||||
|
// Execute tests on all JVMs, even rare and outdated ones
|
||||||
|
coreJavaInstrumentation = true
|
||||||
|
minJavaVersionForTests = JavaVersion.VERSION_1_7
|
||||||
|
}
|
||||||
|
|
||||||
|
muzzle {
|
||||||
|
pass {
|
||||||
|
group = "org.hibernate"
|
||||||
|
module = "hibernate-core"
|
||||||
|
versions = "[3.5.0-Final,4.0.0.Final)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "${rootDir}/gradle/java.gradle"
|
||||||
|
|
||||||
|
apply plugin: 'org.unbroken-dome.test-sets'
|
||||||
|
|
||||||
|
testSets {
|
||||||
|
latestDepTest {
|
||||||
|
dirName = 'test'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly group: 'org.hibernate', name: 'hibernate-core', version: '3.5.0-Final'
|
||||||
|
|
||||||
|
compile project(':dd-java-agent:agent-tooling')
|
||||||
|
compile project(':dd-java-agent:instrumentation:hibernate')
|
||||||
|
|
||||||
|
compile deps.bytebuddy
|
||||||
|
compile deps.opentracing
|
||||||
|
annotationProcessor deps.autoservice
|
||||||
|
implementation deps.autoservice
|
||||||
|
|
||||||
|
testCompile project(':dd-java-agent:testing')
|
||||||
|
testCompile project(':dd-java-agent:instrumentation:jdbc')
|
||||||
|
|
||||||
|
testCompile group: 'org.hibernate', name: 'hibernate-core', version: '3.5.0-Final'
|
||||||
|
testCompile group: 'org.hibernate', name: 'hibernate-annotations', version: '+'
|
||||||
|
testCompile group: 'javassist', name: 'javassist', version: '+'
|
||||||
|
testCompile group: 'com.h2database', name: 'h2', version: '1.4.197'
|
||||||
|
testCompile "javax.xml.bind:jaxb-api:2.2.11"
|
||||||
|
testCompile "com.sun.xml.bind:jaxb-core:2.2.11"
|
||||||
|
testCompile "com.sun.xml.bind:jaxb-impl:2.2.11"
|
||||||
|
testCompile "javax.activation:activation:1.1.1"
|
||||||
|
|
||||||
|
latestDepTestCompile group: 'org.hibernate', name: 'hibernate-core', version: '3.+'
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
// Needed for test, but for latestDepTest this would otherwise bundle a second incompatible version of hibernate-core.
|
||||||
|
latestDepTestCompile.exclude group: 'org.hibernate', module: 'hibernate-annotations'
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package datadog.trace.instrumentation.hibernate.v4_0;
|
package datadog.trace.instrumentation.hibernate.core.v3_5;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
|
@ -11,6 +11,8 @@ import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
import datadog.trace.bootstrap.ContextStore;
|
import datadog.trace.bootstrap.ContextStore;
|
||||||
import datadog.trace.bootstrap.InstrumentationContext;
|
import datadog.trace.bootstrap.InstrumentationContext;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionMethodUtils;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionState;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.description.method.MethodDescription;
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
@ -34,13 +36,13 @@ public class CriteriaInstrumentation extends Instrumenter.Default {
|
||||||
@Override
|
@Override
|
||||||
public String[] helperClassNames() {
|
public String[] helperClassNames() {
|
||||||
return new String[] {
|
return new String[] {
|
||||||
packageName + ".SessionMethodUtils",
|
"datadog.trace.instrumentation.hibernate.SessionMethodUtils",
|
||||||
packageName + ".SessionState",
|
"datadog.trace.instrumentation.hibernate.SessionState",
|
||||||
"datadog.trace.agent.decorator.BaseDecorator",
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
"datadog.trace.agent.decorator.ClientDecorator",
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
"datadog.trace.agent.decorator.OrmClientDecorator",
|
"datadog.trace.agent.decorator.OrmClientDecorator",
|
||||||
packageName + ".HibernateDecorator",
|
"datadog.trace.instrumentation.hibernate.HibernateDecorator",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package datadog.trace.instrumentation.hibernate.v4_0;
|
package datadog.trace.instrumentation.hibernate.core.v3_5;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||||
import static datadog.trace.instrumentation.hibernate.v4_0.HibernateDecorator.DECORATOR;
|
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
@ -12,6 +12,8 @@ import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
import datadog.trace.bootstrap.ContextStore;
|
import datadog.trace.bootstrap.ContextStore;
|
||||||
import datadog.trace.bootstrap.InstrumentationContext;
|
import datadog.trace.bootstrap.InstrumentationContext;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionMethodUtils;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionState;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.description.method.MethodDescription;
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
@ -36,13 +38,13 @@ public class QueryInstrumentation extends Instrumenter.Default {
|
||||||
@Override
|
@Override
|
||||||
public String[] helperClassNames() {
|
public String[] helperClassNames() {
|
||||||
return new String[] {
|
return new String[] {
|
||||||
packageName + ".SessionMethodUtils",
|
"datadog.trace.instrumentation.hibernate.SessionMethodUtils",
|
||||||
packageName + ".SessionState",
|
"datadog.trace.instrumentation.hibernate.SessionState",
|
||||||
"datadog.trace.agent.decorator.BaseDecorator",
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
"datadog.trace.agent.decorator.ClientDecorator",
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
"datadog.trace.agent.decorator.OrmClientDecorator",
|
"datadog.trace.agent.decorator.OrmClientDecorator",
|
||||||
packageName + ".HibernateDecorator",
|
"datadog.trace.instrumentation.hibernate.HibernateDecorator",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package datadog.trace.instrumentation.hibernate.core.v3_5;
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||||
|
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
import datadog.trace.bootstrap.ContextStore;
|
||||||
|
import datadog.trace.bootstrap.InstrumentationContext;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionState;
|
||||||
|
import io.opentracing.Span;
|
||||||
|
import io.opentracing.util.GlobalTracer;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.StatelessSession;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public class SessionFactoryInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public SessionFactoryInstrumentation() {
|
||||||
|
super("hibernate", "hibernate-core");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> contextStore() {
|
||||||
|
final Map<String, String> stores = new HashMap<>();
|
||||||
|
stores.put("org.hibernate.Session", SessionState.class.getName());
|
||||||
|
stores.put("org.hibernate.StatelessSession", SessionState.class.getName());
|
||||||
|
stores.put("org.hibernate.SharedSessionContract", SessionState.class.getName());
|
||||||
|
return stores;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {
|
||||||
|
"datadog.trace.instrumentation.hibernate.SessionState",
|
||||||
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
|
"datadog.trace.agent.decorator.OrmClientDecorator",
|
||||||
|
"datadog.trace.instrumentation.hibernate.HibernateDecorator",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.SessionFactory")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
isMethod()
|
||||||
|
.and(named("openSession").or(named("openStatelessSession")))
|
||||||
|
.and(takesArguments(0))
|
||||||
|
.and(
|
||||||
|
returns(
|
||||||
|
named("org.hibernate.Session")
|
||||||
|
.or(named("org.hibernate.StatelessSession"))
|
||||||
|
.or(safeHasSuperType(named("org.hibernate.Session"))))),
|
||||||
|
SessionFactoryAdvice.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SessionFactoryAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
|
public static void openSession(@Advice.Return final Object session) {
|
||||||
|
|
||||||
|
final Span span = GlobalTracer.get().buildSpan("hibernate.session").start();
|
||||||
|
DECORATOR.afterStart(span);
|
||||||
|
DECORATOR.onConnection(span, session);
|
||||||
|
|
||||||
|
if (session instanceof Session) {
|
||||||
|
final ContextStore<Session, SessionState> contextStore =
|
||||||
|
InstrumentationContext.get(Session.class, SessionState.class);
|
||||||
|
contextStore.putIfAbsent((Session) session, new SessionState(span));
|
||||||
|
} else if (session instanceof StatelessSession) {
|
||||||
|
final ContextStore<StatelessSession, SessionState> contextStore =
|
||||||
|
InstrumentationContext.get(StatelessSession.class, SessionState.class);
|
||||||
|
contextStore.putIfAbsent((StatelessSession) session, new SessionState(span));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,257 @@
|
||||||
|
package datadog.trace.instrumentation.hibernate.core.v3_5;
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||||
|
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
||||||
|
import static datadog.trace.instrumentation.hibernate.SessionMethodUtils.SCOPE_ONLY_METHODS;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
import datadog.trace.bootstrap.ContextStore;
|
||||||
|
import datadog.trace.bootstrap.InstrumentationContext;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionMethodUtils;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionState;
|
||||||
|
import io.opentracing.Span;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import org.hibernate.Criteria;
|
||||||
|
import org.hibernate.Query;
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.StatelessSession;
|
||||||
|
import org.hibernate.Transaction;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public class SessionInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public SessionInstrumentation() {
|
||||||
|
super("hibernate", "hibernate-core");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> contextStore() {
|
||||||
|
final Map<String, String> map = new HashMap<>();
|
||||||
|
map.put("org.hibernate.Session", SessionState.class.getName());
|
||||||
|
map.put("org.hibernate.StatelessSession", SessionState.class.getName());
|
||||||
|
map.put("org.hibernate.Query", SessionState.class.getName());
|
||||||
|
map.put("org.hibernate.Transaction", SessionState.class.getName());
|
||||||
|
map.put("org.hibernate.Criteria", SessionState.class.getName());
|
||||||
|
return Collections.unmodifiableMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {
|
||||||
|
"datadog.trace.instrumentation.hibernate.SessionMethodUtils",
|
||||||
|
"datadog.trace.instrumentation.hibernate.SessionState",
|
||||||
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
|
"datadog.trace.agent.decorator.OrmClientDecorator",
|
||||||
|
"datadog.trace.instrumentation.hibernate.HibernateDecorator",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return not(isInterface())
|
||||||
|
.and(
|
||||||
|
safeHasSuperType(
|
||||||
|
named("org.hibernate.Session").or(named("org.hibernate.StatelessSession"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
|
||||||
|
transformers.put(
|
||||||
|
isMethod().and(named("close")).and(takesArguments(0)), SessionCloseAdvice.class.getName());
|
||||||
|
|
||||||
|
// Session synchronous methods we want to instrument.
|
||||||
|
transformers.put(
|
||||||
|
isMethod()
|
||||||
|
.and(
|
||||||
|
named("save")
|
||||||
|
.or(named("replicate"))
|
||||||
|
.or(named("saveOrUpdate"))
|
||||||
|
.or(named("update"))
|
||||||
|
.or(named("merge"))
|
||||||
|
.or(named("persist"))
|
||||||
|
.or(named("lock"))
|
||||||
|
.or(named("refresh"))
|
||||||
|
.or(named("insert"))
|
||||||
|
.or(named("delete"))
|
||||||
|
// Iterator methods.
|
||||||
|
.or(named("iterate"))
|
||||||
|
// Lazy-load methods.
|
||||||
|
.or(named("immediateLoad"))
|
||||||
|
.or(named("internalLoad"))),
|
||||||
|
SessionMethodAdvice.class.getName());
|
||||||
|
// Handle the non-generic 'get' separately.
|
||||||
|
transformers.put(
|
||||||
|
isMethod()
|
||||||
|
.and(named("get"))
|
||||||
|
.and(returns(named("java.lang.Object")))
|
||||||
|
.and(takesArgument(0, named("java.lang.String"))),
|
||||||
|
SessionMethodAdvice.class.getName());
|
||||||
|
|
||||||
|
// These methods return some object that we want to instrument, and so the Advice will pin the
|
||||||
|
// current Span to the returned object using a ContextStore.
|
||||||
|
transformers.put(
|
||||||
|
isMethod()
|
||||||
|
.and(named("beginTransaction").or(named("getTransaction")))
|
||||||
|
.and(returns(named("org.hibernate.Transaction"))),
|
||||||
|
GetTransactionAdvice.class.getName());
|
||||||
|
|
||||||
|
transformers.put(
|
||||||
|
isMethod().and(returns(safeHasSuperType(named("org.hibernate.Query")))),
|
||||||
|
GetQueryAdvice.class.getName());
|
||||||
|
|
||||||
|
transformers.put(
|
||||||
|
isMethod().and(returns(safeHasSuperType(named("org.hibernate.Criteria")))),
|
||||||
|
GetCriteriaAdvice.class.getName());
|
||||||
|
|
||||||
|
return transformers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SessionCloseAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void closeSession(
|
||||||
|
@Advice.This final Object session, @Advice.Thrown final Throwable throwable) {
|
||||||
|
|
||||||
|
SessionState state = null;
|
||||||
|
if (session instanceof Session) {
|
||||||
|
final ContextStore<Session, SessionState> contextStore =
|
||||||
|
InstrumentationContext.get(Session.class, SessionState.class);
|
||||||
|
state = contextStore.get((Session) session);
|
||||||
|
} else if (session instanceof StatelessSession) {
|
||||||
|
final ContextStore<StatelessSession, SessionState> contextStore =
|
||||||
|
InstrumentationContext.get(StatelessSession.class, SessionState.class);
|
||||||
|
state = contextStore.get((StatelessSession) session);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == null || state.getSessionSpan() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state.getMethodScope() != null) {
|
||||||
|
state.getMethodScope().close();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Span span = state.getSessionSpan();
|
||||||
|
DECORATOR.onError(span, throwable);
|
||||||
|
DECORATOR.beforeFinish(span);
|
||||||
|
span.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SessionMethodAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static SessionState startMethod(
|
||||||
|
@Advice.This final Object session,
|
||||||
|
@Advice.Origin("#m") final String name,
|
||||||
|
@Advice.Argument(0) final Object entity) {
|
||||||
|
|
||||||
|
final boolean startSpan = !SCOPE_ONLY_METHODS.contains(name);
|
||||||
|
if (session instanceof Session) {
|
||||||
|
final ContextStore<Session, SessionState> contextStore =
|
||||||
|
InstrumentationContext.get(Session.class, SessionState.class);
|
||||||
|
return SessionMethodUtils.startScopeFrom(
|
||||||
|
contextStore, (Session) session, "hibernate." + name, entity, startSpan);
|
||||||
|
} else if (session instanceof StatelessSession) {
|
||||||
|
final ContextStore<StatelessSession, SessionState> contextStore =
|
||||||
|
InstrumentationContext.get(StatelessSession.class, SessionState.class);
|
||||||
|
return SessionMethodUtils.startScopeFrom(
|
||||||
|
contextStore, (StatelessSession) session, "hibernate." + name, entity, startSpan);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void endMethod(
|
||||||
|
@Advice.Enter final SessionState sessionState,
|
||||||
|
@Advice.Thrown final Throwable throwable,
|
||||||
|
@Advice.Return(typing = Assigner.Typing.DYNAMIC) final Object returned) {
|
||||||
|
|
||||||
|
SessionMethodUtils.closeScope(sessionState, throwable, returned);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GetQueryAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
|
public static void getQuery(
|
||||||
|
@Advice.This final Object session, @Advice.Return final Query query) {
|
||||||
|
|
||||||
|
final ContextStore<Query, SessionState> queryContextStore =
|
||||||
|
InstrumentationContext.get(Query.class, SessionState.class);
|
||||||
|
if (session instanceof Session) {
|
||||||
|
final ContextStore<Session, SessionState> sessionContextStore =
|
||||||
|
InstrumentationContext.get(Session.class, SessionState.class);
|
||||||
|
SessionMethodUtils.attachSpanFromStore(
|
||||||
|
sessionContextStore, (Session) session, queryContextStore, query);
|
||||||
|
} else if (session instanceof StatelessSession) {
|
||||||
|
final ContextStore<StatelessSession, SessionState> sessionContextStore =
|
||||||
|
InstrumentationContext.get(StatelessSession.class, SessionState.class);
|
||||||
|
SessionMethodUtils.attachSpanFromStore(
|
||||||
|
sessionContextStore, (StatelessSession) session, queryContextStore, query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GetTransactionAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
|
public static void getTransaction(
|
||||||
|
@Advice.This final Object session, @Advice.Return final Transaction transaction) {
|
||||||
|
|
||||||
|
final ContextStore<Transaction, SessionState> transactionContextStore =
|
||||||
|
InstrumentationContext.get(Transaction.class, SessionState.class);
|
||||||
|
|
||||||
|
if (session instanceof Session) {
|
||||||
|
final ContextStore<Session, SessionState> sessionContextStore =
|
||||||
|
InstrumentationContext.get(Session.class, SessionState.class);
|
||||||
|
SessionMethodUtils.attachSpanFromStore(
|
||||||
|
sessionContextStore, (Session) session, transactionContextStore, transaction);
|
||||||
|
} else if (session instanceof StatelessSession) {
|
||||||
|
final ContextStore<StatelessSession, SessionState> sessionContextStore =
|
||||||
|
InstrumentationContext.get(StatelessSession.class, SessionState.class);
|
||||||
|
SessionMethodUtils.attachSpanFromStore(
|
||||||
|
sessionContextStore, (StatelessSession) session, transactionContextStore, transaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GetCriteriaAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
|
public static void getCriteria(
|
||||||
|
@Advice.This final Object session, @Advice.Return final Criteria criteria) {
|
||||||
|
|
||||||
|
final ContextStore<Criteria, SessionState> criteriaContextStore =
|
||||||
|
InstrumentationContext.get(Criteria.class, SessionState.class);
|
||||||
|
if (session instanceof Session) {
|
||||||
|
final ContextStore<Session, SessionState> sessionContextStore =
|
||||||
|
InstrumentationContext.get(Session.class, SessionState.class);
|
||||||
|
SessionMethodUtils.attachSpanFromStore(
|
||||||
|
sessionContextStore, (Session) session, criteriaContextStore, criteria);
|
||||||
|
} else if (session instanceof StatelessSession) {
|
||||||
|
final ContextStore<StatelessSession, SessionState> sessionContextStore =
|
||||||
|
InstrumentationContext.get(StatelessSession.class, SessionState.class);
|
||||||
|
SessionMethodUtils.attachSpanFromStore(
|
||||||
|
sessionContextStore, (StatelessSession) session, criteriaContextStore, criteria);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package datadog.trace.instrumentation.hibernate.v4_0;
|
package datadog.trace.instrumentation.hibernate.core.v3_5;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
|
@ -12,6 +12,8 @@ import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
import datadog.trace.bootstrap.ContextStore;
|
import datadog.trace.bootstrap.ContextStore;
|
||||||
import datadog.trace.bootstrap.InstrumentationContext;
|
import datadog.trace.bootstrap.InstrumentationContext;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionMethodUtils;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionState;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.description.method.MethodDescription;
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
@ -34,13 +36,13 @@ public class TransactionInstrumentation extends Instrumenter.Default {
|
||||||
@Override
|
@Override
|
||||||
public String[] helperClassNames() {
|
public String[] helperClassNames() {
|
||||||
return new String[] {
|
return new String[] {
|
||||||
packageName + ".SessionMethodUtils",
|
"datadog.trace.instrumentation.hibernate.SessionMethodUtils",
|
||||||
packageName + ".SessionState",
|
"datadog.trace.instrumentation.hibernate.SessionState",
|
||||||
"datadog.trace.agent.decorator.BaseDecorator",
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
"datadog.trace.agent.decorator.ClientDecorator",
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
"datadog.trace.agent.decorator.OrmClientDecorator",
|
"datadog.trace.agent.decorator.OrmClientDecorator",
|
||||||
packageName + ".HibernateDecorator",
|
"datadog.trace.instrumentation.hibernate.HibernateDecorator",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import datadog.trace.agent.test.AgentTestRunner
|
||||||
|
import org.hibernate.Session
|
||||||
|
import org.hibernate.SessionFactory
|
||||||
|
import org.hibernate.cfg.AnnotationConfiguration
|
||||||
|
import spock.lang.Shared
|
||||||
|
|
||||||
|
abstract class AbstractHibernateTest extends AgentTestRunner {
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
protected SessionFactory sessionFactory
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
protected List<Value> prepopulated
|
||||||
|
|
||||||
|
def setupSpec() {
|
||||||
|
sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory()
|
||||||
|
|
||||||
|
// Pre-populate the DB, so delete/update can be tested.
|
||||||
|
Session writer = sessionFactory.openSession()
|
||||||
|
writer.beginTransaction()
|
||||||
|
prepopulated = new ArrayList<>()
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
prepopulated.add(new Value("Hello :) " + i))
|
||||||
|
writer.save(prepopulated.get(i))
|
||||||
|
}
|
||||||
|
writer.getTransaction().commit()
|
||||||
|
writer.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
def cleanupSpec() {
|
||||||
|
if (sessionFactory != null) {
|
||||||
|
sessionFactory.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,627 @@
|
||||||
|
import datadog.trace.api.DDSpanTypes
|
||||||
|
import io.opentracing.Scope
|
||||||
|
import io.opentracing.Tracer
|
||||||
|
import io.opentracing.tag.Tags
|
||||||
|
import io.opentracing.util.GlobalTracer
|
||||||
|
import org.hibernate.*
|
||||||
|
import spock.lang.Shared
|
||||||
|
|
||||||
|
class SessionTest extends AbstractHibernateTest {
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
private Closure sessionBuilder = { return sessionFactory.openSession() }
|
||||||
|
@Shared
|
||||||
|
private Closure statelessSessionBuilder = { return sessionFactory.openStatelessSession() }
|
||||||
|
|
||||||
|
|
||||||
|
def "test hibernate action #testName"() {
|
||||||
|
setup:
|
||||||
|
|
||||||
|
// Test for each implementation of Session.
|
||||||
|
for (def buildSession : sessionImplementations) {
|
||||||
|
def session = buildSession()
|
||||||
|
session.beginTransaction()
|
||||||
|
|
||||||
|
try {
|
||||||
|
sessionMethodTest.call(session, prepopulated.get(0))
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We expected this, we should see the error field set on the span.
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
expect:
|
||||||
|
assertTraces(sessionImplementations.size()) {
|
||||||
|
for (int i = 0; i < sessionImplementations.size(); i++) {
|
||||||
|
trace(i, 4) {
|
||||||
|
span(0) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.session"
|
||||||
|
operationName "hibernate.session"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
parent()
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.transaction.commit"
|
||||||
|
operationName "hibernate.transaction.commit"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName resource
|
||||||
|
operationName "hibernate.$methodName"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
serviceName "h2"
|
||||||
|
spanType "sql"
|
||||||
|
childOf span(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
testName | methodName | resource | sessionImplementations | sessionMethodTest
|
||||||
|
"lock" | "lock" | "Value" | [sessionBuilder] | { sesh, val ->
|
||||||
|
sesh.lock(val, LockMode.READ)
|
||||||
|
}
|
||||||
|
"refresh" | "refresh" | "Value" | [sessionBuilder, statelessSessionBuilder] | { sesh, val ->
|
||||||
|
sesh.refresh(val)
|
||||||
|
}
|
||||||
|
"get" | "get" | "Value" | [sessionBuilder, statelessSessionBuilder] | { sesh, val ->
|
||||||
|
sesh.get("Value", val.getId())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test hibernate statless action #testName"() {
|
||||||
|
setup:
|
||||||
|
|
||||||
|
// Test for each implementation of Session.
|
||||||
|
def session = statelessSessionBuilder()
|
||||||
|
session.beginTransaction()
|
||||||
|
|
||||||
|
try {
|
||||||
|
sessionMethodTest.call(session, prepopulated.get(0))
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We expected this, we should see the error field set on the span.
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 4) {
|
||||||
|
span(0) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.session"
|
||||||
|
operationName "hibernate.session"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
parent()
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.transaction.commit"
|
||||||
|
operationName "hibernate.transaction.commit"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
serviceName "h2"
|
||||||
|
spanType "sql"
|
||||||
|
childOf span(1)
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName resource
|
||||||
|
operationName "hibernate.$methodName"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
testName | methodName | resource | sessionMethodTest
|
||||||
|
"insert" | "insert" | "Value" | { sesh, val ->
|
||||||
|
sesh.insert("Value", new Value("insert me"))
|
||||||
|
}
|
||||||
|
"update" | "update" | "Value" | { sesh, val ->
|
||||||
|
val.setName("New name")
|
||||||
|
sesh.update(val)
|
||||||
|
}
|
||||||
|
"update by entityName" | "update" | "Value" | { sesh, val ->
|
||||||
|
val.setName("New name")
|
||||||
|
sesh.update("Value", val)
|
||||||
|
}
|
||||||
|
"delete" | "delete" | "Value" | { sesh, val ->
|
||||||
|
sesh.delete(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test hibernate replicate: #testName"() {
|
||||||
|
setup:
|
||||||
|
|
||||||
|
// Test for each implementation of Session.
|
||||||
|
def session = sessionFactory.openSession()
|
||||||
|
session.beginTransaction()
|
||||||
|
|
||||||
|
try {
|
||||||
|
sessionMethodTest.call(session, prepopulated.get(0))
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We expected this, we should see the error field set on the span.
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 5) {
|
||||||
|
span(0) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.session"
|
||||||
|
operationName "hibernate.session"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
parent()
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.transaction.commit"
|
||||||
|
operationName "hibernate.transaction.commit"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
serviceName "h2"
|
||||||
|
spanType "sql"
|
||||||
|
childOf span(1)
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName resource
|
||||||
|
operationName "hibernate.$methodName"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(4) {
|
||||||
|
serviceName "h2"
|
||||||
|
spanType "sql"
|
||||||
|
childOf span(3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
testName | methodName | resource | sessionMethodTest
|
||||||
|
"replicate" | "replicate" | "Value" | { sesh, val ->
|
||||||
|
Value replicated = new Value(val.getName() + " replicated")
|
||||||
|
replicated.setId(val.getId())
|
||||||
|
sesh.replicate(replicated, ReplicationMode.OVERWRITE)
|
||||||
|
}
|
||||||
|
"replicate by entityName" | "replicate" | "Value" | { sesh, val ->
|
||||||
|
Value replicated = new Value(val.getName() + " replicated")
|
||||||
|
replicated.setId(val.getId())
|
||||||
|
sesh.replicate("Value", replicated, ReplicationMode.OVERWRITE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test hibernate failed replicate"() {
|
||||||
|
setup:
|
||||||
|
|
||||||
|
// Test for each implementation of Session.
|
||||||
|
def session = sessionFactory.openSession()
|
||||||
|
session.beginTransaction()
|
||||||
|
|
||||||
|
try {
|
||||||
|
session.replicate(new Long(123) /* Not a valid entity */, ReplicationMode.OVERWRITE)
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We expected this, we should see the error field set on the span.
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 3) {
|
||||||
|
span(0) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.session"
|
||||||
|
operationName "hibernate.session"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
parent()
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.transaction.commit"
|
||||||
|
operationName "hibernate.transaction.commit"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.replicate"
|
||||||
|
operationName "hibernate.replicate"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
errored(true)
|
||||||
|
tags {
|
||||||
|
errorTags(MappingException, "Unknown entity: java.lang.Long")
|
||||||
|
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def "test hibernate commit action #testName"() {
|
||||||
|
setup:
|
||||||
|
|
||||||
|
def session = sessionBuilder()
|
||||||
|
session.beginTransaction()
|
||||||
|
|
||||||
|
try {
|
||||||
|
sessionMethodTest.call(session, prepopulated.get(0))
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We expected this, we should see the error field set on the span.
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 4) {
|
||||||
|
span(0) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.session"
|
||||||
|
operationName "hibernate.session"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
parent()
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.transaction.commit"
|
||||||
|
operationName "hibernate.transaction.commit"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
serviceName "h2"
|
||||||
|
spanType "sql"
|
||||||
|
childOf span(1)
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName resource
|
||||||
|
operationName "hibernate.$methodName"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
testName | methodName | resource | sessionMethodTest
|
||||||
|
"save" | "save" | "Value" | { sesh, val ->
|
||||||
|
sesh.save(new Value("Another value"))
|
||||||
|
}
|
||||||
|
"saveOrUpdate save" | "saveOrUpdate" | "Value" | { sesh, val ->
|
||||||
|
sesh.saveOrUpdate(new Value("Value"))
|
||||||
|
}
|
||||||
|
"saveOrUpdate update" | "saveOrUpdate" | "Value" | { sesh, val ->
|
||||||
|
val.setName("New name")
|
||||||
|
sesh.saveOrUpdate(val)
|
||||||
|
}
|
||||||
|
"merge" | "merge" | "Value" | { sesh, val ->
|
||||||
|
sesh.merge(new Value("merge me in"))
|
||||||
|
}
|
||||||
|
"persist" | "persist" | "Value" | { sesh, val ->
|
||||||
|
sesh.persist(new Value("merge me in"))
|
||||||
|
}
|
||||||
|
"update (Session)" | "update" | "Value" | { sesh, val ->
|
||||||
|
val.setName("New name")
|
||||||
|
sesh.update(val)
|
||||||
|
}
|
||||||
|
"update by entityName (Session)" | "update" | "Value" | { sesh, val ->
|
||||||
|
val.setName("New name")
|
||||||
|
sesh.update("Value", val)
|
||||||
|
}
|
||||||
|
"delete (Session)" | "delete" | "Value" | { sesh, val ->
|
||||||
|
sesh.delete(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def "test attaches State to query created via #queryMethodName"() {
|
||||||
|
setup:
|
||||||
|
Session session = sessionFactory.openSession()
|
||||||
|
session.beginTransaction()
|
||||||
|
Query query = queryBuildMethod(session)
|
||||||
|
query.list()
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 4) {
|
||||||
|
span(0) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.session"
|
||||||
|
operationName "hibernate.session"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
parent()
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.transaction.commit"
|
||||||
|
operationName "hibernate.transaction.commit"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "$resource"
|
||||||
|
operationName "hibernate.query.list"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
serviceName "h2"
|
||||||
|
spanType "sql"
|
||||||
|
childOf span(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
queryMethodName | resource | queryBuildMethod
|
||||||
|
"createQuery" | "Value" | { sess -> sess.createQuery("from Value") }
|
||||||
|
"getNamedQuery" | "Value" | { sess -> sess.getNamedQuery("TestNamedQuery") }
|
||||||
|
"createSQLQuery" | "SELECT * FROM Value" | { sess -> sess.createSQLQuery("SELECT * FROM Value") }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def "test hibernate overlapping Sessions"() {
|
||||||
|
setup:
|
||||||
|
|
||||||
|
Tracer tracer = GlobalTracer.get()
|
||||||
|
|
||||||
|
Scope scope = tracer.buildSpan("overlapping Sessions").startActive(true)
|
||||||
|
|
||||||
|
def session1 = sessionFactory.openSession()
|
||||||
|
session1.beginTransaction()
|
||||||
|
def session2 = sessionFactory.openStatelessSession()
|
||||||
|
def session3 = sessionFactory.openSession()
|
||||||
|
|
||||||
|
def value1 = new Value("Value 1")
|
||||||
|
session1.save(value1)
|
||||||
|
session2.insert(new Value("Value 2"))
|
||||||
|
session3.save(new Value("Value 3"))
|
||||||
|
session1.delete(value1)
|
||||||
|
|
||||||
|
session2.close()
|
||||||
|
session1.getTransaction().commit()
|
||||||
|
session1.close()
|
||||||
|
session3.close()
|
||||||
|
|
||||||
|
scope.close()
|
||||||
|
|
||||||
|
|
||||||
|
expect:
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 11) {
|
||||||
|
span(0) {
|
||||||
|
serviceName "unnamed-java-app"
|
||||||
|
operationName "overlapping Sessions"
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.session"
|
||||||
|
operationName "hibernate.session"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.session"
|
||||||
|
operationName "hibernate.session"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.transaction.commit"
|
||||||
|
operationName "hibernate.transaction.commit"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(2)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(4) {
|
||||||
|
serviceName "h2"
|
||||||
|
spanType "sql"
|
||||||
|
childOf span(3)
|
||||||
|
}
|
||||||
|
span(5) {
|
||||||
|
serviceName "h2"
|
||||||
|
spanType "sql"
|
||||||
|
childOf span(3)
|
||||||
|
}
|
||||||
|
span(6) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.session"
|
||||||
|
operationName "hibernate.session"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(7) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "Value"
|
||||||
|
operationName "hibernate.delete"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(2)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(8) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "Value"
|
||||||
|
operationName "hibernate.save"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(1)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(9) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "Value"
|
||||||
|
operationName "hibernate.insert"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(6)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(10) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "Value"
|
||||||
|
operationName "hibernate.save"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(2)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<!DOCTYPE hibernate-configuration PUBLIC
|
||||||
|
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
|
||||||
|
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
|
||||||
|
|
||||||
|
<hibernate-configuration>
|
||||||
|
|
||||||
|
<session-factory>
|
||||||
|
|
||||||
|
<property name="hibernate.cache.use_second_level_cache">false</property>
|
||||||
|
|
||||||
|
<!-- Use in-memory DB for testing -->
|
||||||
|
<property name="connection.driver_class">org.h2.Driver</property>
|
||||||
|
<property name="connection.url">jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE</property>
|
||||||
|
<property name="connection.username">sa</property>
|
||||||
|
<property name="connection.password"/>
|
||||||
|
<property name="dialect">org.hibernate.dialect.H2Dialect</property>
|
||||||
|
|
||||||
|
<property name="connection.pool_size">3</property>
|
||||||
|
<property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
|
||||||
|
<property name="show_sql">true</property>
|
||||||
|
|
||||||
|
<!-- Reset the DB each test -->
|
||||||
|
<property name="hbm2ddl.auto">create</property>
|
||||||
|
|
||||||
|
<!-- Objects -->
|
||||||
|
<mapping class="Value"/>
|
||||||
|
|
||||||
|
</session-factory>
|
||||||
|
|
||||||
|
</hibernate-configuration>
|
|
@ -27,6 +27,7 @@ dependencies {
|
||||||
compileOnly group: 'org.hibernate', name: 'hibernate-core', version: '4.0.0.Final'
|
compileOnly group: 'org.hibernate', name: 'hibernate-core', version: '4.0.0.Final'
|
||||||
|
|
||||||
compile project(':dd-java-agent:agent-tooling')
|
compile project(':dd-java-agent:agent-tooling')
|
||||||
|
compile project(':dd-java-agent:instrumentation:hibernate')
|
||||||
|
|
||||||
compile deps.bytebuddy
|
compile deps.bytebuddy
|
||||||
compile deps.opentracing
|
compile deps.opentracing
|
||||||
|
@ -47,5 +48,5 @@ dependencies {
|
||||||
latestDepTestCompile group: 'org.hibernate', name: 'hibernate-core', version: '+'
|
latestDepTestCompile group: 'org.hibernate', name: 'hibernate-core', version: '+'
|
||||||
latestDepTestCompile group: 'com.h2database', name: 'h2', version: '1.4.197'
|
latestDepTestCompile group: 'com.h2database', name: 'h2', version: '1.4.197'
|
||||||
// Test that the incremental instrumentation for hibernate 4.3 doesn't cause issues.
|
// Test that the incremental instrumentation for hibernate 4.3 doesn't cause issues.
|
||||||
latestDepTestCompile project(':dd-java-agent:instrumentation:hibernate-4.3')
|
latestDepTestCompile project(':dd-java-agent:instrumentation:hibernate:core-4.3')
|
||||||
}
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package datadog.trace.instrumentation.hibernate.core.v4_0;
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
import datadog.trace.bootstrap.ContextStore;
|
||||||
|
import datadog.trace.bootstrap.InstrumentationContext;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionMethodUtils;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionState;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import org.hibernate.Criteria;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public class CriteriaInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public CriteriaInstrumentation() {
|
||||||
|
super("hibernate", "hibernate-core");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> contextStore() {
|
||||||
|
return singletonMap("org.hibernate.Criteria", SessionState.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {
|
||||||
|
"datadog.trace.instrumentation.hibernate.SessionMethodUtils",
|
||||||
|
"datadog.trace.instrumentation.hibernate.SessionState",
|
||||||
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
|
"datadog.trace.agent.decorator.OrmClientDecorator",
|
||||||
|
"datadog.trace.instrumentation.hibernate.HibernateDecorator",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Criteria")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
isMethod().and(named("list").or(named("uniqueResult")).or(named("scroll"))),
|
||||||
|
CriteriaMethodAdvice.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CriteriaMethodAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static SessionState startMethod(
|
||||||
|
@Advice.This final Criteria criteria, @Advice.Origin("#m") final String name) {
|
||||||
|
|
||||||
|
final ContextStore<Criteria, SessionState> contextStore =
|
||||||
|
InstrumentationContext.get(Criteria.class, SessionState.class);
|
||||||
|
|
||||||
|
return SessionMethodUtils.startScopeFrom(
|
||||||
|
contextStore, criteria, "hibernate.criteria." + name, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void endMethod(
|
||||||
|
@Advice.Enter final SessionState state,
|
||||||
|
@Advice.Thrown final Throwable throwable,
|
||||||
|
@Advice.Return(typing = Assigner.Typing.DYNAMIC) final Object entity) {
|
||||||
|
|
||||||
|
SessionMethodUtils.closeScope(state, throwable, entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
package datadog.trace.instrumentation.hibernate.core.v4_0;
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||||
|
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
import datadog.trace.bootstrap.ContextStore;
|
||||||
|
import datadog.trace.bootstrap.InstrumentationContext;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionMethodUtils;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionState;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import org.hibernate.Query;
|
||||||
|
import org.hibernate.SQLQuery;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public class QueryInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public QueryInstrumentation() {
|
||||||
|
super("hibernate", "hibernate-core");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> contextStore() {
|
||||||
|
return singletonMap("org.hibernate.Query", SessionState.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {
|
||||||
|
"datadog.trace.instrumentation.hibernate.SessionMethodUtils",
|
||||||
|
"datadog.trace.instrumentation.hibernate.SessionState",
|
||||||
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
|
"datadog.trace.agent.decorator.OrmClientDecorator",
|
||||||
|
"datadog.trace.instrumentation.hibernate.HibernateDecorator",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Query")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
isMethod()
|
||||||
|
.and(
|
||||||
|
named("list")
|
||||||
|
.or(named("executeUpdate"))
|
||||||
|
.or(named("uniqueResult"))
|
||||||
|
.or(named("scroll"))),
|
||||||
|
QueryMethodAdvice.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class QueryMethodAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static SessionState startMethod(
|
||||||
|
@Advice.This final Query query, @Advice.Origin("#m") final String name) {
|
||||||
|
|
||||||
|
final ContextStore<Query, SessionState> contextStore =
|
||||||
|
InstrumentationContext.get(Query.class, SessionState.class);
|
||||||
|
|
||||||
|
// Note: We don't know what the entity is until the method is returning.
|
||||||
|
final SessionState state =
|
||||||
|
SessionMethodUtils.startScopeFrom(
|
||||||
|
contextStore, query, "hibernate.query." + name, null, true);
|
||||||
|
DECORATOR.onStatement(state.getMethodScope().span(), query.getQueryString());
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void endMethod(
|
||||||
|
@Advice.This final Query query,
|
||||||
|
@Advice.Enter final SessionState state,
|
||||||
|
@Advice.Thrown final Throwable throwable,
|
||||||
|
@Advice.Return(typing = Assigner.Typing.DYNAMIC) final Object returned) {
|
||||||
|
|
||||||
|
Object entity = returned;
|
||||||
|
if (returned == null || query instanceof SQLQuery) {
|
||||||
|
// Not a method that returns results, or the query returns a table rather than an ORM
|
||||||
|
// object.
|
||||||
|
entity = query.getQueryString();
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionMethodUtils.closeScope(state, throwable, entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package datadog.trace.instrumentation.hibernate.v4_0;
|
package datadog.trace.instrumentation.hibernate.core.v4_0;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||||
import static datadog.trace.instrumentation.hibernate.v4_0.HibernateDecorator.DECORATOR;
|
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
@ -14,6 +14,7 @@ import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
import datadog.trace.bootstrap.ContextStore;
|
import datadog.trace.bootstrap.ContextStore;
|
||||||
import datadog.trace.bootstrap.InstrumentationContext;
|
import datadog.trace.bootstrap.InstrumentationContext;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionState;
|
||||||
import io.opentracing.Span;
|
import io.opentracing.Span;
|
||||||
import io.opentracing.util.GlobalTracer;
|
import io.opentracing.util.GlobalTracer;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -38,12 +39,12 @@ public class SessionFactoryInstrumentation extends Instrumenter.Default {
|
||||||
@Override
|
@Override
|
||||||
public String[] helperClassNames() {
|
public String[] helperClassNames() {
|
||||||
return new String[] {
|
return new String[] {
|
||||||
packageName + ".SessionState",
|
"datadog.trace.instrumentation.hibernate.SessionState",
|
||||||
"datadog.trace.agent.decorator.BaseDecorator",
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
"datadog.trace.agent.decorator.ClientDecorator",
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
"datadog.trace.agent.decorator.OrmClientDecorator",
|
"datadog.trace.agent.decorator.OrmClientDecorator",
|
||||||
packageName + ".HibernateDecorator",
|
"datadog.trace.instrumentation.hibernate.HibernateDecorator",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package datadog.trace.instrumentation.hibernate.v4_0;
|
package datadog.trace.instrumentation.hibernate.core.v4_0;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||||
import static datadog.trace.instrumentation.hibernate.v4_0.HibernateDecorator.DECORATOR;
|
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
||||||
import static datadog.trace.instrumentation.hibernate.v4_0.SessionMethodUtils.SCOPE_ONLY_METHODS;
|
import static datadog.trace.instrumentation.hibernate.SessionMethodUtils.SCOPE_ONLY_METHODS;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
@ -15,6 +15,8 @@ import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
import datadog.trace.bootstrap.ContextStore;
|
import datadog.trace.bootstrap.ContextStore;
|
||||||
import datadog.trace.bootstrap.InstrumentationContext;
|
import datadog.trace.bootstrap.InstrumentationContext;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionMethodUtils;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionState;
|
||||||
import io.opentracing.Span;
|
import io.opentracing.Span;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -49,13 +51,13 @@ public class SessionInstrumentation extends Instrumenter.Default {
|
||||||
@Override
|
@Override
|
||||||
public String[] helperClassNames() {
|
public String[] helperClassNames() {
|
||||||
return new String[] {
|
return new String[] {
|
||||||
packageName + ".SessionMethodUtils",
|
"datadog.trace.instrumentation.hibernate.SessionMethodUtils",
|
||||||
packageName + ".SessionState",
|
"datadog.trace.instrumentation.hibernate.SessionState",
|
||||||
"datadog.trace.agent.decorator.BaseDecorator",
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
"datadog.trace.agent.decorator.ClientDecorator",
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
"datadog.trace.agent.decorator.OrmClientDecorator",
|
"datadog.trace.agent.decorator.OrmClientDecorator",
|
||||||
packageName + ".HibernateDecorator",
|
"datadog.trace.instrumentation.hibernate.HibernateDecorator",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
package datadog.trace.instrumentation.hibernate.core.v4_0;
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
import datadog.trace.bootstrap.ContextStore;
|
||||||
|
import datadog.trace.bootstrap.InstrumentationContext;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionMethodUtils;
|
||||||
|
import datadog.trace.instrumentation.hibernate.SessionState;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import org.hibernate.Transaction;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public class TransactionInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public TransactionInstrumentation() {
|
||||||
|
super("hibernate", "hibernate-core");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> contextStore() {
|
||||||
|
return singletonMap("org.hibernate.Transaction", SessionState.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {
|
||||||
|
"datadog.trace.instrumentation.hibernate.SessionMethodUtils",
|
||||||
|
"datadog.trace.instrumentation.hibernate.SessionState",
|
||||||
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
|
"datadog.trace.agent.decorator.OrmClientDecorator",
|
||||||
|
"datadog.trace.instrumentation.hibernate.HibernateDecorator",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Transaction")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
isMethod().and(named("commit")).and(takesArguments(0)),
|
||||||
|
TransactionCommitAdvice.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TransactionCommitAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static SessionState startCommit(@Advice.This final Transaction transaction) {
|
||||||
|
|
||||||
|
final ContextStore<Transaction, SessionState> contextStore =
|
||||||
|
InstrumentationContext.get(Transaction.class, SessionState.class);
|
||||||
|
|
||||||
|
return SessionMethodUtils.startScopeFrom(
|
||||||
|
contextStore, transaction, "hibernate.transaction.commit", null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void endCommit(
|
||||||
|
@Advice.This final Transaction transaction,
|
||||||
|
@Advice.Enter final SessionState state,
|
||||||
|
@Advice.Thrown final Throwable throwable) {
|
||||||
|
|
||||||
|
SessionMethodUtils.closeScope(state, throwable, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
import datadog.trace.api.DDSpanTypes
|
||||||
|
import io.opentracing.tag.Tags
|
||||||
|
import org.hibernate.Criteria
|
||||||
|
import org.hibernate.Session
|
||||||
|
import org.hibernate.criterion.Order
|
||||||
|
import org.hibernate.criterion.Restrictions
|
||||||
|
|
||||||
|
class CriteriaTest extends AbstractHibernateTest {
|
||||||
|
|
||||||
|
def "test criteria.#methodName"() {
|
||||||
|
setup:
|
||||||
|
Session session = sessionFactory.openSession()
|
||||||
|
session.beginTransaction()
|
||||||
|
Criteria criteria = session.createCriteria(Value)
|
||||||
|
.add(Restrictions.like("name", "Hello"))
|
||||||
|
.addOrder(Order.desc("name"))
|
||||||
|
interaction.call(criteria)
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 4) {
|
||||||
|
span(0) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.session"
|
||||||
|
operationName "hibernate.session"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
parent()
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.transaction.commit"
|
||||||
|
operationName "hibernate.transaction.commit"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.criteria.$methodName"
|
||||||
|
operationName "hibernate.criteria.$methodName"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
serviceName "h2"
|
||||||
|
spanType "sql"
|
||||||
|
childOf span(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
methodName | interaction
|
||||||
|
"list" | { c -> c.list() }
|
||||||
|
"uniqueResult" | { c -> c.uniqueResult() }
|
||||||
|
"scroll" | { c -> c.scroll() }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
import datadog.trace.api.DDSpanTypes
|
||||||
|
import io.opentracing.tag.Tags
|
||||||
|
import org.hibernate.Query
|
||||||
|
import org.hibernate.Session
|
||||||
|
|
||||||
|
class QueryTest extends AbstractHibernateTest {
|
||||||
|
|
||||||
|
def "test hibernate query.#queryMethodName single call"() {
|
||||||
|
setup:
|
||||||
|
|
||||||
|
// With Transaction
|
||||||
|
Session session = sessionFactory.openSession()
|
||||||
|
session.beginTransaction()
|
||||||
|
queryInteraction(session)
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
// Without Transaction
|
||||||
|
if (!requiresTransaction) {
|
||||||
|
session = sessionFactory.openSession()
|
||||||
|
queryInteraction(session)
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
expect:
|
||||||
|
assertTraces(requiresTransaction ? 1 : 2) {
|
||||||
|
// With Transaction
|
||||||
|
trace(0, 4) {
|
||||||
|
span(0) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.session"
|
||||||
|
operationName "hibernate.session"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
parent()
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.transaction.commit"
|
||||||
|
operationName "hibernate.transaction.commit"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "$resource"
|
||||||
|
operationName "hibernate.$queryMethodName"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
serviceName "h2"
|
||||||
|
spanType "sql"
|
||||||
|
childOf span(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!requiresTransaction) {
|
||||||
|
// Without Transaction
|
||||||
|
trace(1, 3) {
|
||||||
|
span(0) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.session"
|
||||||
|
operationName "hibernate.session"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
parent()
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "$resource"
|
||||||
|
operationName "hibernate.$queryMethodName"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
serviceName "h2"
|
||||||
|
spanType "sql"
|
||||||
|
childOf span(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
queryMethodName | resource | requiresTransaction | queryInteraction
|
||||||
|
"query.list" | "Value" | false | { sess ->
|
||||||
|
Query q = sess.createQuery("from Value")
|
||||||
|
q.list()
|
||||||
|
}
|
||||||
|
"query.executeUpdate" | "update Value set name = 'alyx'" | true | { sess ->
|
||||||
|
Query q = sess.createQuery("update Value set name = 'alyx'")
|
||||||
|
q.executeUpdate()
|
||||||
|
}
|
||||||
|
"query.uniqueResult" | "Value" | false | { sess ->
|
||||||
|
Query q = sess.createQuery("from Value where id = 1")
|
||||||
|
q.uniqueResult()
|
||||||
|
}
|
||||||
|
"iterate" | "from Value" | false | { sess ->
|
||||||
|
Query q = sess.createQuery("from Value")
|
||||||
|
q.iterate()
|
||||||
|
}
|
||||||
|
"query.scroll" | "from Value" | false | { sess ->
|
||||||
|
Query q = sess.createQuery("from Value")
|
||||||
|
q.scroll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test hibernate query.iterate"() {
|
||||||
|
setup:
|
||||||
|
|
||||||
|
Session session = sessionFactory.openSession()
|
||||||
|
session.beginTransaction()
|
||||||
|
Query q = session.createQuery("from Value")
|
||||||
|
Iterator it = q.iterate()
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next()
|
||||||
|
}
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 4) {
|
||||||
|
span(0) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.session"
|
||||||
|
operationName "hibernate.session"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
parent()
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "hibernate.transaction.commit"
|
||||||
|
operationName "hibernate.transaction.commit"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
serviceName "hibernate"
|
||||||
|
resourceName "from Value"
|
||||||
|
operationName "hibernate.iterate"
|
||||||
|
spanType DDSpanTypes.HIBERNATE
|
||||||
|
childOf span(0)
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT.key" "java-hibernate"
|
||||||
|
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
serviceName "h2"
|
||||||
|
spanType "sql"
|
||||||
|
childOf span(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -27,7 +27,7 @@ dependencies {
|
||||||
compileOnly group: 'org.hibernate', name: 'hibernate-core', version: '4.3.0.Final'
|
compileOnly group: 'org.hibernate', name: 'hibernate-core', version: '4.3.0.Final'
|
||||||
|
|
||||||
compile project(':dd-java-agent:agent-tooling')
|
compile project(':dd-java-agent:agent-tooling')
|
||||||
compile project(':dd-java-agent:instrumentation:hibernate-4.0')
|
compile project(':dd-java-agent:instrumentation:hibernate:core-4.0')
|
||||||
|
|
||||||
compile deps.bytebuddy
|
compile deps.bytebuddy
|
||||||
compile deps.opentracing
|
compile deps.opentracing
|
|
@ -1,4 +1,4 @@
|
||||||
package datadog.trace.instrumentation.hibernate.v4_3;
|
package datadog.trace.instrumentation.hibernate.core.v4_3;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
|
@ -11,8 +11,8 @@ import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
import datadog.trace.bootstrap.ContextStore;
|
import datadog.trace.bootstrap.ContextStore;
|
||||||
import datadog.trace.bootstrap.InstrumentationContext;
|
import datadog.trace.bootstrap.InstrumentationContext;
|
||||||
import datadog.trace.instrumentation.hibernate.v4_0.SessionMethodUtils;
|
import datadog.trace.instrumentation.hibernate.SessionMethodUtils;
|
||||||
import datadog.trace.instrumentation.hibernate.v4_0.SessionState;
|
import datadog.trace.instrumentation.hibernate.SessionState;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.description.method.MethodDescription;
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
@ -35,13 +35,13 @@ public class ProcedureCallInstrumentation extends Instrumenter.Default {
|
||||||
@Override
|
@Override
|
||||||
public String[] helperClassNames() {
|
public String[] helperClassNames() {
|
||||||
return new String[] {
|
return new String[] {
|
||||||
"datadog.trace.instrumentation.hibernate.v4_0.SessionMethodUtils",
|
"datadog.trace.instrumentation.hibernate.SessionMethodUtils",
|
||||||
"datadog.trace.instrumentation.hibernate.v4_0.SessionState",
|
"datadog.trace.instrumentation.hibernate.SessionState",
|
||||||
"datadog.trace.agent.decorator.BaseDecorator",
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
"datadog.trace.agent.decorator.ClientDecorator",
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
"datadog.trace.agent.decorator.OrmClientDecorator",
|
"datadog.trace.agent.decorator.OrmClientDecorator",
|
||||||
"datadog.trace.instrumentation.hibernate.v4_0.HibernateDecorator",
|
"datadog.trace.instrumentation.hibernate.HibernateDecorator",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package datadog.trace.instrumentation.hibernate.v4_3;
|
package datadog.trace.instrumentation.hibernate.core.v4_3;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||||
|
@ -11,8 +11,8 @@ import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
import datadog.trace.bootstrap.ContextStore;
|
import datadog.trace.bootstrap.ContextStore;
|
||||||
import datadog.trace.bootstrap.InstrumentationContext;
|
import datadog.trace.bootstrap.InstrumentationContext;
|
||||||
import datadog.trace.instrumentation.hibernate.v4_0.SessionMethodUtils;
|
import datadog.trace.instrumentation.hibernate.SessionMethodUtils;
|
||||||
import datadog.trace.instrumentation.hibernate.v4_0.SessionState;
|
import datadog.trace.instrumentation.hibernate.SessionState;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -41,13 +41,13 @@ public class SessionInstrumentation extends Instrumenter.Default {
|
||||||
@Override
|
@Override
|
||||||
public String[] helperClassNames() {
|
public String[] helperClassNames() {
|
||||||
return new String[] {
|
return new String[] {
|
||||||
"datadog.trace.instrumentation.hibernate.v4_0.SessionMethodUtils",
|
"datadog.trace.instrumentation.hibernate.SessionMethodUtils",
|
||||||
"datadog.trace.instrumentation.hibernate.v4_0.SessionState",
|
"datadog.trace.instrumentation.hibernate.SessionState",
|
||||||
"datadog.trace.agent.decorator.BaseDecorator",
|
"datadog.trace.agent.decorator.BaseDecorator",
|
||||||
"datadog.trace.agent.decorator.ClientDecorator",
|
"datadog.trace.agent.decorator.ClientDecorator",
|
||||||
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
"datadog.trace.agent.decorator.DatabaseClientDecorator",
|
||||||
"datadog.trace.agent.decorator.OrmClientDecorator",
|
"datadog.trace.agent.decorator.OrmClientDecorator",
|
||||||
"datadog.trace.instrumentation.hibernate.v4_0.HibernateDecorator",
|
"datadog.trace.instrumentation.hibernate.HibernateDecorator",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import datadog.trace.api.Trace;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
|
import org.hibernate.annotations.NamedQuery;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table
|
||||||
|
@NamedQuery(name = "TestNamedQuery", query = "from Value")
|
||||||
|
public class Value {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Value() {}
|
||||||
|
|
||||||
|
public Value(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(generator = "increment")
|
||||||
|
@GenericGenerator(name = "increment", strategy = "increment")
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setId(final Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Trace
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(final String title) {
|
||||||
|
this.name = title;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* Classes that are common to all versions of the Hibernate instrumentation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply from: "${rootDir}/gradle/java.gradle"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile project(':dd-java-agent:agent-tooling')
|
||||||
|
|
||||||
|
compile deps.bytebuddy
|
||||||
|
compile deps.opentracing
|
||||||
|
annotationProcessor deps.autoservice
|
||||||
|
implementation deps.autoservice
|
||||||
|
}
|
|
@ -1,9 +1,11 @@
|
||||||
package datadog.trace.instrumentation.hibernate.v4_0;
|
package datadog.trace.instrumentation.hibernate;
|
||||||
|
|
||||||
import datadog.trace.agent.decorator.OrmClientDecorator;
|
import datadog.trace.agent.decorator.OrmClientDecorator;
|
||||||
import datadog.trace.api.DDSpanTypes;
|
import datadog.trace.api.DDSpanTypes;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.persistence.Entity;
|
import java.util.Set;
|
||||||
|
|
||||||
public class HibernateDecorator extends OrmClientDecorator {
|
public class HibernateDecorator extends OrmClientDecorator {
|
||||||
public static final HibernateDecorator DECORATOR = new HibernateDecorator();
|
public static final HibernateDecorator DECORATOR = new HibernateDecorator();
|
||||||
|
@ -46,16 +48,22 @@ public class HibernateDecorator extends OrmClientDecorator {
|
||||||
@Override
|
@Override
|
||||||
public String entityName(final Object entity) {
|
public String entityName(final Object entity) {
|
||||||
String name = null;
|
String name = null;
|
||||||
|
final Set<String> annotations = new HashSet<>();
|
||||||
|
for (final Annotation annotation : entity.getClass().getDeclaredAnnotations()) {
|
||||||
|
annotations.add(annotation.annotationType().getName());
|
||||||
|
}
|
||||||
|
|
||||||
if (entity instanceof String) {
|
if (entity instanceof String) {
|
||||||
// We were given an entity name, not the entity itself.
|
// We were given an entity name, not the entity itself.
|
||||||
name = (String) entity;
|
name = (String) entity;
|
||||||
} else if (entity.getClass().isAnnotationPresent(Entity.class)) {
|
} else if (annotations.contains("javax.persistence.Entity")) {
|
||||||
// We were given an instance of an entity.
|
// We were given an instance of an entity.
|
||||||
name = entity.getClass().getName();
|
name = entity.getClass().getName();
|
||||||
} else if (entity instanceof List && ((List) entity).size() > 0) {
|
} else if (entity instanceof List && ((List) entity).size() > 0) {
|
||||||
// We have a list of entities.
|
// We have a list of entities.
|
||||||
name = entityName(((List) entity).get(0));
|
name = entityName(((List) entity).get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package datadog.trace.instrumentation.hibernate.v4_0;
|
package datadog.trace.instrumentation.hibernate;
|
||||||
|
|
||||||
import static datadog.trace.instrumentation.hibernate.v4_0.HibernateDecorator.DECORATOR;
|
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
||||||
|
|
||||||
import datadog.trace.bootstrap.CallDepthThreadLocalMap;
|
import datadog.trace.bootstrap.CallDepthThreadLocalMap;
|
||||||
import datadog.trace.bootstrap.ContextStore;
|
import datadog.trace.bootstrap.ContextStore;
|
|
@ -1,4 +1,4 @@
|
||||||
package datadog.trace.instrumentation.hibernate.v4_0;
|
package datadog.trace.instrumentation.hibernate;
|
||||||
|
|
||||||
import io.opentracing.Scope;
|
import io.opentracing.Scope;
|
||||||
import io.opentracing.Span;
|
import io.opentracing.Span;
|
|
@ -40,8 +40,10 @@ include ':dd-java-agent:instrumentation:elasticsearch:transport-5'
|
||||||
include ':dd-java-agent:instrumentation:elasticsearch:transport-5.3'
|
include ':dd-java-agent:instrumentation:elasticsearch:transport-5.3'
|
||||||
include ':dd-java-agent:instrumentation:elasticsearch:transport-6'
|
include ':dd-java-agent:instrumentation:elasticsearch:transport-6'
|
||||||
include ':dd-java-agent:instrumentation:grpc-1.5'
|
include ':dd-java-agent:instrumentation:grpc-1.5'
|
||||||
include ':dd-java-agent:instrumentation:hibernate-4.0'
|
include ':dd-java-agent:instrumentation:hibernate'
|
||||||
include ':dd-java-agent:instrumentation:hibernate-4.3'
|
include ':dd-java-agent:instrumentation:hibernate:core-3.5'
|
||||||
|
include ':dd-java-agent:instrumentation:hibernate:core-4.0'
|
||||||
|
include ':dd-java-agent:instrumentation:hibernate:core-4.3'
|
||||||
include ':dd-java-agent:instrumentation:http-url-connection'
|
include ':dd-java-agent:instrumentation:http-url-connection'
|
||||||
include ':dd-java-agent:instrumentation:hystrix-1.4'
|
include ':dd-java-agent:instrumentation:hystrix-1.4'
|
||||||
include ':dd-java-agent:instrumentation:jax-rs-annotations'
|
include ':dd-java-agent:instrumentation:jax-rs-annotations'
|
||||||
|
|
Loading…
Reference in New Issue