diff --git a/instrumentation-api/src/main/jflex/SqlSanitizer.flex b/instrumentation-api/src/main/jflex/SqlSanitizer.flex index 618385c8853d..759659107935 100644 --- a/instrumentation-api/src/main/jflex/SqlSanitizer.flex +++ b/instrumentation-api/src/main/jflex/SqlSanitizer.flex @@ -295,6 +295,11 @@ WHITESPACE = [ \t\r\n]+ "FROM" { if (!insideComment && !extractionDone) { + if (operation == NoOp.INSTANCE) { + // hql/jpql queries may skip SELECT and start with FROM clause + // treat such queries as SELECT queries + setOperation(new Select()); + } extractionDone = operation.handleFrom(); } appendCurrentFragment(); diff --git a/instrumentation-api/src/test/groovy/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizerTest.groovy b/instrumentation-api/src/test/groovy/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizerTest.groovy index 5e54696cd394..991198e447d5 100644 --- a/instrumentation-api/src/test/groovy/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizerTest.groovy +++ b/instrumentation-api/src/test/groovy/io/opentelemetry/instrumentation/api/db/SqlStatementSanitizerTest.groovy @@ -80,6 +80,9 @@ class SqlStatementSanitizerTest extends Specification { // whitespace normalization "SELECT * \t\r\nFROM TABLE WHERE FIELD1 = 12344 AND FIELD2 = 5678" | "SELECT * FROM TABLE WHERE FIELD1 = ? AND FIELD2 = ?" + + // hibernate/jpa query language + "FROM TABLE WHERE FIELD=1234" | "FROM TABLE WHERE FIELD=?" } @Unroll @@ -110,6 +113,9 @@ class SqlStatementSanitizerTest extends Specification { '/* update comment */ select * from table1' | SqlStatementInfo.create(sql, 'SELECT', 'table1') 'select /*((*/abc from table' | SqlStatementInfo.create(sql, 'SELECT', 'table') 'SeLeCT * FrOm TAblE' | SqlStatementInfo.create(sql, 'SELECT', 'TAblE') + // hibernate/jpa + 'FROM schema.table' | SqlStatementInfo.create(sql, 'SELECT', 'schema.table') + '/* update comment */ from table1' | SqlStatementInfo.create(sql, 'SELECT', 'table1') // Insert ' insert into table where lalala' | SqlStatementInfo.create(sql, 'INSERT', 'table') 'insert insert into table where lalala' | SqlStatementInfo.create(sql, 'INSERT', 'table') diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/hibernate-3.3-javaagent.gradle b/instrumentation/hibernate/hibernate-3.3/javaagent/hibernate-3.3-javaagent.gradle index a33674637294..6fbb66f387a8 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/hibernate-3.3-javaagent.gradle +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/hibernate-3.3-javaagent.gradle @@ -24,7 +24,7 @@ dependencies { testInstrumentation project(':instrumentation:jdbc:javaagent') // Added to ensure cross compatibility: testInstrumentation project(':instrumentation:hibernate:hibernate-4.0:javaagent') - testInstrumentation project(':instrumentation:hibernate:hibernate-4.3:javaagent') + testInstrumentation project(':instrumentation:hibernate:hibernate-procedure-call-4.3:javaagent') testLibrary "org.hibernate:hibernate-core:3.3.0.SP1" testImplementation "org.hibernate:hibernate-annotations:3.4.0.GA" diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java index 45714ee933c9..2150f234350d 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java @@ -53,7 +53,7 @@ public static void startMethod( ContextStore contextStore = InstrumentationContext.get(Query.class, Context.class); - context = SessionMethodUtils.startSpanFrom(contextStore, query, query.getQueryString(), null); + context = SessionMethodUtils.startSpanFromQuery(contextStore, query, query.getQueryString()); if (context != null) { scope = context.makeCurrent(); } diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/groovy/QueryTest.groovy b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/groovy/QueryTest.groovy index ab3fde82ac5c..ac0bc2e60f60 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/groovy/QueryTest.groovy +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/groovy/QueryTest.groovy @@ -105,25 +105,25 @@ class QueryTest extends AbstractHibernateTest { where: queryMethodName | expectedSpanName | requiresTransaction | queryInteraction - "Query.list" | "from Value" | false | { sess -> + "Query.list" | "SELECT Value" | false | { sess -> Query q = sess.createQuery("from Value") q.list() } - "Query.executeUpdate" | "update Value set name = ?" | true | { sess -> + "Query.executeUpdate" | "UPDATE Value" | true | { sess -> Query q = sess.createQuery("update Value set name = ?") q.setParameter(0, "alyx") q.executeUpdate() } - "Query.uniqueResult" | "from Value where id = ?" | false | { sess -> + "Query.uniqueResult" | "SELECT Value" | false | { sess -> Query q = sess.createQuery("from Value where id = ?") q.setParameter(0, 1L) q.uniqueResult() } - "Query.iterate" | "from Value" | false | { sess -> + "Query.iterate" | "SELECT Value" | false | { sess -> Query q = sess.createQuery("from Value") q.iterate() } - "Query.scroll" | "from Value" | false | { sess -> + "Query.scroll" | "SELECT Value" | false | { sess -> Query q = sess.createQuery("from Value") q.scroll() } @@ -153,7 +153,7 @@ class QueryTest extends AbstractHibernateTest { } } span(1) { - name "from Value" + name "SELECT Value" kind INTERNAL childOf span(0) attributes { diff --git a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/groovy/SessionTest.groovy b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/groovy/SessionTest.groovy index 8c43da3b476e..b7986bb1d4d4 100644 --- a/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/groovy/SessionTest.groovy +++ b/instrumentation/hibernate/hibernate-3.3/javaagent/src/test/groovy/SessionTest.groovy @@ -440,10 +440,10 @@ class SessionTest extends AbstractHibernateTest { } where: - queryMethodName | expectedSpanName | queryBuildMethod - "createQuery" | "from Value" | { sess -> sess.createQuery("from Value") } - "getNamedQuery" | "from Value" | { sess -> sess.getNamedQuery("TestNamedQuery") } - "createSQLQuery" | "SELECT * FROM Value" | { sess -> sess.createSQLQuery("SELECT * FROM Value") } + queryMethodName | expectedSpanName | queryBuildMethod + "createQuery" | "SELECT Value" | { sess -> sess.createQuery("from Value") } + "getNamedQuery" | "SELECT Value" | { sess -> sess.getNamedQuery("TestNamedQuery") } + "createSQLQuery" | "SELECT Value" | { sess -> sess.createSQLQuery("SELECT * FROM Value") } } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/hibernate-4.0-javaagent.gradle b/instrumentation/hibernate/hibernate-4.0/javaagent/hibernate-4.0-javaagent.gradle index ec91ff6fdaf3..9ccb43df660d 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/hibernate-4.0-javaagent.gradle +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/hibernate-4.0-javaagent.gradle @@ -1,4 +1,5 @@ apply from: "$rootDir/gradle/instrumentation.gradle" +apply plugin: 'org.unbroken-dome.test-sets' muzzle { pass { @@ -9,21 +10,53 @@ muzzle { } } +testSets { + version5Test { + dirName = 'test' + } + version6Test { + dirName = 'hibernate6Test' + } + + latestDepTest { + dirName = 'test' + } +} + +test.dependsOn version5Test, version6Test + dependencies { - library "org.hibernate:hibernate-core:4.0.0.Final" + compileOnly "org.hibernate:hibernate-core:4.0.0.Final" implementation project(':instrumentation:hibernate:hibernate-common:javaagent') testInstrumentation project(':instrumentation:jdbc:javaagent') // Added to ensure cross compatibility: testInstrumentation project(':instrumentation:hibernate:hibernate-3.3:javaagent') - testInstrumentation project(':instrumentation:hibernate:hibernate-4.3:javaagent') + testInstrumentation project(':instrumentation:hibernate:hibernate-procedure-call-4.3:javaagent') testImplementation "com.h2database:h2:1.4.197" testImplementation "javax.xml.bind:jaxb-api:2.2.11" testImplementation "com.sun.xml.bind:jaxb-core:2.2.11" testImplementation "com.sun.xml.bind:jaxb-impl:2.2.11" testImplementation "javax.activation:activation:1.1.1" + testImplementation "org.hsqldb:hsqldb:2.0.0" + //First version to work with Java 14 + testImplementation "org.springframework.data:spring-data-jpa:1.8.0.RELEASE" + + testImplementation "org.hibernate:hibernate-core:4.0.0.Final" + testImplementation "org.hibernate:hibernate-entitymanager:4.0.0.Final" + + version5TestImplementation "org.hibernate:hibernate-core:5.0.0.Final" + version5TestImplementation "org.hibernate:hibernate-entitymanager:5.0.0.Final" + version5TestImplementation "org.springframework.data:spring-data-jpa:2.3.0.RELEASE" + + version6TestImplementation "org.hibernate:hibernate-core:6.0.0.Alpha6" + version6TestImplementation "org.hibernate:hibernate-entitymanager:6.0.0.Alpha6" + version6TestImplementation "org.springframework.data:spring-data-jpa:2.3.0.RELEASE" - latestDepTestLibrary "org.hibernate:hibernate-core:4.2.+" + // hibernate 6 is alpha so use 5 as latest version + latestDepTestImplementation "org.hibernate:hibernate-core:5.+" + latestDepTestImplementation "org.hibernate:hibernate-entitymanager:5.+" + latestDepTestImplementation "org.springframework.data:spring-data-jpa:(2.4.0,)" } diff --git a/instrumentation/hibernate/hibernate-4.3/javaagent/src/test/groovy/SpringJpaTest.groovy b/instrumentation/hibernate/hibernate-4.0/javaagent/src/hibernate6Test/groovy/SpringJpaTest.groovy similarity index 100% rename from instrumentation/hibernate/hibernate-4.3/javaagent/src/test/groovy/SpringJpaTest.groovy rename to instrumentation/hibernate/hibernate-4.0/javaagent/src/hibernate6Test/groovy/SpringJpaTest.groovy diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/hibernate6Test/java/Value.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/hibernate6Test/java/Value.java new file mode 100644 index 000000000000..996b544bbde0 --- /dev/null +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/hibernate6Test/java/Value.java @@ -0,0 +1,45 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +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(String name) { + this.name = name; + } + + @Id + @GeneratedValue(generator = "increment") + @GenericGenerator(name = "increment", strategy = "increment") + public Long getId() { + return id; + } + + private void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String title) { + name = title; + } +} diff --git a/instrumentation/hibernate/hibernate-4.3/javaagent/src/test/java/spring/jpa/Customer.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/hibernate6Test/java/spring/jpa/Customer.java similarity index 100% rename from instrumentation/hibernate/hibernate-4.3/javaagent/src/test/java/spring/jpa/Customer.java rename to instrumentation/hibernate/hibernate-4.0/javaagent/src/hibernate6Test/java/spring/jpa/Customer.java diff --git a/instrumentation/hibernate/hibernate-4.3/javaagent/src/test/java/spring/jpa/CustomerRepository.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/hibernate6Test/java/spring/jpa/CustomerRepository.java similarity index 100% rename from instrumentation/hibernate/hibernate-4.3/javaagent/src/test/java/spring/jpa/CustomerRepository.java rename to instrumentation/hibernate/hibernate-4.0/javaagent/src/hibernate6Test/java/spring/jpa/CustomerRepository.java diff --git a/instrumentation/hibernate/hibernate-4.3/javaagent/src/test/java/spring/jpa/PersistenceConfig.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/hibernate6Test/java/spring/jpa/PersistenceConfig.java similarity index 94% rename from instrumentation/hibernate/hibernate-4.3/javaagent/src/test/java/spring/jpa/PersistenceConfig.java rename to instrumentation/hibernate/hibernate-4.0/javaagent/src/hibernate6Test/java/spring/jpa/PersistenceConfig.java index 98afef6b75d7..bd6b88985bf4 100644 --- a/instrumentation/hibernate/hibernate-4.3/javaagent/src/test/java/spring/jpa/PersistenceConfig.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/hibernate6Test/java/spring/jpa/PersistenceConfig.java @@ -57,9 +57,6 @@ private Properties additionalProperties() { properties.setProperty("hibernate.show_sql", "true"); properties.setProperty("hibernate.hbm2ddl.auto", "create"); properties.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect"); - // properties.setProperty( - // "hibernate.format_sql", - // env.getProperty("spring.jpa.properties.hibernate.format_sql")); return properties; } } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java index dfa2b13d306c..be693d2c1e52 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java @@ -53,7 +53,7 @@ public static void startMethod( ContextStore contextStore = InstrumentationContext.get(Query.class, Context.class); - context = SessionMethodUtils.startSpanFrom(contextStore, query, query.getQueryString(), null); + context = SessionMethodUtils.startSpanFromQuery(contextStore, query, query.getQueryString()); if (context != null) { scope = context.makeCurrent(); } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/QueryTest.groovy b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/QueryTest.groovy index 1cbb52f29661..0841d35bb01b 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/QueryTest.groovy +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/QueryTest.groovy @@ -105,25 +105,25 @@ class QueryTest extends AbstractHibernateTest { where: queryMethodName | expectedSpanName | requiresTransaction | queryInteraction - "query/list" | "from Value" | false | { sess -> + "query/list" | "SELECT Value" | false | { sess -> Query q = sess.createQuery("from Value") q.list() } - "query/executeUpdate" | "update Value set name = ?" | true | { sess -> - Query q = sess.createQuery("update Value set name = ?") - q.setParameter(0, "alyx") + "query/executeUpdate" | "UPDATE Value" | true | { sess -> + Query q = sess.createQuery("update Value set name = :name") + q.setParameter("name", "alyx") q.executeUpdate() } - "query/uniqueResult" | "from Value where id = ?" | false | { sess -> - Query q = sess.createQuery("from Value where id = ?") - q.setParameter(0, 1L) + "query/uniqueResult" | "SELECT Value" | false | { sess -> + Query q = sess.createQuery("from Value where id = :id") + q.setParameter("id", 1L) q.uniqueResult() } - "iterate" | "from Value" | false | { sess -> + "iterate" | "SELECT Value" | false | { sess -> Query q = sess.createQuery("from Value") q.iterate() } - "query/scroll" | "from Value" | false | { sess -> + "query/scroll" | "SELECT Value" | false | { sess -> Query q = sess.createQuery("from Value") q.scroll() } @@ -153,7 +153,7 @@ class QueryTest extends AbstractHibernateTest { } } span(1) { - name "from Value" + name "SELECT Value" kind INTERNAL childOf span(0) attributes { diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/SessionTest.groovy b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/SessionTest.groovy index b82fe56d7043..5eb5a9fa81bd 100644 --- a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/SessionTest.groovy +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/SessionTest.groovy @@ -379,10 +379,10 @@ class SessionTest extends AbstractHibernateTest { } where: - queryMethodName | resource | queryBuildMethod - "createQuery" | "from Value" | { sess -> sess.createQuery("from Value") } - "getNamedQuery" | "from Value" | { sess -> sess.getNamedQuery("TestNamedQuery") } - "createSQLQuery" | "SELECT * FROM Value" | { sess -> sess.createSQLQuery("SELECT * FROM Value") } + queryMethodName | resource | queryBuildMethod + "createQuery" | "SELECT Value" | { sess -> sess.createQuery("from Value") } + "getNamedQuery" | "SELECT Value" | { sess -> sess.getNamedQuery("TestNamedQuery") } + "createSQLQuery" | "SELECT Value" | { sess -> sess.createSQLQuery("SELECT * FROM Value") } } diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/SpringJpaTest.groovy b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/SpringJpaTest.groovy new file mode 100644 index 000000000000..7cc1ce6264dd --- /dev/null +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/SpringJpaTest.groovy @@ -0,0 +1,198 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import static io.opentelemetry.api.trace.SpanKind.CLIENT + +import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes +import org.springframework.context.annotation.AnnotationConfigApplicationContext +import spock.lang.Shared +import spring.jpa.Customer +import spring.jpa.CustomerRepository +import spring.jpa.PersistenceConfig + +/** + * Unfortunately this test verifies that our hibernate instrumentation doesn't currently work with Spring Data Repositories. + */ +class SpringJpaTest extends AgentInstrumentationSpecification { + + @Shared + def context = new AnnotationConfigApplicationContext(PersistenceConfig) + + @Shared + def repo = context.getBean(CustomerRepository) + + def "test CRUD"() { + setup: + def customer = new Customer("Bob", "Anonymous") + + expect: + customer.id == null + !repo.findAll().iterator().hasNext() + + assertTraces(1) { + trace(0, 1) { + span(0) { + name "SELECT test.Customer" + kind CLIENT + attributes { + "${SemanticAttributes.DB_SYSTEM.key}" "hsqldb" + "${SemanticAttributes.DB_NAME.key}" "test" + "${SemanticAttributes.DB_USER.key}" "sa" + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "hsqldb:mem:" + "${SemanticAttributes.DB_STATEMENT.key}" ~/select ([^\.]+)\.id([^\,]*), ([^\.]+)\.firstName([^\,]*), ([^\.]+)\.lastName(.*)from Customer(.*)/ + "${SemanticAttributes.DB_OPERATION.key}" "SELECT" + "${SemanticAttributes.DB_SQL_TABLE.key}" "Customer" + } + } + } + } + clearExportedData() + + when: + repo.save(customer) + def savedId = customer.id + + then: + customer.id != null + // Behavior changed in new version: + def extraTrace = traces.size() == 2 + assertTraces(extraTrace ? 2 : 1) { + if (extraTrace) { + trace(0, 1) { + span(0) { + name "test" + kind CLIENT + attributes { + "${SemanticAttributes.DB_SYSTEM.key}" "hsqldb" + "${SemanticAttributes.DB_NAME.key}" "test" + "${SemanticAttributes.DB_USER.key}" "sa" + "${SemanticAttributes.DB_STATEMENT.key}" "call next value for hibernate_sequence" + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "hsqldb:mem:" + } + } + } + } + trace(extraTrace ? 1 : 0, 1) { + span(0) { + name "INSERT test.Customer" + kind CLIENT + attributes { + "${SemanticAttributes.DB_SYSTEM.key}" "hsqldb" + "${SemanticAttributes.DB_NAME.key}" "test" + "${SemanticAttributes.DB_USER.key}" "sa" + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "hsqldb:mem:" + "${SemanticAttributes.DB_STATEMENT.key}" ~/insert into Customer \(.*\) values \(.*, \?, \?\)/ + "${SemanticAttributes.DB_OPERATION.key}" "INSERT" + "${SemanticAttributes.DB_SQL_TABLE.key}" "Customer" + } + } + } + } + clearExportedData() + + when: + customer.firstName = "Bill" + repo.save(customer) + + then: + customer.id == savedId + assertTraces(2) { + trace(0, 1) { + span(0) { + name "SELECT test.Customer" + kind CLIENT + attributes { + "${SemanticAttributes.DB_SYSTEM.key}" "hsqldb" + "${SemanticAttributes.DB_NAME.key}" "test" + "${SemanticAttributes.DB_USER.key}" "sa" + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "hsqldb:mem:" + "${SemanticAttributes.DB_STATEMENT.key}" ~/select ([^\.]+)\.id([^\,]*), ([^\.]+)\.firstName([^\,]*), ([^\.]+)\.lastName (.*)from Customer (.*)where ([^\.]+)\.id( ?)=( ?)\?/ + "${SemanticAttributes.DB_OPERATION.key}" "SELECT" + "${SemanticAttributes.DB_SQL_TABLE.key}" "Customer" + } + } + } + trace(1, 1) { + span(0) { + name "UPDATE test.Customer" + kind CLIENT + attributes { + "${SemanticAttributes.DB_SYSTEM.key}" "hsqldb" + "${SemanticAttributes.DB_NAME.key}" "test" + "${SemanticAttributes.DB_USER.key}" "sa" + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "hsqldb:mem:" + "${SemanticAttributes.DB_STATEMENT.key}" "update Customer set firstName=?, lastName=? where id=?" + "${SemanticAttributes.DB_OPERATION.key}" "UPDATE" + "${SemanticAttributes.DB_SQL_TABLE.key}" "Customer" + } + } + } + } + clearExportedData() + + when: + customer = repo.findByLastName("Anonymous")[0] + + then: + customer.id == savedId + customer.firstName == "Bill" + assertTraces(1) { + trace(0, 1) { + span(0) { + name "SELECT test.Customer" + kind CLIENT + attributes { + "${SemanticAttributes.DB_SYSTEM.key}" "hsqldb" + "${SemanticAttributes.DB_NAME.key}" "test" + "${SemanticAttributes.DB_USER.key}" "sa" + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "hsqldb:mem:" + "${SemanticAttributes.DB_STATEMENT.key}" ~/select ([^\.]+)\.id([^\,]*), ([^\.]+)\.firstName([^\,]*), ([^\.]+)\.lastName (.*)from Customer (.*)(where ([^\.]+)\.lastName( ?)=( ?)\?|)/ + "${SemanticAttributes.DB_OPERATION.key}" "SELECT" + "${SemanticAttributes.DB_SQL_TABLE.key}" "Customer" + } + } + } + } + clearExportedData() + + when: + repo.delete(customer) + + then: + assertTraces(2) { + trace(0, 1) { + span(0) { + name "SELECT test.Customer" + kind CLIENT + attributes { + "${SemanticAttributes.DB_SYSTEM.key}" "hsqldb" + "${SemanticAttributes.DB_NAME.key}" "test" + "${SemanticAttributes.DB_USER.key}" "sa" + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "hsqldb:mem:" + "${SemanticAttributes.DB_STATEMENT.key}" ~/select ([^\.]+)\.id([^\,]*), ([^\.]+)\.firstName([^\,]*), ([^\.]+)\.lastName (.*)from Customer (.*)where ([^\.]+)\.id( ?)=( ?)\?/ + "${SemanticAttributes.DB_OPERATION.key}" "SELECT" + "${SemanticAttributes.DB_SQL_TABLE.key}" "Customer" + } + } + } + trace(1, 1) { + span(0) { + name "DELETE test.Customer" + kind CLIENT + attributes { + "${SemanticAttributes.DB_SYSTEM.key}" "hsqldb" + "${SemanticAttributes.DB_NAME.key}" "test" + "${SemanticAttributes.DB_USER.key}" "sa" + "${SemanticAttributes.DB_CONNECTION_STRING.key}" "hsqldb:mem:" + "${SemanticAttributes.DB_STATEMENT.key}" "delete from Customer where id=?" + "${SemanticAttributes.DB_OPERATION.key}" "DELETE" + "${SemanticAttributes.DB_SQL_TABLE.key}" "Customer" + } + } + } + } + } +} diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/Customer.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/Customer.java new file mode 100644 index 000000000000..27e7d6894055 --- /dev/null +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/Customer.java @@ -0,0 +1,78 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package spring.jpa; + +import java.util.Objects; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class Customer { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String firstName; + private String lastName; + + protected Customer() {} + + public Customer(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + @Override + public String toString() { + return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id, firstName, lastName); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Customer)) { + return false; + } + Customer other = (Customer) obj; + return Objects.equals(id, other.id) + && Objects.equals(firstName, other.firstName) + && Objects.equals(lastName, other.lastName); + } + + @Override + public int hashCode() { + return Objects.hash(id, firstName, lastName); + } +} diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/CustomerRepository.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/CustomerRepository.java new file mode 100644 index 000000000000..9662f2a4b59c --- /dev/null +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/CustomerRepository.java @@ -0,0 +1,14 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package spring.jpa; + +import java.util.List; +import org.springframework.data.repository.CrudRepository; + +public interface CustomerRepository extends CrudRepository { + + List findByLastName(String lastName); +} diff --git a/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/PersistenceConfig.java b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/PersistenceConfig.java new file mode 100644 index 000000000000..bd6b88985bf4 --- /dev/null +++ b/instrumentation/hibernate/hibernate-4.0/javaagent/src/test/java/spring/jpa/PersistenceConfig.java @@ -0,0 +1,62 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package spring.jpa; + +import java.util.Properties; +import javax.sql.DataSource; +import org.springframework.context.annotation.Bean; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.Database; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.transaction.PlatformTransactionManager; + +@EnableJpaRepositories(basePackages = "spring/jpa") +public class PersistenceConfig { + + @Bean(name = "transactionManager") + public PlatformTransactionManager dbTransactionManager() { + JpaTransactionManager transactionManager = new JpaTransactionManager(); + transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); + return transactionManager; + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + + HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); + vendorAdapter.setDatabase(Database.HSQL); + vendorAdapter.setGenerateDdl(true); + + LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); + em.setDataSource(dataSource()); + em.setPackagesToScan("spring/jpa"); + em.setJpaVendorAdapter(vendorAdapter); + em.setJpaProperties(additionalProperties()); + + return em; + } + + @Bean + public DataSource dataSource() { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName("org.hsqldb.jdbcDriver"); + dataSource.setUrl("jdbc:hsqldb:mem:test"); + dataSource.setUsername("sa"); + dataSource.setPassword("1"); + return dataSource; + } + + private Properties additionalProperties() { + Properties properties = new Properties(); + properties.setProperty("hibernate.show_sql", "true"); + properties.setProperty("hibernate.hbm2ddl.auto", "create"); + properties.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect"); + return properties; + } +} diff --git a/instrumentation/hibernate/hibernate-4.3/javaagent/hibernate-4.3-javaagent.gradle b/instrumentation/hibernate/hibernate-4.3/javaagent/hibernate-4.3-javaagent.gradle deleted file mode 100644 index f340c1012f76..000000000000 --- a/instrumentation/hibernate/hibernate-4.3/javaagent/hibernate-4.3-javaagent.gradle +++ /dev/null @@ -1,60 +0,0 @@ -apply from: "$rootDir/gradle/instrumentation.gradle" -apply plugin: 'org.unbroken-dome.test-sets' - -muzzle { - pass { - group = "org.hibernate" - module = "hibernate-core" - versions = "[4.3.0.Final,)" - assertInverse = true - } -} - -testSets { - version5Test { - dirName = 'test' - } - version6Test { - dirName = 'test' - } - - latestDepTest { - dirName = 'test' - } -} - -test.dependsOn version5Test, version6Test - -dependencies { - compileOnly "org.hibernate:hibernate-core:4.3.0.Final" - - implementation project(':instrumentation:hibernate:hibernate-common:javaagent') - - testInstrumentation project(':instrumentation:jdbc:javaagent') - // Added to ensure cross compatibility: - testInstrumentation project(':instrumentation:hibernate:hibernate-3.3:javaagent') - testInstrumentation project(':instrumentation:hibernate:hibernate-4.0:javaagent') - - testImplementation "org.hibernate:hibernate-core:4.3.0.Final" - testImplementation "org.hibernate:hibernate-entitymanager:4.3.0.Final" - testImplementation "org.hsqldb:hsqldb:2.0.0" - //First version to work with Java 14 - testImplementation "org.springframework.data:spring-data-jpa:1.8.0.RELEASE" - - version5TestImplementation "org.hibernate:hibernate-core:5.0.0.Final" - version5TestImplementation "org.hibernate:hibernate-entitymanager:5.0.0.Final" - version5TestImplementation "org.springframework.data:spring-data-jpa:2.3.0.RELEASE" - - version6TestImplementation "org.hibernate:hibernate-core:6.0.0.Alpha6" - version6TestImplementation "org.hibernate:hibernate-entitymanager:6.0.0.Alpha6" - version6TestImplementation "org.springframework.data:spring-data-jpa:2.3.0.RELEASE" - - testImplementation "javax.activation:javax.activation-api:1.2.0" - testImplementation "javax.xml.bind:jaxb-api:2.3.1" - testImplementation "org.glassfish.jaxb:jaxb-runtime:2.3.3" - - // hibernate 6 is alpha so use 5 as latest version - latestDepTestImplementation "org.hibernate:hibernate-core:5.+" - latestDepTestImplementation "org.hibernate:hibernate-entitymanager:5.+" - latestDepTestImplementation "org.springframework.data:spring-data-jpa:(2.4.0,)" -} diff --git a/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/SessionMethodUtils.java b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/SessionMethodUtils.java index 74543bb0b1d4..d793a43c7c9f 100644 --- a/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/SessionMethodUtils.java +++ b/instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/SessionMethodUtils.java @@ -9,11 +9,14 @@ import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.db.SqlStatementInfo; +import io.opentelemetry.instrumentation.api.db.SqlStatementSanitizer; import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap; import io.opentelemetry.javaagent.instrumentation.api.ContextStore; import java.util.Arrays; import java.util.HashSet; import java.util.Set; +import java.util.function.Supplier; import org.checkerframework.checker.nullness.qual.Nullable; public class SessionMethodUtils { @@ -26,6 +29,14 @@ public static Context startSpanFrom( TARGET spanKey, String operationName, ENTITY entity) { + return startSpanFrom(contextStore, spanKey, () -> operationName, entity); + } + + private static Context startSpanFrom( + ContextStore contextStore, + TARGET spanKey, + Supplier operationNameSupplier, + ENTITY entity) { Context sessionContext = contextStore.get(spanKey); if (sessionContext == null) { @@ -37,7 +48,26 @@ public static Context startSpanFrom( return null; // This method call is being traced already. } - return tracer().startSpan(sessionContext, operationName, entity); + return tracer().startSpan(sessionContext, operationNameSupplier.get(), entity); + } + + public static Context startSpanFromQuery( + ContextStore contextStore, TARGET spanKey, String query) { + Supplier operationNameSupplier = + () -> { + // set operation to default value that is used when sql sanitizer fails to extract + // operation name + String operation = "Hibernate Query"; + SqlStatementInfo info = SqlStatementSanitizer.sanitize(query); + if (info.getOperation() != null) { + operation = info.getOperation(); + if (info.getTable() != null) { + operation += " " + info.getTable(); + } + } + return operation; + }; + return startSpanFrom(contextStore, spanKey, operationNameSupplier, null); } public static void end( diff --git a/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/hibernate-procedure-call-4.3-javaagent.gradle b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/hibernate-procedure-call-4.3-javaagent.gradle new file mode 100644 index 000000000000..fcfd02dee509 --- /dev/null +++ b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/hibernate-procedure-call-4.3-javaagent.gradle @@ -0,0 +1,31 @@ +apply from: "$rootDir/gradle/instrumentation.gradle" + +muzzle { + pass { + group = "org.hibernate" + module = "hibernate-core" + versions = "[4.3.0.Final,)" + assertInverse = true + } +} + +dependencies { + library "org.hibernate:hibernate-core:4.3.0.Final" + + implementation project(':instrumentation:hibernate:hibernate-common:javaagent') + + testInstrumentation project(':instrumentation:jdbc:javaagent') + // Added to ensure cross compatibility: + testInstrumentation project(':instrumentation:hibernate:hibernate-3.3:javaagent') + testInstrumentation project(':instrumentation:hibernate:hibernate-4.0:javaagent') + + testLibrary "org.hibernate:hibernate-entitymanager:4.3.0.Final" + + testImplementation "org.hsqldb:hsqldb:2.0.0" + testImplementation "javax.xml.bind:jaxb-api:2.3.1" + testImplementation "org.glassfish.jaxb:jaxb-runtime:2.3.3" + + // hibernate 6 is alpha so use 5 as latest version + latestDepTestLibrary "org.hibernate:hibernate-core:5.+" + latestDepTestLibrary "org.hibernate:hibernate-entitymanager:5.+" +} diff --git a/instrumentation/hibernate/hibernate-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/HibernateInstrumentationModule.java b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/HibernateInstrumentationModule.java similarity index 100% rename from instrumentation/hibernate/hibernate-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/HibernateInstrumentationModule.java rename to instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/HibernateInstrumentationModule.java diff --git a/instrumentation/hibernate/hibernate-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallInstrumentation.java b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallInstrumentation.java similarity index 100% rename from instrumentation/hibernate/hibernate-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallInstrumentation.java rename to instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallInstrumentation.java diff --git a/instrumentation/hibernate/hibernate-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/SessionInstrumentation.java b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/SessionInstrumentation.java similarity index 100% rename from instrumentation/hibernate/hibernate-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/SessionInstrumentation.java rename to instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/SessionInstrumentation.java diff --git a/instrumentation/hibernate/hibernate-4.3/javaagent/src/test/groovy/ProcedureCallTest.groovy b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/groovy/ProcedureCallTest.groovy similarity index 99% rename from instrumentation/hibernate/hibernate-4.3/javaagent/src/test/groovy/ProcedureCallTest.groovy rename to instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/groovy/ProcedureCallTest.groovy index 402d8ec67064..3ced5e988d50 100644 --- a/instrumentation/hibernate/hibernate-4.3/javaagent/src/test/groovy/ProcedureCallTest.groovy +++ b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/groovy/ProcedureCallTest.groovy @@ -23,7 +23,6 @@ import spock.lang.Shared class ProcedureCallTest extends AgentInstrumentationSpecification { - @Shared protected SessionFactory sessionFactory diff --git a/instrumentation/hibernate/hibernate-4.3/javaagent/src/test/java/Value.java b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/java/Value.java similarity index 100% rename from instrumentation/hibernate/hibernate-4.3/javaagent/src/test/java/Value.java rename to instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/java/Value.java diff --git a/instrumentation/hibernate/hibernate-4.3/javaagent/src/test/resources/hibernate.cfg.xml b/instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/resources/hibernate.cfg.xml similarity index 100% rename from instrumentation/hibernate/hibernate-4.3/javaagent/src/test/resources/hibernate.cfg.xml rename to instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/test/resources/hibernate.cfg.xml diff --git a/settings.gradle b/settings.gradle index e94d533f65f7..3213629251c6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -139,8 +139,8 @@ include ':instrumentation:guava-10.0:library' include ':instrumentation:gwt-2.0:javaagent' include ':instrumentation:hibernate:hibernate-3.3:javaagent' include ':instrumentation:hibernate:hibernate-4.0:javaagent' -include ':instrumentation:hibernate:hibernate-4.3:javaagent' include ':instrumentation:hibernate:hibernate-common:javaagent' +include ':instrumentation:hibernate:hibernate-procedure-call-4.3:javaagent' include ':instrumentation:http-url-connection:javaagent' include ':instrumentation:hystrix-1.4:javaagent' include ':instrumentation:java-http-client:javaagent'