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:
parent
9a05c1a0fd
commit
35d6bdb730
|
@ -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();
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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") }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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,)"
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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") }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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,)"
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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.+"
|
||||
}
|
|
@ -23,7 +23,6 @@ import spock.lang.Shared
|
|||
|
||||
class ProcedureCallTest extends AgentInstrumentationSpecification {
|
||||
|
||||
|
||||
@Shared
|
||||
protected SessionFactory sessionFactory
|
||||
|
|
@ -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'
|
||||
|
|
Loading…
Reference in New Issue