Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
apply from: "$rootDir/gradle/java.gradle"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is also just a copy of the existing instrumentation.

apply plugin: 'idea'

testJvmConstraints {
minJavaVersion = JavaVersion.VERSION_17
}

muzzle {
pass {
group = 'io.github.resilience4j'
module = 'resilience4j-all'
versions = '[2.0.0,)'
assertInverse = true
javaVersion = "17"
}
}

idea {
module {
jdkName = '17'
}
}

project.tasks.withType(AbstractCompile).configureEach {
configureCompiler(
it,
17,
JavaVersion.VERSION_1_8,
"Set all compile tasks to use JDK17 but let instrumentation code target 1.8 compatibility"
)
}

addTestSuiteForDir('latestDepTest', 'test')

dependencies {
compileOnly group: 'io.github.resilience4j', name: 'resilience4j-all', version: '2.0.0'

testImplementation group: 'io.github.resilience4j', name: 'resilience4j-all', version: '2.0.0'
latestDepTestImplementation group: 'io.github.resilience4j', name: 'resilience4j-all', version: '2.+'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package datadog.trace.instrumentation.resilience4j;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.agent.tooling.InstrumenterModule;
import java.util.Arrays;
import java.util.List;

@AutoService(InstrumenterModule.class)
public class Resilience4jComprehensiveModule extends InstrumenterModule.Tracing {

public Resilience4jComprehensiveModule() {
super("resilience4j", "resilience4j-comprehensive");
}

@Override
public String[] helperClassNames() {
return new String[] {
// Common infrastructure
packageName + ".common.WrapperWithContext",
packageName + ".common.WrapperWithContext$CallableWithContext",
packageName + ".common.WrapperWithContext$CheckedRunnableWithContext",
packageName + ".common.WrapperWithContext$RunnableWithContext",
packageName + ".common.WrapperWithContext$CheckedFunctionWithContext",
packageName + ".common.WrapperWithContext$ConsumerWithContext",
packageName + ".common.WrapperWithContext$CheckedSupplierWithContext",
packageName + ".common.WrapperWithContext$CheckedConsumerWithContext",
packageName + ".common.WrapperWithContext$FunctionWithContext",
packageName + ".common.WrapperWithContext$SupplierOfCompletionStageWithContext",
packageName + ".common.WrapperWithContext$SupplierWithContext",
packageName + ".common.WrapperWithContext$SupplierOfFutureWithContext",
packageName + ".common.WrapperWithContext$FinishOnGetFuture",
packageName + ".common.Resilience4jSpanDecorator",
packageName + ".common.Resilience4jSpan",

// Component decorators
packageName + ".circuitbreaker.CircuitBreakerDecorator",
packageName + ".retry.RetryDecorator",
packageName + ".ratelimiter.RateLimiterDecorator",
packageName + ".bulkhead.BulkheadDecorator",
packageName + ".bulkhead.ThreadPoolBulkheadDecorator",
packageName + ".timelimiter.TimeLimiterDecorator",
packageName + ".cache.CacheDecorator",
packageName + ".hedge.HedgeDecorator",
};
}

@Override
public List<Instrumenter> typeInstrumentations() {
return Arrays.asList(
new datadog.trace.instrumentation.resilience4j.circuitbreaker.CircuitBreakerInstrumentation(),
new datadog.trace.instrumentation.resilience4j.retry.RetryInstrumentation(),
new datadog.trace.instrumentation.resilience4j.ratelimiter.RateLimiterInstrumentation(),
new datadog.trace.instrumentation.resilience4j.bulkhead.BulkheadInstrumentation(),
new datadog.trace.instrumentation.resilience4j.bulkhead.ThreadPoolBulkheadInstrumentation(),
new datadog.trace.instrumentation.resilience4j.timelimiter.TimeLimiterInstrumentation(),
new datadog.trace.instrumentation.resilience4j.cache.CacheInstrumentation(),
new datadog.trace.instrumentation.resilience4j.hedge.HedgeInstrumentation(),
new datadog.trace.instrumentation.resilience4j.fallback.FallbackCallableInstrumentation(),
new datadog.trace.instrumentation.resilience4j.fallback.FallbackSupplierInstrumentation(),
new datadog.trace.instrumentation.resilience4j.fallback.FallbackCheckedSupplierInstrumentation(),
new datadog.trace.instrumentation.resilience4j.fallback.FallbackCompletionStageInstrumentation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package datadog.trace.instrumentation.resilience4j.bulkhead;

import datadog.trace.api.Config;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.instrumentation.resilience4j.common.Resilience4jSpanDecorator;
import io.github.resilience4j.bulkhead.Bulkhead;

public final class BulkheadDecorator extends Resilience4jSpanDecorator<Bulkhead> {
public static final BulkheadDecorator DECORATE = new BulkheadDecorator();
public static final String TAG_PREFIX = "resilience4j.bulkhead.";
public static final String TAG_METRICS_PREFIX = TAG_PREFIX + "metrics.";

private BulkheadDecorator() {
super();
}

@Override
public void decorate(AgentSpan span, Bulkhead data) {
span.setTag(TAG_PREFIX + "name", data.getName());
span.setTag(TAG_PREFIX + "type", "semaphore");
if (Config.get().isResilience4jTagMetricsEnabled()) {
Bulkhead.Metrics metrics = data.getMetrics();
span.setTag(TAG_METRICS_PREFIX + "available_concurrent_calls", metrics.getAvailableConcurrentCalls());
span.setTag(TAG_METRICS_PREFIX + "max_allowed_concurrent_calls", metrics.getMaxAllowedConcurrentCalls());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
package datadog.trace.instrumentation.resilience4j.bulkhead;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.instrumentation.resilience4j.common.WrapperWithContext;
import io.github.resilience4j.bulkhead.Bulkhead;
import io.github.resilience4j.core.functions.CheckedConsumer;
import io.github.resilience4j.core.functions.CheckedFunction;
import io.github.resilience4j.core.functions.CheckedRunnable;
import io.github.resilience4j.core.functions.CheckedSupplier;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import net.bytebuddy.asm.Advice;

public final class BulkheadInstrumentation
implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice {
private static final String BULKHEAD_FQCN = "io.github.resilience4j.bulkhead.Bulkhead";
private static final String THIS_CLASS = BulkheadInstrumentation.class.getName();

@Override
public String instrumentedType() {
return BULKHEAD_FQCN;
}

@Override
public void methodAdvice(MethodTransformer transformer) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing instrumentation for the other nine decorate* methods, which must also be instrumented for comprehensive coverage.

transformer.applyAdvice(
isMethod()
.and(isStatic())
.and(named("decorateSupplier"))
.and(takesArgument(0, named(BULKHEAD_FQCN)))
.and(returns(named(Supplier.class.getName()))),
THIS_CLASS + "$SupplierAdvice");

transformer.applyAdvice(
isMethod()
.and(isStatic())
.and(named("decorateCallable"))
.and(takesArgument(0, named(BULKHEAD_FQCN)))
.and(returns(named(Callable.class.getName()))),
THIS_CLASS + "$CallableAdvice");

transformer.applyAdvice(
isMethod()
.and(isStatic())
.and(named("decorateRunnable"))
.and(takesArgument(0, named(BULKHEAD_FQCN)))
.and(returns(named(Runnable.class.getName()))),
THIS_CLASS + "$RunnableAdvice");

transformer.applyAdvice(
isMethod()
.and(isStatic())
.and(named("decorateFunction"))
.and(takesArgument(0, named(BULKHEAD_FQCN)))
.and(returns(named(Function.class.getName()))),
THIS_CLASS + "$FunctionAdvice");

transformer.applyAdvice(
isMethod()
.and(isStatic())
.and(named("decorateConsumer"))
.and(takesArgument(0, named(BULKHEAD_FQCN)))
.and(returns(named(Consumer.class.getName()))),
THIS_CLASS + "$ConsumerAdvice");

transformer.applyAdvice(
isMethod()
.and(isStatic())
.and(named("decorateCheckedSupplier"))
.and(takesArgument(0, named(BULKHEAD_FQCN)))
.and(returns(named("io.github.resilience4j.core.functions.CheckedSupplier"))),
THIS_CLASS + "$CheckedSupplierAdvice");

transformer.applyAdvice(
isMethod()
.and(isStatic())
.and(named("decorateCheckedRunnable"))
.and(takesArgument(0, named(BULKHEAD_FQCN)))
.and(returns(named("io.github.resilience4j.core.functions.CheckedRunnable"))),
THIS_CLASS + "$CheckedRunnableAdvice");

transformer.applyAdvice(
isMethod()
.and(isStatic())
.and(named("decorateCheckedFunction"))
.and(takesArgument(0, named(BULKHEAD_FQCN)))
.and(returns(named("io.github.resilience4j.core.functions.CheckedFunction"))),
THIS_CLASS + "$CheckedFunctionAdvice");

transformer.applyAdvice(
isMethod()
.and(isStatic())
.and(named("decorateCheckedConsumer"))
.and(takesArgument(0, named(BULKHEAD_FQCN)))
.and(returns(named("io.github.resilience4j.core.functions.CheckedConsumer"))),
THIS_CLASS + "$CheckedConsumerAdvice");

transformer.applyAdvice(
isMethod()
.and(isStatic())
.and(named("decorateCompletionStage"))
.and(takesArgument(0, named(BULKHEAD_FQCN)))
.and(returns(named(Supplier.class.getName()))),
THIS_CLASS + "$CompletionStageAdvice");

transformer.applyAdvice(
isMethod()
.and(isStatic())
.and(named("decorateFuture"))
.and(takesArgument(0, named(BULKHEAD_FQCN)))
.and(returns(named(Supplier.class.getName()))),
THIS_CLASS + "$FutureAdvice");
}

public static class SupplierAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void afterExecute(
@Advice.Argument(value = 0) Bulkhead bulkhead,
@Advice.Return(readOnly = false) Supplier<?> result) {
result = new WrapperWithContext.SupplierWithContext<>(
result, BulkheadDecorator.DECORATE, bulkhead);
}
}

public static class CallableAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void afterExecute(
@Advice.Argument(value = 0) Bulkhead bulkhead,
@Advice.Return(readOnly = false) Callable<?> result) {
result = new WrapperWithContext.CallableWithContext<>(
result, BulkheadDecorator.DECORATE, bulkhead);
}
}

public static class RunnableAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void afterExecute(
@Advice.Argument(value = 0) Bulkhead bulkhead,
@Advice.Return(readOnly = false) Runnable result) {
result = new WrapperWithContext.RunnableWithContext<>(
result, BulkheadDecorator.DECORATE, bulkhead);
}
}

public static class FunctionAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void afterExecute(
@Advice.Argument(value = 0) Bulkhead bulkhead,
@Advice.Return(readOnly = false) Function<?, ?> result) {
result = new WrapperWithContext.FunctionWithContext<>(
result, BulkheadDecorator.DECORATE, bulkhead);
}
}

public static class ConsumerAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void afterExecute(
@Advice.Argument(value = 0) Bulkhead bulkhead,
@Advice.Return(readOnly = false) Consumer<?> result) {
result = new WrapperWithContext.ConsumerWithContext<>(
result, BulkheadDecorator.DECORATE, bulkhead);
}
}

public static class CheckedSupplierAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void afterExecute(
@Advice.Argument(value = 0) Bulkhead bulkhead,
@Advice.Return(readOnly = false) CheckedSupplier<?> result) {
result = new WrapperWithContext.CheckedSupplierWithContext<>(
result, BulkheadDecorator.DECORATE, bulkhead);
}
}

public static class CheckedRunnableAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void afterExecute(
@Advice.Argument(value = 0) Bulkhead bulkhead,
@Advice.Return(readOnly = false) CheckedRunnable result) {
result = new WrapperWithContext.CheckedRunnableWithContext<>(
result, BulkheadDecorator.DECORATE, bulkhead);
}
}

