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

This commit is contained in:
Lauri Tulmin 2021-07-26 10:29:40 +03:00 committed by GitHub
parent 26dc106399
commit 3555c251c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 209 additions and 99 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 ->
val.setName("New name")
sesh.saveOrUpdate(val)
"saveOrUpdate save with entity name" | "saveOrUpdate" | "Value" | { sesh, val ->
sesh.saveOrUpdate("Value", new Value("Value"))
}
"merge" | "merge" | "Value" | { sesh, val ->
"saveOrUpdate update with entity name" | "saveOrUpdate" | "Value" | { sesh, val ->
val.setName("New name")
sesh.saveOrUpdate("Value", 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)
}
}

View File

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

View File

@ -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() {}
}

View File

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