Browse Source

Hibernate: set span name only on method entry (#3603)

Lauri Tulmin 3 years ago
parent
commit
3555c251c9
18 changed files with 208 additions and 98 deletions
  1. 9 4
      instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/CriteriaInstrumentation.java
  2. 30 0
      instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/EntityNameUtil.java
  3. 1 1
      instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java
  4. 8 6
      instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionInstrumentation.java
  5. 1 1
      instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/TransactionInstrumentation.java
  6. 1 1
      instrumentation/hibernate/hibernate-3.3/javaagent/src/test/groovy/CriteriaTest.groovy
  7. 1 1
      instrumentation/hibernate/hibernate-3.3/javaagent/src/test/groovy/SessionTest.groovy
  8. 9 5
      instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaInstrumentation.java
  9. 34 0
      instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityNameUtil.java
  10. 1 1
      instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java
  11. 9 6
      instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionInstrumentation.java
  12. 1 1
      instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/TransactionInstrumentation.java
  13. 1 1
      instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/AbstractHibernateTest.groovy
  14. 1 1
      instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/CriteriaTest.groovy
  15. 67 19
      instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/SessionTest.groovy
  16. 5 36
      instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateTracer.java
  17. 28 13
      instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/SessionMethodUtils.java
  18. 1 1
      instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallInstrumentation.java

+ 9 - 4
instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/CriteriaInstrumentation.java

@@ -21,9 +21,9 @@ import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
 import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils;
 import net.bytebuddy.asm.Advice;
 import net.bytebuddy.description.type.TypeDescription;
-import net.bytebuddy.implementation.bytecode.assign.Assigner;
 import net.bytebuddy.matcher.ElementMatcher;
 import org.hibernate.Criteria;
+import org.hibernate.impl.CriteriaImpl;
 
 public class CriteriaInstrumentation implements TypeInstrumentation {
 
@@ -63,7 +63,13 @@ public class CriteriaInstrumentation implements TypeInstrumentation {
       ContextStore<Criteria, Context> contextStore =
           InstrumentationContext.get(Criteria.class, Context.class);
 
-      context = SessionMethodUtils.startSpanFrom(contextStore, criteria, "Criteria." + name, null);
+      String entityName = null;
+      if (criteria instanceof CriteriaImpl) {
+        entityName = ((CriteriaImpl) criteria).getEntityOrClassName();
+      }
+
+      context =
+          SessionMethodUtils.startSpanFrom(contextStore, criteria, "Criteria." + name, entityName);
       if (context != null) {
         scope = context.makeCurrent();
       }
@@ -72,7 +78,6 @@ public class CriteriaInstrumentation implements TypeInstrumentation {
     @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
     public static void endMethod(
         @Advice.Thrown Throwable throwable,
-        @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object entity,
         @Advice.Origin("#m") String name,
         @Advice.Local("otelCallDepth") CallDepth callDepth,
         @Advice.Local("otelContext") Context context,
@@ -84,7 +89,7 @@ public class CriteriaInstrumentation implements TypeInstrumentation {
 
       if (scope != null) {
         scope.close();
-        SessionMethodUtils.end(context, throwable, "Criteria." + name, entity);
+        SessionMethodUtils.end(context, throwable);
       }
     }
   }

+ 30 - 0
instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/EntityNameUtil.java

@@ -0,0 +1,30 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.hibernate.v3_3;
+
+import java.util.function.Function;
+import org.hibernate.impl.AbstractSessionImpl;
+
+public final class EntityNameUtil {
+
+  private EntityNameUtil() {}
+
+  private static String bestGuessEntityName(Object session, Object entity) {
+    if (entity == null) {
+      return null;
+    }
+
+    if (session instanceof AbstractSessionImpl) {
+      return ((AbstractSessionImpl) session).bestGuessEntityName(entity);
+    }
+
+    return null;
+  }
+
+  public static Function<Object, String> bestGuessEntityName(Object session) {
+    return (entity) -> bestGuessEntityName(session, entity);
+  }
+}

+ 1 - 1
instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/QueryInstrumentation.java

@@ -80,7 +80,7 @@ public class QueryInstrumentation implements TypeInstrumentation {
 
       if (scope != null) {
         scope.close();
-        SessionMethodUtils.end(context, throwable, null, null);
+        SessionMethodUtils.end(context, throwable);
       }
     }
   }

+ 8 - 6
instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/SessionInstrumentation.java

@@ -9,6 +9,7 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.
 import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
 import static io.opentelemetry.javaagent.instrumentation.hibernate.HibernateTracer.tracer;
 import static io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils.SCOPE_ONLY_METHODS;
+import static io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils.getEntityName;
 import static net.bytebuddy.matcher.ElementMatchers.isMethod;
 import static net.bytebuddy.matcher.ElementMatchers.named;
 import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
@@ -26,7 +27,6 @@ import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
 import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils;
 import net.bytebuddy.asm.Advice;
 import net.bytebuddy.description.type.TypeDescription;
-import net.bytebuddy.implementation.bytecode.assign.Assigner;
 import net.bytebuddy.matcher.ElementMatcher;
 import org.hibernate.Criteria;
 import org.hibernate.Query;
@@ -131,7 +131,9 @@ public class SessionInstrumentation implements TypeInstrumentation {
     public static void startMethod(
         @Advice.This Object session,
         @Advice.Origin("#m") String name,
-        @Advice.Argument(0) Object entity,
+        @Advice.Origin("#d") String descriptor,
+        @Advice.Argument(0) Object arg0,
+        @Advice.Argument(value = 1, optional = true) Object arg1,
         @Advice.Local("otelCallDepth") CallDepth callDepth,
         @Advice.Local("otelContext") Context spanContext,
         @Advice.Local("otelScope") Scope scope) {
@@ -157,7 +159,9 @@ public class SessionInstrumentation implements TypeInstrumentation {
       }
 
       if (!SCOPE_ONLY_METHODS.contains(name)) {
-        spanContext = tracer().startSpan(sessionContext, "Session." + name, entity);
+        String entityName =
+            getEntityName(descriptor, arg0, arg1, EntityNameUtil.bestGuessEntityName(session));
+        spanContext = tracer().startSpan(sessionContext, "Session." + name, entityName);
         scope = spanContext.makeCurrent();
       } else {
         scope = sessionContext.makeCurrent();
@@ -167,8 +171,6 @@ public class SessionInstrumentation implements TypeInstrumentation {
     @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
     public static void endMethod(
         @Advice.Thrown Throwable throwable,
-        @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object returned,
-        @Advice.Origin("#m") String name,
         @Advice.Local("otelCallDepth") CallDepth callDepth,
         @Advice.Local("otelContext") Context spanContext,
         @Advice.Local("otelScope") Scope scope) {
@@ -179,7 +181,7 @@ public class SessionInstrumentation implements TypeInstrumentation {
 
       if (scope != null) {
         scope.close();
-        SessionMethodUtils.end(spanContext, throwable, "Session." + name, returned);
+        SessionMethodUtils.end(spanContext, throwable);
       }
     }
   }

+ 1 - 1
instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/TransactionInstrumentation.java

@@ -81,7 +81,7 @@ public class TransactionInstrumentation implements TypeInstrumentation {
 
       if (scope != null) {
         scope.close();
-        SessionMethodUtils.end(context, throwable, null, null);
+        SessionMethodUtils.end(context, throwable);
       }
     }
   }

+ 1 - 1
instrumentation/hibernate/hibernate-3.3/javaagent/src/test/groovy/CriteriaTest.groovy

@@ -36,7 +36,7 @@ class CriteriaTest extends AbstractHibernateTest {
           }
         }
         span(1) {
-          name "Criteria.$methodName"
+          name "Criteria.$methodName Value"
           kind INTERNAL
           childOf span(0)
           attributes {

+ 1 - 1
instrumentation/hibernate/hibernate-3.3/javaagent/src/test/groovy/SessionTest.groovy

@@ -283,7 +283,7 @@ class SessionTest extends AbstractHibernateTest {
           }
         }
         span(1) {
-          name "Session.replicate"
+          name "Session.replicate java.lang.Long"
           kind INTERNAL
           childOf span(0)
           status ERROR

+ 9 - 5
instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/CriteriaInstrumentation.java

@@ -21,9 +21,9 @@ import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
 import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils;
 import net.bytebuddy.asm.Advice;
 import net.bytebuddy.description.type.TypeDescription;
-import net.bytebuddy.implementation.bytecode.assign.Assigner;
 import net.bytebuddy.matcher.ElementMatcher;
 import org.hibernate.Criteria;
+import org.hibernate.internal.CriteriaImpl;
 
 public class CriteriaInstrumentation implements TypeInstrumentation {
 
@@ -63,7 +63,13 @@ public class CriteriaInstrumentation implements TypeInstrumentation {
       ContextStore<Criteria, Context> contextStore =
           InstrumentationContext.get(Criteria.class, Context.class);
 
-      context = SessionMethodUtils.startSpanFrom(contextStore, criteria, "Criteria." + name, null);
+      String entityName = null;
+      if (criteria instanceof CriteriaImpl) {
+        entityName = ((CriteriaImpl) criteria).getEntityOrClassName();
+      }
+
+      context =
+          SessionMethodUtils.startSpanFrom(contextStore, criteria, "Criteria." + name, entityName);
       if (context != null) {
         scope = context.makeCurrent();
       }
@@ -72,8 +78,6 @@ public class CriteriaInstrumentation implements TypeInstrumentation {
     @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
     public static void endMethod(
         @Advice.Thrown Throwable throwable,
-        @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object entity,
-        @Advice.Origin("#m") String name,
         @Advice.Local("otelCallDepth") CallDepth callDepth,
         @Advice.Local("otelContext") Context context,
         @Advice.Local("otelScope") Scope scope) {
@@ -84,7 +88,7 @@ public class CriteriaInstrumentation implements TypeInstrumentation {
 
       if (scope != null) {
         scope.close();
-        SessionMethodUtils.end(context, throwable, "Criteria." + name, entity);
+        SessionMethodUtils.end(context, throwable);
       }
     }
   }

+ 34 - 0
instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/EntityNameUtil.java

@@ -0,0 +1,34 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.hibernate.v4_0;
+
+import java.util.function.Function;
+import org.hibernate.SharedSessionContract;
+import org.hibernate.internal.SessionImpl;
+import org.hibernate.internal.StatelessSessionImpl;
+
+public final class EntityNameUtil {
+
+  private EntityNameUtil() {}
+
+  private static String bestGuessEntityName(SharedSessionContract session, Object entity) {
+    if (entity == null) {
+      return null;
+    }
+
+    if (session instanceof SessionImpl) {
+      return ((SessionImpl) session).bestGuessEntityName(entity);
+    } else if (session instanceof StatelessSessionImpl) {
+      return ((StatelessSessionImpl) session).bestGuessEntityName(entity);
+    }
+
+    return null;
+  }
+
+  public static Function<Object, String> bestGuessEntityName(SharedSessionContract session) {
+    return (entity) -> bestGuessEntityName(session, entity);
+  }
+}

+ 1 - 1
instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/QueryInstrumentation.java

@@ -80,7 +80,7 @@ public class QueryInstrumentation implements TypeInstrumentation {
 
       if (scope != null) {
         scope.close();
-        SessionMethodUtils.end(context, throwable, null, null);
+        SessionMethodUtils.end(context, throwable);
       }
     }
   }

+ 9 - 6
instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/SessionInstrumentation.java

@@ -9,6 +9,7 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.
 import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
 import static io.opentelemetry.javaagent.instrumentation.hibernate.HibernateTracer.tracer;
 import static io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils.SCOPE_ONLY_METHODS;
+import static io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils.getEntityName;
 import static io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils.getSessionMethodSpanName;
 import static net.bytebuddy.matcher.ElementMatchers.isMethod;
 import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -27,7 +28,6 @@ import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
 import io.opentelemetry.javaagent.instrumentation.hibernate.SessionMethodUtils;
 import net.bytebuddy.asm.Advice;
 import net.bytebuddy.description.type.TypeDescription;
-import net.bytebuddy.implementation.bytecode.assign.Assigner;
 import net.bytebuddy.matcher.ElementMatcher;
 import org.hibernate.Criteria;
 import org.hibernate.Query;
@@ -125,7 +125,9 @@ public class SessionInstrumentation implements TypeInstrumentation {
     public static void startMethod(
         @Advice.This SharedSessionContract session,
         @Advice.Origin("#m") String name,
-        @Advice.Argument(0) Object entity,
+        @Advice.Origin("#d") String descriptor,
+        @Advice.Argument(0) Object arg0,
+        @Advice.Argument(value = 1, optional = true) Object arg1,
         @Advice.Local("otelCallDepth") CallDepth callDepth,
         @Advice.Local("otelContext") Context spanContext,
         @Advice.Local("otelScope") Scope scope) {
@@ -144,7 +146,10 @@ public class SessionInstrumentation implements TypeInstrumentation {
       }
 
       if (!SCOPE_ONLY_METHODS.contains(name)) {
-        spanContext = tracer().startSpan(sessionContext, getSessionMethodSpanName(name), entity);
+        String entityName =
+            getEntityName(descriptor, arg0, arg1, EntityNameUtil.bestGuessEntityName(session));
+        spanContext =
+            tracer().startSpan(sessionContext, getSessionMethodSpanName(name), entityName);
         scope = spanContext.makeCurrent();
       } else {
         scope = sessionContext.makeCurrent();
@@ -154,8 +159,6 @@ public class SessionInstrumentation implements TypeInstrumentation {
     @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
     public static void endMethod(
         @Advice.Thrown Throwable throwable,
-        @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object returned,
-        @Advice.Origin("#m") String name,
         @Advice.Local("otelCallDepth") CallDepth callDepth,
         @Advice.Local("otelContext") Context spanContext,
         @Advice.Local("otelScope") Scope scope) {
@@ -166,7 +169,7 @@ public class SessionInstrumentation implements TypeInstrumentation {
 
       if (scope != null) {
         scope.close();
-        SessionMethodUtils.end(spanContext, throwable, getSessionMethodSpanName(name), returned);
+        SessionMethodUtils.end(spanContext, throwable);
       }
     }
   }

+ 1 - 1
instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/TransactionInstrumentation.java

@@ -81,7 +81,7 @@ public class TransactionInstrumentation implements TypeInstrumentation {
 
       if (scope != null) {
         scope.close();
-        SessionMethodUtils.end(context, throwable, null, null);
+        SessionMethodUtils.end(context, throwable);
       }
     }
   }

+ 1 - 1
instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/AbstractHibernateTest.groovy

@@ -23,7 +23,7 @@ abstract class AbstractHibernateTest extends AgentInstrumentationSpecification {
     Session writer = sessionFactory.openSession()
     writer.beginTransaction()
     prepopulated = new ArrayList<>()
-    for (int i = 0; i < 2; i++) {
+    for (int i = 0; i < 5; i++) {
       prepopulated.add(new Value("Hello :) " + i))
       writer.save(prepopulated.get(i))
     }

+ 1 - 1
instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/CriteriaTest.groovy

@@ -36,7 +36,7 @@ class CriteriaTest extends AbstractHibernateTest {
           }
         }
         span(1) {
-          name "Criteria.$methodName"
+          name "Criteria.$methodName Value"
           kind INTERNAL
           childOf span(0)
           attributes {

+ 67 - 19
instrumentation/hibernate/hibernate-4.0/javaagent/src/test/groovy/SessionTest.groovy

@@ -9,6 +9,7 @@ import static io.opentelemetry.api.trace.StatusCode.ERROR
 
 import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
 import org.hibernate.LockMode
+import org.hibernate.LockOptions
 import org.hibernate.MappingException
 import org.hibernate.Query
 import org.hibernate.ReplicationMode
@@ -84,29 +85,56 @@ class SessionTest extends AbstractHibernateTest {
     }
 
     where:
-    testName                                  | methodName | resource | sessionImplementations                    | sessionMethodTest
-    "lock"                                    | "lock"     | "Value"  | [sessionBuilder]                          | { sesh, val ->
+    testName                                     | methodName | resource | sessionImplementations                    | sessionMethodTest
+    "lock"                                       | "lock"     | "Value"  | [sessionBuilder]                          | { sesh, val ->
       sesh.lock(val, LockMode.READ)
     }
-    "refresh"                                 | "refresh"  | "Value"  | [sessionBuilder, statelessSessionBuilder] | { sesh, val ->
+    "lock with entity name"                      | "lock"     | "Value"  | [sessionBuilder]                          | { sesh, val ->
+      sesh.lock("Value", val, LockMode.READ)
+    }
+    "lock with null name"                        | "lock"     | "Value"  | [sessionBuilder]                          | { sesh, val ->
+      sesh.lock(null, val, LockMode.READ)
+    }
+    "buildLockRequest"                           | "lock"     | "Value"  | [sessionBuilder]                          | { sesh, val ->
+      sesh.buildLockRequest(LockOptions.READ)
+        .lock(val)
+    }
+    "refresh"                                    | "refresh"  | "Value"  | [sessionBuilder, statelessSessionBuilder] | { sesh, val ->
       sesh.refresh(val)
     }
-    "get"                                     | "get"      | "Value"  | [sessionBuilder, statelessSessionBuilder] | { sesh, val ->
+    "refresh with entity name"                   | "refresh"  | "Value"  | [sessionBuilder, statelessSessionBuilder] | { sesh, val ->
+      sesh.refresh("Value", val)
+    }
+    "get with entity name"                       | "get"      | "Value"  | [sessionBuilder, statelessSessionBuilder] | { sesh, val ->
       sesh.get("Value", val.getId())
     }
-    "insert"                                  | "insert"   | "Value"  | [statelessSessionBuilder]                 | { sesh, val ->
+    "get with entity class"                      | "get"      | "Value"  | [sessionBuilder, statelessSessionBuilder] | { sesh, val ->
+      sesh.get(Value, val.getId())
+    }
+    "insert"                                     | "insert"   | "Value"  | [statelessSessionBuilder]                 | { sesh, val ->
+      sesh.insert(new Value("insert me"))
+    }
+    "insert with entity name"                    | "insert"   | "Value"  | [statelessSessionBuilder]                 | { sesh, val ->
       sesh.insert("Value", new Value("insert me"))
     }
-    "update (StatelessSession)"               | "update"   | "Value"  | [statelessSessionBuilder]                 | { sesh, val ->
+    "insert with null entity name"               | "insert"   | "Value"  | [statelessSessionBuilder]                 | { sesh, val ->
+      sesh.insert(null, new Value("insert me"))
+    }
+    "update (StatelessSession)"                  | "update"   | "Value"  | [statelessSessionBuilder]                 | { sesh, val ->
       val.setName("New name")
       sesh.update(val)
     }
-    "update by entityName (StatelessSession)" | "update"   | "Value"  | [statelessSessionBuilder]                 | { sesh, val ->
+    "update with entity name (StatelessSession)" | "update"   | "Value"  | [statelessSessionBuilder]                 | { sesh, val ->
       val.setName("New name")
       sesh.update("Value", val)
     }
-    "delete (Session)"                        | "delete"   | "Value"  | [statelessSessionBuilder]                 | { sesh, val ->
+    "delete (Session)"                           | "delete"   | "Value"  | [statelessSessionBuilder]                 | { sesh, val ->
       sesh.delete(val)
+      prepopulated.remove(val)
+    }
+    "delete with entity name (Session)"          | "delete"   | "Value"  | [statelessSessionBuilder]                 | { sesh, val ->
+      sesh.delete("Value", val)
+      prepopulated.remove(val)
     }
   }
 
@@ -222,7 +250,7 @@ class SessionTest extends AbstractHibernateTest {
           }
         }
         span(1) {
-          name "Session.replicate"
+          name "Session.replicate java.lang.Long"
           kind INTERNAL
           childOf span(0)
           status ERROR
@@ -297,33 +325,53 @@ class SessionTest extends AbstractHibernateTest {
     }
 
     where:
-    testName                         | methodName     | resource | sessionMethodTest
-    "save"                           | "save"         | "Value"  | { sesh, val ->
+    testName                               | methodName     | resource | sessionMethodTest
+    "save"                                 | "save"         | "Value"  | { sesh, val ->
       sesh.save(new Value("Another value"))
     }
-    "saveOrUpdate save"              | "saveOrUpdate" | "Value"  | { sesh, val ->
+    "save with entity name"                | "save"         | "Value"  | { sesh, val ->
+      sesh.save("Value", new Value("Another value"))
+    }
+    "saveOrUpdate save"                    | "saveOrUpdate" | "Value"  | { sesh, val ->
       sesh.saveOrUpdate(new Value("Value"))
     }
-    "saveOrUpdate update"            | "saveOrUpdate" | "Value"  | { sesh, val ->
+    "saveOrUpdate save with entity name"   | "saveOrUpdate" | "Value"  | { sesh, val ->
+      sesh.saveOrUpdate("Value", new Value("Value"))
+    }
+    "saveOrUpdate update with entity name" | "saveOrUpdate" | "Value"  | { sesh, val ->
       val.setName("New name")
-      sesh.saveOrUpdate(val)
+      sesh.saveOrUpdate("Value", val)
     }
-    "merge"                          | "merge"        | "Value"  | { sesh, val ->
+    "merge"                                | "merge"        | "Value"  | { sesh, val ->
       sesh.merge(new Value("merge me in"))
     }
-    "persist"                        | "persist"      | "Value"  | { sesh, val ->
+    "merge with entity name"               | "merge"        | "Value"  | { sesh, val ->
+      sesh.merge("Value", new Value("merge me in"))
+    }
+    "persist"                              | "persist"      | "Value"  | { sesh, val ->
       sesh.persist(new Value("merge me in"))
     }
-    "update (Session)"               | "update"       | "Value"  | { sesh, val ->
+    "persist with entity name"             | "persist"      | "Value"  | { sesh, val ->
+      sesh.persist("Value", new Value("merge me in"))
+    }
+    "persist with null entity name"        | "persist"      | "Value"  | { sesh, val ->
+      sesh.persist(null, new Value("merge me in"))
+    }
+    "update (Session)"                     | "update"       | "Value"  | { sesh, val ->
       val.setName("New name")
       sesh.update(val)
     }
-    "update by entityName (Session)" | "update"       | "Value"  | { sesh, val ->
+    "update by entityName (Session)"       | "update"       | "Value"  | { sesh, val ->
       val.setName("New name")
       sesh.update("Value", val)
     }
-    "delete (Session)"               | "delete"       | "Value"  | { sesh, val ->
+    "delete (Session)"                     | "delete"       | "Value"  | { sesh, val ->
       sesh.delete(val)
+      prepopulated.remove(val)
+    }
+    "delete by entityName (Session)"       | "delete"       | "Value"  | { sesh, val ->
+      sesh.delete("Value", val)
+      prepopulated.remove(val)
     }
   }
 

+ 5 - 36
instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/HibernateTracer.java

@@ -8,10 +8,6 @@ package io.opentelemetry.javaagent.instrumentation.hibernate;
 import io.opentelemetry.api.trace.SpanKind;
 import io.opentelemetry.context.Context;
 import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
-import java.lang.annotation.Annotation;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
 
 public class HibernateTracer extends BaseTracer {
   private static final HibernateTracer TRACER = new HibernateTracer();
@@ -20,48 +16,21 @@ public class HibernateTracer extends BaseTracer {
     return TRACER;
   }
 
-  public Context startSpan(Context parentContext, String operationName, Object entity) {
-    return startSpan(parentContext, spanNameForOperation(operationName, entity));
+  public Context startSpan(Context parentContext, String operationName, String entityName) {
+    return startSpan(parentContext, spanNameForOperation(operationName, entityName));
   }
 
   public Context startSpan(Context parentContext, String spanName) {
     return startSpan(parentContext, spanName, SpanKind.INTERNAL);
   }
 
-  private String spanNameForOperation(String operationName, Object entity) {
-    if (entity != null) {
-      String entityName = entityName(entity);
-      if (entityName != null) {
-        return operationName + " " + entityName;
-      }
+  private static String spanNameForOperation(String operationName, String entityName) {
+    if (entityName != null) {
+      return operationName + " " + entityName;
     }
     return operationName;
   }
 
-  String entityName(Object entity) {
-    if (entity == null) {
-      return null;
-    }
-    String name = null;
-    Set<String> annotations = new HashSet<>();
-    for (Annotation annotation : entity.getClass().getDeclaredAnnotations()) {
-      annotations.add(annotation.annotationType().getName());
-    }
-
-    if (entity instanceof String) {
-      // We were given an entity name, not the entity itself.
-      name = (String) entity;
-    } else if (annotations.contains("javax.persistence.Entity")) {
-      // We were given an instance of an entity.
-      name = entity.getClass().getName();
-    } else if (entity instanceof List && !((List) entity).isEmpty()) {
-      // We have a list of entities.
-      name = entityName(((List) entity).get(0));
-    }
-
-    return name;
-  }
-
   @Override
   protected String getInstrumentationName() {
     return "io.opentelemetry.hibernate-common";

+ 28 - 13
instrumentation/hibernate/hibernate-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/SessionMethodUtils.java

@@ -7,7 +7,6 @@ package io.opentelemetry.javaagent.instrumentation.hibernate;
 
 import static io.opentelemetry.javaagent.instrumentation.hibernate.HibernateTracer.tracer;
 
-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;
@@ -15,6 +14,7 @@ import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.function.Function;
 import java.util.function.Supplier;
 import org.checkerframework.checker.nullness.qual.Nullable;
 
@@ -27,22 +27,22 @@ public final class SessionMethodUtils {
       ContextStore<TARGET, Context> contextStore,
       TARGET spanKey,
       String operationName,
-      ENTITY entity) {
-    return startSpanFrom(contextStore, spanKey, () -> operationName, entity);
+      String entityName) {
+    return startSpanFrom(contextStore, spanKey, () -> operationName, entityName);
   }
 
   private static <TARGET, ENTITY> Context startSpanFrom(
       ContextStore<TARGET, Context> contextStore,
       TARGET spanKey,
       Supplier<String> operationNameSupplier,
-      ENTITY entity) {
+      String entityName) {
 
     Context sessionContext = contextStore.get(spanKey);
     if (sessionContext == null) {
       return null; // No state found. We aren't in a Session.
     }
 
-    return tracer().startSpan(sessionContext, operationNameSupplier.get(), entity);
+    return tracer().startSpan(sessionContext, operationNameSupplier.get(), entityName);
   }
 
   public static <TARGET> Context startSpanFromQuery(
@@ -64,19 +64,12 @@ public final class SessionMethodUtils {
     return startSpanFrom(contextStore, spanKey, operationNameSupplier, null);
   }
 
-  public static void end(
-      @Nullable Context context, Throwable throwable, String operationName, Object entity) {
+  public static void end(@Nullable Context context, Throwable throwable) {
 
     if (context == null) {
       return;
     }
 
-    if (operationName != null && entity != null) {
-      String entityName = tracer().entityName(entity);
-      if (entityName != null) {
-        Span.fromContext(context).updateName(operationName + " " + entityName);
-      }
-    }
     if (throwable != null) {
       tracer().endExceptionally(context, throwable);
     } else {
@@ -107,5 +100,27 @@ public final class SessionMethodUtils {
     return "Session." + methodName;
   }
 
+  public static String getEntityName(
+      String descriptor, Object arg0, Object arg1, Function<Object, String> nameFromEntity) {
+    String entityName = null;
+    // methods like save(String entityName, Object object)
+    // that take entity name as first argument and entity as second
+    // if given entity name is null compute it from entity object
+    if (descriptor.startsWith("(Ljava/lang/String;Ljava/lang/Object;")) {
+      entityName = arg0 == null ? nameFromEntity.apply(arg1) : (String) arg0;
+      // methods like save(Object obj)
+    } else if (descriptor.startsWith("(Ljava/lang/Object;")) {
+      entityName = nameFromEntity.apply(arg0);
+      // methods like get(String entityName, Serializable id)
+    } else if (descriptor.startsWith("(Ljava/lang/String;")) {
+      entityName = (String) arg0;
+      // methods like get(Class entityClass, Serializable id)
+    } else if (descriptor.startsWith("(Ljava/lang/Class;") && arg0 != null) {
+      entityName = ((Class<?>) arg0).getName();
+    }
+
+    return entityName;
+  }
+
   private SessionMethodUtils() {}
 }

+ 1 - 1
instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_3/ProcedureCallInstrumentation.java

@@ -82,7 +82,7 @@ public class ProcedureCallInstrumentation implements TypeInstrumentation {
 
       if (scope != null) {
         scope.close();
-        SessionMethodUtils.end(context, throwable, null, null);
+        SessionMethodUtils.end(context, throwable);
       }
     }
   }