public static class CheckedFunctionAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void afterExecute(
@Advice.Argument(value = 0) Bulkhead bulkhead,
@Advice.Return(readOnly = false) CheckedFunction<?, ?> result) {
result = new WrapperWithContext.CheckedFunctionWithContext<>(
result, BulkheadDecorator.DECORATE, bulkhead);
}
}

public static class CheckedConsumerAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void afterExecute(
@Advice.Argument(value = 0) Bulkhead bulkhead,
@Advice.Return(readOnly = false) CheckedConsumer<?> result) {
result = new WrapperWithContext.CheckedConsumerWithContext<>(
result, BulkheadDecorator.DECORATE, bulkhead);
}
}

public static class CompletionStageAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void afterExecute(
@Advice.Argument(value = 0) Bulkhead bulkhead,
@Advice.Return(readOnly = false) Supplier<CompletionStage<?>> result) {
result = new WrapperWithContext.SupplierOfCompletionStageWithContext<>(
result, BulkheadDecorator.DECORATE, bulkhead);
}
}

public static class FutureAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void afterExecute(
@Advice.Argument(value = 0) Bulkhead bulkhead,
@Advice.Return(readOnly = false) Supplier<Future<?>> result) {
result = new WrapperWithContext.SupplierOfFutureWithContext<>(
result, BulkheadDecorator.DECORATE, bulkhead);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package datadog.trace.instrumentation.resilience4j.bulkhead;

import datadog.trace.api.Config;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.instrumentation.resilience4j.common.Resilience4jSpanDecorator;
import io.github.resilience4j.bulkhead.ThreadPoolBulkhead;

public final class ThreadPoolBulkheadDecorator extends Resilience4jSpanDecorator<ThreadPoolBulkhead> {
public static final ThreadPoolBulkheadDecorator DECORATE = new ThreadPoolBulkheadDecorator();
public static final String TAG_PREFIX = "resilience4j.bulkhead.";
public static final String TAG_METRICS_PREFIX = TAG_PREFIX + "metrics.";

private ThreadPoolBulkheadDecorator() {
super();
}

@Override
public void decorate(AgentSpan span, ThreadPoolBulkhead data) {
span.setTag(TAG_PREFIX + "name", data.getName());
span.setTag(TAG_PREFIX + "type", "threadpool");
if (Config.get().isResilience4jTagMetricsEnabled()) {
ThreadPoolBulkhead.Metrics metrics = data.getMetrics();
span.setTag(TAG_METRICS_PREFIX + "queue_depth", metrics.getQueueDepth());
span.setTag(TAG_METRICS_PREFIX + "queue_capacity", metrics.getQueueCapacity());
span.setTag(TAG_METRICS_PREFIX + "thread_pool_size", metrics.getThreadPoolSize());
span.setTag(TAG_METRICS_PREFIX + "remaining_queue_capacity", metrics.getRemainingQueueCapacity());
}
}
}
Loading
Loading