From e8f0eaf85e537f7d466944f667020a63f4e49ba6 Mon Sep 17 00:00:00 2001 From: Will Gittoes Date: Mon, 11 Feb 2019 14:47:21 +1100 Subject: [PATCH] Add basic Hibernate integration --- .../hibernate/hibernate.gradle | 68 ++++++ .../hibernate/HibernateInstrumentation.java | 208 ++++++++++++++++++ .../hibernate/QueryInstrumentation.java | 85 +++++++ .../hibernate/SessionMethodUtils.java | 71 ++++++ .../hibernate/TransactionInstrumentation.java | 85 +++++++ .../src/test/groovy/HibernateTest.groovy | 75 +++++++ .../hibernate/src/test/java/Value.java | 40 ++++ .../src/test/resources/hibernate.cfg.xml | 29 +++ .../java/datadog/trace/api/DDSpanTypes.java | 1 + settings.gradle | 1 + 10 files changed, 663 insertions(+) create mode 100644 dd-java-agent/instrumentation/hibernate/hibernate.gradle create mode 100644 dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/HibernateInstrumentation.java create mode 100644 dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/QueryInstrumentation.java create mode 100644 dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/SessionMethodUtils.java create mode 100644 dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/TransactionInstrumentation.java create mode 100644 dd-java-agent/instrumentation/hibernate/src/test/groovy/HibernateTest.groovy create mode 100644 dd-java-agent/instrumentation/hibernate/src/test/java/Value.java create mode 100644 dd-java-agent/instrumentation/hibernate/src/test/resources/hibernate.cfg.xml diff --git a/dd-java-agent/instrumentation/hibernate/hibernate.gradle b/dd-java-agent/instrumentation/hibernate/hibernate.gradle new file mode 100644 index 0000000000..7048449a9f --- /dev/null +++ b/dd-java-agent/instrumentation/hibernate/hibernate.gradle @@ -0,0 +1,68 @@ +// 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 = "[5.4.2.Final]" +// assertInverse = true +// } + +// fail { +// group = "org.elasticsearch.client" +// module = "rest" +// versions = "(,)" +// assertInverse = true +// } +//} + +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: '5.4.1.Final' + +// compile project(':dd-trace-api') + compile project(':dd-java-agent:agent-tooling') + + compile deps.bytebuddy + compile deps.opentracing + annotationProcessor deps.autoservice + implementation deps.autoservice + + testCompile project(':dd-java-agent:testing') + + + testCompile group: 'org.hibernate', name: 'hibernate-core', version: '5.4.1.Final' + testCompile group: 'com.h2database', name: 'h2', version: '1.4.197' +// testCompile group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.5' // todo log4j + +// // Include httpclient instrumentation for testing because it is a dependency for elasticsearch-rest-client. +// // It doesn't actually work though. They use HttpAsyncClient, which isn't currently instrumented. +// // TODO: add Apache's HttpAsyncClient instrumentation when that is complete. +// testCompile project(':dd-java-agent:instrumentation:apache-httpclient-4') +// +// testCompile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0' +// testCompile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.0' +// +// testCompile group: 'org.elasticsearch.client', name: 'elasticsearch-rest-client', version: '6.4.0' +// testCompile group: 'org.elasticsearch', name: 'elasticsearch', version: '6.4.0' +// testCompile group: 'org.elasticsearch.plugin', name: 'transport-netty4-client', version: '6.4.0' +// +// latestDepTestCompile group: 'org.elasticsearch.client', name: 'elasticsearch-rest-client', version: '+' +// latestDepTestCompile group: 'org.elasticsearch.client', name: 'transport', version: '6.+' +// latestDepTestCompile group: 'org.elasticsearch', name: 'elasticsearch', version: '6.+' +// latestDepTestCompile group: 'org.elasticsearch.plugin', name: 'transport-netty4-client', version: '6.+' +} diff --git a/dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/HibernateInstrumentation.java b/dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/HibernateInstrumentation.java new file mode 100644 index 0000000000..f7260ebe04 --- /dev/null +++ b/dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/HibernateInstrumentation.java @@ -0,0 +1,208 @@ +package datadog.trace.instrumentation.hibernate; + +import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; +import static io.opentracing.log.Fields.ERROR_OBJECT; +import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy; +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.api.DDSpanTypes; +import datadog.trace.api.DDTags; +import datadog.trace.bootstrap.ContextStore; +import datadog.trace.bootstrap.InstrumentationContext; +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.tag.Tags; +import io.opentracing.util.GlobalTracer; +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.matcher.ElementMatcher; +import org.hibernate.Session; +import org.hibernate.SharedSessionContract; +import org.hibernate.Transaction; +import org.hibernate.internal.AbstractSharedSessionContract; +import org.hibernate.query.Query; +import org.hibernate.query.spi.QueryImplementor; + +@AutoService(Instrumenter.class) +public class HibernateInstrumentation extends Instrumenter.Default { + + public HibernateInstrumentation() { + super("hibernate"); + } + + @Override + public Map contextStore() { + final Map map = new HashMap<>(); + map.put("org.hibernate.Session", Span.class.getName()); + map.put("org.hibernate.query.Query", Span.class.getName()); + map.put("org.hibernate.Transaction", Span.class.getName()); + return Collections.unmodifiableMap(map); + } + + @Override + public String[] helperClassNames() { + return new String[] { + "datadog.trace.instrumentation.hibernate.SessionMethodUtils", + }; + } + + @Override + public ElementMatcher typeMatcher() { + return not(isInterface()) + .and( + safeHasSuperType( + named("org.hibernate.SessionFactory") + .or(named("org.hibernate.Session")) + .or(named("org.hibernate.internal.AbstractSharedSessionContract")))); + } + + @Override + public Map, String> transformers() { + final Map, String> transformers = new HashMap<>(); + transformers.put( + isMethod() + .and(named("openSession")) + .and(isDeclaredBy(safeHasSuperType(named("org.hibernate.SessionFactory")))) + .and(takesArguments(0)) + .and(returns(named("org.hibernate.Session"))), + SessionFactoryAdvice.class.getName()); + transformers.put( + isMethod() + .and(named("close")) + .and(isDeclaredBy(safeHasSuperType(named("org.hibernate.Session")))) + .and(takesArguments(0)), + SessionCloseAdvice.class.getName()); + transformers.put( + isMethod() + .and(named("save")) + .and(isDeclaredBy(safeHasSuperType(named("org.hibernate.Session")))) + .and(takesArguments(1)), + SessionSaveAdvice.class.getName()); + transformers.put( + isMethod() + .and(named("beginTransaction").or(named("getTransaction"))) + .and(isDeclaredBy(safeHasSuperType(named("org.hibernate.SharedSessionContract")))) + .and(takesArguments(0)) + .and(returns(named("org.hibernate.Transaction"))), + GetTransactionAdvice.class.getName()); + transformers.put( + isMethod() + .and(named("createQuery")) + .and(isDeclaredBy(safeHasSuperType(named("org.hibernate.SharedSessionContract")))) + .and(takesArguments(1)), + GetQueryAdvice.class.getName()); + + return transformers; + } + + public static class SessionFactoryAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void openSession(@Advice.Return(readOnly = false) final Session session) { + + final Span span = + GlobalTracer.get() + .buildSpan("hibernate.session") + .withTag(DDTags.SERVICE_NAME, "hibernate") + .withTag(DDTags.SPAN_TYPE, DDSpanTypes.HIBERNATE) + .withTag(Tags.COMPONENT.getKey(), "hibernate-java") + .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT) + .start(); + + final ContextStore contextStore = + InstrumentationContext.get(Session.class, Span.class); + contextStore.putIfAbsent(session, span); + } + } + + public static class SessionCloseAdvice { + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void closeSession( + @Advice.This final Session session, @Advice.Thrown final Throwable throwable) { + + final ContextStore contextStore = + InstrumentationContext.get(Session.class, Span.class); + final Span span = contextStore.get(session); + + if (span == null) { + return; + } + if (throwable != null) { + Tags.ERROR.set(span, true); + span.log(Collections.singletonMap(ERROR_OBJECT, throwable)); + } + span.finish(); + } + } + + public static class SessionSaveAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static Scope startSave( + @Advice.This final Session session, @Advice.Argument(0) final Object entity) { + + final ContextStore contextStore = + InstrumentationContext.get(Session.class, Span.class); + return SessionMethodUtils.startScopeFrom(contextStore, session, "hibernate.save"); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void endSave( + @Advice.This final Session session, + @Advice.Enter final Scope scope, + @Advice.Thrown final Throwable throwable) { + + SessionMethodUtils.closeScope(scope, throwable); + } + } + + public static class GetQueryAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void getQuery( + @Advice.This final SharedSessionContract session, + @Advice.Return(readOnly = false) final QueryImplementor query) { + + if (!(query instanceof Query)) { + return; + } + + final ContextStore sessionContextStore = + InstrumentationContext.get(Session.class, Span.class); + final ContextStore queryContextStore = + InstrumentationContext.get(Query.class, Span.class); + + SessionMethodUtils.attachSpanFromSession( + sessionContextStore, session, queryContextStore, query); + } + } + + public static class GetTransactionAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void getTransaction( + @Advice.This final AbstractSharedSessionContract session, + @Advice.Return(readOnly = false) final Transaction transaction) { + + final ContextStore sessionContextStore = + InstrumentationContext.get(Session.class, Span.class); + final ContextStore transactionContextStore = + InstrumentationContext.get(Transaction.class, Span.class); + + SessionMethodUtils.attachSpanFromSession( + sessionContextStore, session, transactionContextStore, transaction); + } + } +} diff --git a/dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/QueryInstrumentation.java b/dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/QueryInstrumentation.java new file mode 100644 index 0000000000..cd44c85f70 --- /dev/null +++ b/dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/QueryInstrumentation.java @@ -0,0 +1,85 @@ +package datadog.trace.instrumentation.hibernate; + +import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy; +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 io.opentracing.Scope; +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.matcher.ElementMatcher; +import org.hibernate.query.Query; + +@AutoService(Instrumenter.class) +public class QueryInstrumentation extends Instrumenter.Default { + + public QueryInstrumentation() { + super("hibernate"); + } + + @Override + public Map contextStore() { + final Map map = new HashMap<>(); + map.put("org.hibernate.query.Query", Span.class.getName()); + return Collections.unmodifiableMap(map); + } + + @Override + public String[] helperClassNames() { + return new String[] { + "datadog.trace.instrumentation.hibernate.SessionMethodUtils", + }; + } + + @Override + public ElementMatcher typeMatcher() { + return not(isInterface()).and(safeHasSuperType(named("org.hibernate.query.Query"))); + } + + @Override + public Map, String> transformers() { + final Map, String> transformers = new HashMap<>(); + transformers.put( + isMethod() + .and(named("list")) + .and(isDeclaredBy(safeHasSuperType(named("org.hibernate.Query")))) + .and(takesArguments(0)), + QueryListAdvice.class.getName()); + + return transformers; + } + + public static class QueryListAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static Scope startList(@Advice.This final Query query) { + + final ContextStore contextStore = + InstrumentationContext.get(Query.class, Span.class); + + return SessionMethodUtils.startScopeFrom(contextStore, query, "hibernate.query.list"); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void endList( + @Advice.This final Query query, + @Advice.Enter final Scope scope, + @Advice.Thrown final Throwable throwable) { + + SessionMethodUtils.closeScope(scope, throwable); + } + } +} diff --git a/dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/SessionMethodUtils.java b/dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/SessionMethodUtils.java new file mode 100644 index 0000000000..ddff1308d2 --- /dev/null +++ b/dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/SessionMethodUtils.java @@ -0,0 +1,71 @@ +package datadog.trace.instrumentation.hibernate; + +import static io.opentracing.log.Fields.ERROR_OBJECT; + +import datadog.trace.api.DDSpanTypes; +import datadog.trace.api.DDTags; +import datadog.trace.bootstrap.ContextStore; +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.tag.Tags; +import io.opentracing.util.GlobalTracer; +import java.util.Collections; +import org.hibernate.Session; +import org.hibernate.SharedSessionContract; + +public class SessionMethodUtils { + + // Starts a scope as a child from a Span, where the Span is attached to the given spanKey using + // the given contextStore. + public static Scope startScopeFrom( + final ContextStore contextStore, final T spanKey, final String operationName) { + + final Span span = contextStore.get(spanKey); + + final Scope scope = + GlobalTracer.get() + .buildSpan(operationName) + .asChildOf(span) // Can be null. + .withTag(DDTags.SERVICE_NAME, "hibernate") + .withTag(DDTags.SPAN_TYPE, DDSpanTypes.HIBERNATE) + .withTag(Tags.COMPONENT.getKey(), "hibernate-java") + .startActive(true); + + return scope; + } + + // Closes a Scope/Span, adding an error tag if the given Throwable is not null. + public static void closeScope(final Scope scope, final Throwable throwable) { + + final Span span = scope.span(); + if (throwable != null) { + Tags.ERROR.set(span, true); + span.log(Collections.singletonMap(ERROR_OBJECT, throwable)); + } + + span.finish(); + scope.close(); + } + + // Copies a span from the given Session ContextStore into the targetContextStore. Used to + // propagate a Span from a Session to transient Session objects such as Transaction and Query. + public static void attachSpanFromSession( + final ContextStore sessionContextStore, + final SharedSessionContract session, + final ContextStore targetContextStore, + final T target) { + + if (!(session instanceof Session) + || sessionContextStore == null + || targetContextStore == null) { + return; + } + + final Span span = sessionContextStore.get((Session) session); + if (span == null) { + return; + } + + targetContextStore.putIfAbsent(target, span); + } +} diff --git a/dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/TransactionInstrumentation.java b/dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/TransactionInstrumentation.java new file mode 100644 index 0000000000..47b2501343 --- /dev/null +++ b/dd-java-agent/instrumentation/hibernate/src/main/java/datadog/trace/instrumentation/hibernate/TransactionInstrumentation.java @@ -0,0 +1,85 @@ +package datadog.trace.instrumentation.hibernate; + +import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy; +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 io.opentracing.Scope; +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.matcher.ElementMatcher; +import org.hibernate.Transaction; + +@AutoService(Instrumenter.class) +public class TransactionInstrumentation extends Instrumenter.Default { + + public TransactionInstrumentation() { + super("hibernate"); + } + + @Override + public Map contextStore() { + final Map map = new HashMap<>(); + map.put("org.hibernate.Transaction", Span.class.getName()); + return Collections.unmodifiableMap(map); + } + + @Override + public String[] helperClassNames() { + return new String[] { + "datadog.trace.instrumentation.hibernate.SessionMethodUtils", + }; + } + + @Override + public ElementMatcher typeMatcher() { + return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Transaction"))); + } + + @Override + public Map, String> transformers() { + final Map, String> transformers = new HashMap<>(); + transformers.put( + isMethod() + .and(named("commit")) + .and(isDeclaredBy(safeHasSuperType(named("org.hibernate.Transaction")))) + .and(takesArguments(0)), + TransactionCommitAdvice.class.getName()); + return transformers; + } + + public static class TransactionCommitAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static Scope startCommit(@Advice.This final Transaction transaction) { + + final ContextStore contextStore = + InstrumentationContext.get(Transaction.class, Span.class); + + return SessionMethodUtils.startScopeFrom( + contextStore, transaction, "hibernate.transaction.commit"); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void endCommit( + @Advice.This final Transaction transaction, + @Advice.Enter final Scope scope, + @Advice.Thrown final Throwable throwable) { + + SessionMethodUtils.closeScope(scope, throwable); + } + } +} diff --git a/dd-java-agent/instrumentation/hibernate/src/test/groovy/HibernateTest.groovy b/dd-java-agent/instrumentation/hibernate/src/test/groovy/HibernateTest.groovy new file mode 100644 index 0000000000..9e9f725b59 --- /dev/null +++ b/dd-java-agent/instrumentation/hibernate/src/test/groovy/HibernateTest.groovy @@ -0,0 +1,75 @@ +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.api.DDSpanTypes +import org.hibernate.Session +import org.hibernate.SessionFactory +import org.hibernate.boot.MetadataSources +import org.hibernate.boot.registry.StandardServiceRegistry +import org.hibernate.boot.registry.StandardServiceRegistryBuilder +import spock.lang.Shared + +class HibernateTest extends AgentTestRunner { + + @Shared + private SessionFactory sessionFactory + + def setupSpec() { + final StandardServiceRegistry registry = + new StandardServiceRegistryBuilder() + .configure() + .build() + try { + sessionFactory = new MetadataSources(registry).buildMetadata().buildSessionFactory(); + } catch (Exception e) { + System.out.println(e.toString()); + StandardServiceRegistryBuilder.destroy(registry); + } + + } + + def cleanupSpec() { + if (sessionFactory != null) { + sessionFactory.close() + } + } + + def "test hibernate session"() { + setup: + Session session = sessionFactory.openSession() + session.beginTransaction() + session.save(new Value("A Hibernate value to be serialized")) + session.save(new Value("Another value")) + session.getTransaction().commit() + session.close() + +// session = sessionFactory.openSession() +// session.beginTransaction() +// List result = session.createQuery("from Value").list() +// for (Value value : (List) result) { +// System.out.println(value.getName()) +// } +// session.getTransaction().commit() +// session.close() + + expect: +// result.size() == 2 + + assertTraces(1) { + trace(0, 4) { + span(0) { + serviceName "hibernate" + resourceName "hibernate.session" + operationName "hibernate.session" + spanType DDSpanTypes.HIBERNATE + parent() + tags { + "$Tags.COMPONENT.key" "hibernate-java" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$DDTags.SPAN_TYPE" DDSpanTypes.HIBERNATE + defaultTags() + } + } + } + } + } + +} diff --git a/dd-java-agent/instrumentation/hibernate/src/test/java/Value.java b/dd-java-agent/instrumentation/hibernate/src/test/java/Value.java new file mode 100644 index 0000000000..1be9cd60ed --- /dev/null +++ b/dd-java-agent/instrumentation/hibernate/src/test/java/Value.java @@ -0,0 +1,40 @@ +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; + +@Entity +@Table +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; + } +} diff --git a/dd-java-agent/instrumentation/hibernate/src/test/resources/hibernate.cfg.xml b/dd-java-agent/instrumentation/hibernate/src/test/resources/hibernate.cfg.xml new file mode 100644 index 0000000000..5b20390ba9 --- /dev/null +++ b/dd-java-agent/instrumentation/hibernate/src/test/resources/hibernate.cfg.xml @@ -0,0 +1,29 @@ + + + + + + + + + org.h2.Driver + jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE + sa + + org.hibernate.dialect.H2Dialect + + 1 + org.hibernate.cache.internal.NoCacheProvider + true + + + create + + + + + + + diff --git a/dd-trace-api/src/main/java/datadog/trace/api/DDSpanTypes.java b/dd-trace-api/src/main/java/datadog/trace/api/DDSpanTypes.java index 4491b970e1..e92fd2beb0 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/DDSpanTypes.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/DDSpanTypes.java @@ -14,6 +14,7 @@ public class DDSpanTypes { public static final String REDIS = "redis"; public static final String MEMCACHED = "memcached"; public static final String ELASTICSEARCH = "elasticsearch"; + public static final String HIBERNATE = "hibernate"; // TODO: Could this just be "db", or "orm"? public static final String MESSAGE_CLIENT = "queue"; public static final String MESSAGE_CONSUMER = "queue"; diff --git a/settings.gradle b/settings.gradle index 96064c335f..69e8f79673 100644 --- a/settings.gradle +++ b/settings.gradle @@ -39,6 +39,7 @@ 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' include ':dd-java-agent:instrumentation:http-url-connection' include ':dd-java-agent:instrumentation:hystrix-1.4' include ':dd-java-agent:instrumentation:jax-rs-annotations'