Use ClassValue to store ThreadLocal call depth (DataDog/dd-trace-java#1528)
MODIFY
agent-bootstrap/src/main/java/io/opentelemetry/auto/bootstrap/CallDepthThreadLocalMap.java
agent-bootstrap/src/main/java/io/opentelemetry/auto/bootstrap/CallDepthThreadLocalMap.java
*/
package io.opentelemetry.auto.bootstrap;
-import java.util.HashMap;
-import java.util.Map;
-
/**
* Utility to track nested instrumentation.
*
@@ -25,27 +22,39 @@ import java.util.Map;
* #incrementCallDepth at the beginning of each constructor.
*/
public class CallDepthThreadLocalMap {
- private static final ThreadLocal<Map<Object, Integer>> TLS =
- new ThreadLocal<Map<Object, Integer>>() {
+
+ private static final ClassValue<ThreadLocalDepth> TLS =
+ new ClassValue<ThreadLocalDepth>() {
@Override
- public Map<Object, Integer> initialValue() {
- return new HashMap<>();
+ protected ThreadLocalDepth computeValue(Class<?> type) {
+ return new ThreadLocalDepth();
}
};
- public static int incrementCallDepth(final Object k) {
- final Map<Object, Integer> map = TLS.get();
- Integer depth = map.get(k);
- if (depth == null) {
- depth = 0;
- } else {
- depth += 1;
+ public static int incrementCallDepth(final Class<?> k) {
+ return TLS.get(k).get().increment();
+ }
+
+ public static void reset(final Class<?> k) {
+ TLS.get(k).get().depth = 0;
}
- map.put(k, depth);
- return depth;
+
+ private static final class Depth {
+ private int depth;
+
+ private Depth() {
+ this.depth = 0;
}
- public static void reset(final Object k) {
- TLS.get().remove(k);
+ private int increment() {
+ return this.depth++;
+ }
+ }
+
+ private static final class ThreadLocalDepth extends ThreadLocal<Depth> {
+ @Override
+ protected Depth initialValue() {
+ return new Depth();
+ }
}
}

MODIFY
agent-bootstrap/src/test/groovy/io/opentelemetry/auto/bootstrap/CallDepthThreadLocalMapTest.groovy
agent-bootstrap/src/test/groovy/io/opentelemetry/auto/bootstrap/CallDepthThreadLocalMapTest.groovy
@@ -21,8 +21,8 @@ class CallDepthThreadLocalMapTest extends Specification {
def "test CallDepthThreadLocalMap"() {
setup:
- def k1 = new Object()
- def k2 = new Object()
+ Class<?> k1 = String
+ Class<?> k2 = Integer
expect:
CallDepthThreadLocalMap.incrementCallDepth(k1) == 0

MODIFY
instrumentation/java-util-logging/src/main/java/io/opentelemetry/auto/instrumentation/jul/JavaUtilLoggingSpansInstrumentation.java
instrumentation/java-util-logging/src/main/java/io/opentelemetry/auto/instrumentation/jul/JavaUtilLoggingSpansInstrumentation.java
@@ -80,7 +80,7 @@ public class JavaUtilLoggingSpansInstrumentation extends Instrumenter.Default {
@Advice.This final Logger logger, @Advice.Argument(0) final LogRecord logRecord) {
// need to track call depth across all loggers in order to avoid double capture when one
// logging framework delegates to another
- final boolean topLevel = CallDepthThreadLocalMap.incrementCallDepth("logger") == 0;
+ final boolean topLevel = CallDepthThreadLocalMap.incrementCallDepth(Logger.class) == 0;
if (topLevel) {
JavaUtilLoggingSpans.capture(logger, logRecord);
}
@@ -90,7 +90,7 @@ public class JavaUtilLoggingSpansInstrumentation extends Instrumenter.Default {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(@Advice.Enter final boolean topLevel) {
if (topLevel) {
- CallDepthThreadLocalMap.reset("logger");
+ CallDepthThreadLocalMap.reset(Logger.class);
}
}
}

MODIFY
instrumentation/log4j/log4j-1.1/src/main/java/io/opentelemetry/auto/instrumentation/log4j/v1_1/Log4jSpansInstrumentation.java
instrumentation/log4j/log4j-1.1/src/main/java/io/opentelemetry/auto/instrumentation/log4j/v1_1/Log4jSpansInstrumentation.java
@@ -75,7 +75,8 @@ public class Log4jSpansInstrumentation extends Instrumenter.Default {
@Advice.Argument(3) final Throwable t) {
// need to track call depth across all loggers to avoid double capture when one logging
// framework delegates to another
- final boolean topLevel = CallDepthThreadLocalMap.incrementCallDepth("logger") == 0;
+ final boolean topLevel =
+ CallDepthThreadLocalMap.incrementCallDepth(java.util.logging.Logger.class) == 0;
if (topLevel) {
Log4jSpans.capture(logger, level, message, t);
}
@@ -85,7 +86,7 @@ public class Log4jSpansInstrumentation extends Instrumenter.Default {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(@Advice.Enter final boolean topLevel) {
if (topLevel) {
- CallDepthThreadLocalMap.reset("logger");
+ CallDepthThreadLocalMap.reset(java.util.logging.Logger.class);
}
}
}

MODIFY
instrumentation/log4j/log4j-2.0/src/main/java/io/opentelemetry/auto/instrumentation/log4j/v2_0/Log4jSpansInstrumentation.java
instrumentation/log4j/log4j-2.0/src/main/java/io/opentelemetry/auto/instrumentation/log4j/v2_0/Log4jSpansInstrumentation.java
@@ -92,7 +92,8 @@ public class Log4jSpansInstrumentation extends Instrumenter.Default {
@Advice.Argument(4) final Throwable t) {
// need to track call depth across all loggers in order to avoid double capture when one
// logging framework delegates to another
- final boolean topLevel = CallDepthThreadLocalMap.incrementCallDepth("logger") == 0;
+ final boolean topLevel =
+ CallDepthThreadLocalMap.incrementCallDepth(java.util.logging.Logger.class) == 0;
if (topLevel) {
Log4jSpans.capture(logger, level, message, t);
}
@@ -102,7 +103,7 @@ public class Log4jSpansInstrumentation extends Instrumenter.Default {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(@Advice.Enter final boolean topLevel) {
if (topLevel) {
- CallDepthThreadLocalMap.reset("logger");
+ CallDepthThreadLocalMap.reset(java.util.logging.Logger.class);
}
}
}
@@ -117,7 +118,8 @@ public class Log4jSpansInstrumentation extends Instrumenter.Default {
@Advice.Argument(5) final Throwable t) {
// need to track call depth across all loggers in order to avoid double capture when one
// logging framework delegates to another
- final boolean topLevel = CallDepthThreadLocalMap.incrementCallDepth("logger") == 0;
+ final boolean topLevel =
+ CallDepthThreadLocalMap.incrementCallDepth(java.util.logging.Logger.class) == 0;
if (topLevel) {
Log4jSpans.capture(logger, level, message, t);
}
@@ -127,7 +129,7 @@ public class Log4jSpansInstrumentation extends Instrumenter.Default {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(@Advice.Enter final boolean topLevel) {
if (topLevel) {
- CallDepthThreadLocalMap.reset("logger");
+ CallDepthThreadLocalMap.reset(java.util.logging.Logger.class);
}
}
}

MODIFY
instrumentation/logback-1.0/src/main/java/io/opentelemetry/auto/instrumentation/logback/LogbackSpansInstrumentation.java
instrumentation/logback-1.0/src/main/java/io/opentelemetry/auto/instrumentation/logback/LogbackSpansInstrumentation.java
@@ -67,7 +67,8 @@ public class LogbackSpansInstrumentation extends Instrumenter.Default {
public static boolean methodEnter(@Advice.Argument(0) final ILoggingEvent event) {
// need to track call depth across all loggers in order to avoid double capture when one
// logging framework delegates to another
- final boolean topLevel = CallDepthThreadLocalMap.incrementCallDepth("logger") == 0;
+ final boolean topLevel =
+ CallDepthThreadLocalMap.incrementCallDepth(java.util.logging.Logger.class) == 0;
if (topLevel) {
LogbackSpans.capture(event);
}
@@ -77,7 +78,7 @@ public class LogbackSpansInstrumentation extends Instrumenter.Default {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(@Advice.Enter final boolean topLevel) {
if (topLevel) {
- CallDepthThreadLocalMap.reset("logger");
+ CallDepthThreadLocalMap.reset(java.util.logging.Logger.class);
}
}
}

Use ClassValue to store ThreadLocal call depth (DataDog/dd-trace-java#1528)