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 java.util.Collections.singletonMap;
|
||||
|
@ -11,6 +11,8 @@ 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;
|
||||
|
@ -34,13 +36,13 @@ public class CriteriaInstrumentation extends Instrumenter.Default {
|
|||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
packageName + ".SessionMethodUtils",
|
||||
packageName + ".SessionState",
|
||||
"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",
|
||||
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.instrumentation.hibernate.v4_0.HibernateDecorator.DECORATOR;
|
||||
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;
|
||||
|
@ -12,6 +12,8 @@ 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;
|
||||
|
@ -36,13 +38,13 @@ public class QueryInstrumentation extends Instrumenter.Default {
|
|||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
packageName + ".SessionMethodUtils",
|
||||
packageName + ".SessionState",
|
||||
"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",
|
||||
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 java.util.Collections.singletonMap;
|
||||
|
@ -12,6 +12,8 @@ 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;
|
||||
|
@ -34,13 +36,13 @@ public class TransactionInstrumentation extends Instrumenter.Default {
|
|||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
packageName + ".SessionMethodUtils",
|
||||
packageName + ".SessionState",
|
||||
"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",
|
||||
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'
|
||||
|
||||
compile project(':dd-java-agent:agent-tooling')
|
||||
compile project(':dd-java-agent:instrumentation:hibernate')
|
||||
|
||||
compile deps.bytebuddy
|
||||
compile deps.opentracing
|
||||
|
@ -47,5 +48,5 @@ dependencies {
|
|||
latestDepTestCompile group: 'org.hibernate', name: 'hibernate-core', version: '+'
|
||||
latestDepTestCompile group: 'com.h2database', name: 'h2', version: '1.4.197'
|
||||
// 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.instrumentation.hibernate.v4_0.HibernateDecorator.DECORATOR;
|
||||
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;
|
||||
|
@ -14,6 +14,7 @@ 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.Map;
|
||||
|
@ -38,12 +39,12 @@ public class SessionFactoryInstrumentation extends Instrumenter.Default {
|
|||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
packageName + ".SessionState",
|
||||
"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",
|
||||
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.instrumentation.hibernate.v4_0.HibernateDecorator.DECORATOR;
|
||||
import static datadog.trace.instrumentation.hibernate.v4_0.SessionMethodUtils.SCOPE_ONLY_METHODS;
|
||||
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;
|
||||
|
@ -15,6 +15,8 @@ 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;
|
||||
|
@ -49,13 +51,13 @@ public class SessionInstrumentation extends Instrumenter.Default {
|
|||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
packageName + ".SessionMethodUtils",
|
||||
packageName + ".SessionState",
|
||||
"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",
|
||||
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'
|
||||
|
||||
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.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 java.util.Collections.singletonMap;
|
||||
|
@ -11,8 +11,8 @@ 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.v4_0.SessionMethodUtils;
|
||||
import datadog.trace.instrumentation.hibernate.v4_0.SessionState;
|
||||
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;
|
||||
|
@ -35,13 +35,13 @@ public class ProcedureCallInstrumentation extends Instrumenter.Default {
|
|||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
"datadog.trace.instrumentation.hibernate.v4_0.SessionMethodUtils",
|
||||
"datadog.trace.instrumentation.hibernate.v4_0.SessionState",
|
||||
"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.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 net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||
|
@ -11,8 +11,8 @@ 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.v4_0.SessionMethodUtils;
|
||||
import datadog.trace.instrumentation.hibernate.v4_0.SessionState;
|
||||
import datadog.trace.instrumentation.hibernate.SessionMethodUtils;
|
||||
import datadog.trace.instrumentation.hibernate.SessionState;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
@ -41,13 +41,13 @@ public class SessionInstrumentation extends Instrumenter.Default {
|
|||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {
|
||||
"datadog.trace.instrumentation.hibernate.v4_0.SessionMethodUtils",
|
||||
"datadog.trace.instrumentation.hibernate.v4_0.SessionState",
|
||||
"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.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.api.DDSpanTypes;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import javax.persistence.Entity;
|
||||
import java.util.Set;
|
||||
|
||||
public class HibernateDecorator extends OrmClientDecorator {
|
||||
public static final HibernateDecorator DECORATOR = new HibernateDecorator();
|
||||
|
@ -46,16 +48,22 @@ public class HibernateDecorator extends OrmClientDecorator {
|
|||
@Override
|
||||
public String entityName(final Object entity) {
|
||||
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) {
|
||||
// We were given an entity name, not the entity itself.
|
||||
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.
|
||||
name = entity.getClass().getName();
|
||||
} else if (entity instanceof List && ((List) entity).size() > 0) {
|
||||
// We have a list of entities.
|
||||
name = entityName(((List) entity).get(0));
|
||||
}
|
||||
|
||||
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.ContextStore;
|
|
@ -1,4 +1,4 @@
|
|||
package datadog.trace.instrumentation.hibernate.v4_0;
|
||||
package datadog.trace.instrumentation.hibernate;
|
||||
|
||||
import io.opentracing.Scope;
|
||||
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-6'
|
||||
include ':dd-java-agent:instrumentation:grpc-1.5'
|
||||
include ':dd-java-agent:instrumentation:hibernate-4.0'
|
||||
include ':dd-java-agent:instrumentation:hibernate-4.3'
|
||||
include ':dd-java-agent:instrumentation:hibernate'
|
||||
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:hystrix-1.4'
|
||||
include ':dd-java-agent:instrumentation:jax-rs-annotations'
|
||||
|
|
Loading…
Reference in New Issue