Hibernate query span naming (#3106)

* Hibernate query span naming

* remove commented out code

* modify query sanitizer to accept queries that start with from clause

* add sql sanitizer test for queries starting with from

* rename hibernate-4.3 to hibernate-procedure-call-4.3
This commit is contained in:
Lauri Tulmin 2021-05-28 21:31:52 +03:00 committed by GitHub
parent 9a05c1a0fd
commit 35d6bdb730
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 534 additions and 96 deletions

View File

@ -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();

View File

@ -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')

View File

@ -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"

View File

@ -53,7 +53,7 @@ public class QueryInstrumentation implements TypeInstrumentation {
ContextStore<Query, Context> 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();
}

View File

@ -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 {

View File

@ -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") }
}

View File

@ -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"
latestDepTestLibrary "org.hibernate:hibernate-core:4.2.+"
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"
// 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,)"
}

View File

@ -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;
}
}

View File

@ -57,9 +57,6 @@ public class PersistenceConfig {
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;
}
}

View File

@ -53,7 +53,7 @@ public class QueryInstrumentation implements TypeInstrumentation {
ContextStore<Query, Context> 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();
}

View File

@ -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 {

View File

@ -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") }
}

View File

@ -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"
}
}
}
}
}
}

View File

@ -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);
}
}

View File

@ -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<Customer, Long> {
List<Customer> findByLastName(String lastName);
}

View File

@ -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;
}
}

View File

@ -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,)"
}

View File

@ -9,11 +9,14 @@ import static io.opentelemetry.javaagent.instrumentation.hibernate.HibernateTrac
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 class SessionMethodUtils {
TARGET spanKey,
String operationName,
ENTITY entity) {
return startSpanFrom(contextStore, spanKey, () -> operationName, entity);
}
private static <TARGET, ENTITY> Context startSpanFrom(
ContextStore<TARGET, Context> contextStore,
TARGET spanKey,
Supplier<String> operationNameSupplier,
ENTITY entity) {
Context sessionContext = contextStore.get(spanKey);
if (sessionContext == null) {
@ -37,7 +48,26 @@ public class SessionMethodUtils {
return null; // This method call is being traced already.
}
return tracer().startSpan(sessionContext, operationName, entity);
return tracer().startSpan(sessionContext, operationNameSupplier.get(), entity);
}
public static <TARGET> Context startSpanFromQuery(
ContextStore<TARGET, Context> contextStore, TARGET spanKey, String query) {
Supplier<String> 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(

View File

@ -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.+"
}

View File

@ -23,7 +23,6 @@ import spock.lang.Shared
class ProcedureCallTest extends AgentInstrumentationSpecification {
@Shared
protected SessionFactory sessionFactory

View File

@ -141,8 +141,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'