-
Notifications
You must be signed in to change notification settings - Fork 1k
Failsafe 3.0 instrumentation introduced #14057
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
94b0a7e
Failsafe 3.0 instrumentation introduced
onurkybsi b9655de
generateFossaConfiguration executed
onurkybsi a310090
Dependency issue fixed
onurkybsi cbe73b7
Dependency issue fixed
onurkybsi 91f3261
PR comments addressed
onurkybsi d769091
Missing column added in supported-libraries.md
onurkybsi 1ec5a4f
A minor fix in supported-libraries.md
onurkybsi 98d19a4
link to central.sonatype.com
laurit dfbe644
remove unneeded dependency
laurit 89f0cfb
rename instruments, rework tests
laurit cb0d030
use constant attribute key
laurit 7a529e7
restore removed blank line
laurit af6013f
PR comments addressed
onurkybsi 2d60508
review
laurit 0e07641
Merge branch 'main' into issue-10856
laurit 6d52ab3
add unit
laurit File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# Library Instrumentation for Failsafe version 3.0.1 and higher | ||
|
||
Provides OpenTelemetry instrumentation for [Failsafe](https://failsafe.dev/). | ||
|
||
## Quickstart | ||
|
||
### Add these dependencies to your project | ||
|
||
Replace `OPENTELEMETRY_VERSION` with the [latest release](https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-failsafe-3.0). | ||
|
||
For Maven, add to your `pom.xml` dependencies: | ||
|
||
```xml | ||
<dependencies> | ||
<dependency> | ||
<groupId>io.opentelemetry.instrumentation</groupId> | ||
<artifactId>opentelemetry-failsafe-3.0</artifactId> | ||
<version>OPENTELEMETRY_VERSION</version> | ||
</dependency> | ||
</dependencies> | ||
``` | ||
|
||
For Gradle, add to your dependencies: | ||
|
||
```groovy | ||
implementation("io.opentelemetry.instrumentation:opentelemetry-failsafe-3.0:OPENTELEMETRY_VERSION") | ||
``` | ||
|
||
### Usage | ||
|
||
The instrumentation library allows creating instrumented `CircuitBreaker` instances for collecting | ||
OpenTelemetry-based metrics. | ||
|
||
```java | ||
<R> CircuitBreaker<R> configure(OpenTelemetry openTelemetry, CircuitBreaker<R> circuitBreaker) { | ||
FailsafeTelemetry failsafeTelemetry = FailsafeTelemetry.create(openTelemetry); | ||
return failsafeTelemetry.createCircuitBreaker(circuitBreaker, "my-circuit-breaker"); | ||
} | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
plugins { | ||
id("otel.library-instrumentation") | ||
} | ||
|
||
dependencies { | ||
library("dev.failsafe:failsafe:3.0.1") | ||
onurkybsi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} |
74 changes: 74 additions & 0 deletions
74
...a/io/opentelemetry/instrumentation/failsafe/v3_0/CircuitBreakerEventListenerBuilders.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.instrumentation.failsafe.v3_0; | ||
|
||
import static io.opentelemetry.api.common.AttributeKey.stringKey; | ||
|
||
import dev.failsafe.CircuitBreakerConfig; | ||
import dev.failsafe.event.CircuitBreakerStateChangedEvent; | ||
import dev.failsafe.event.EventListener; | ||
import dev.failsafe.event.ExecutionCompletedEvent; | ||
import io.opentelemetry.api.common.AttributeKey; | ||
import io.opentelemetry.api.common.Attributes; | ||
import io.opentelemetry.api.metrics.LongCounter; | ||
|
||
final class CircuitBreakerEventListenerBuilders { | ||
private static final AttributeKey<String> OUTCOME_KEY = | ||
stringKey("failsafe.circuit_breaker.outcome"); | ||
private static final AttributeKey<String> STATE_KEY = stringKey("failsafe.circuit_breaker.state"); | ||
|
||
private CircuitBreakerEventListenerBuilders() {} | ||
|
||
static <R> EventListener<ExecutionCompletedEvent<R>> buildInstrumentedFailureListener( | ||
CircuitBreakerConfig<R> userConfig, | ||
LongCounter executionCounter, | ||
Attributes commonAttributes) { | ||
Attributes attributes = commonAttributes.toBuilder().put(OUTCOME_KEY, "failure").build(); | ||
return count(executionCounter, attributes, userConfig.getFailureListener()); | ||
} | ||
|
||
static <R> EventListener<ExecutionCompletedEvent<R>> buildInstrumentedSuccessListener( | ||
CircuitBreakerConfig<R> userConfig, | ||
LongCounter executionCounter, | ||
Attributes commonAttributes) { | ||
Attributes attributes = commonAttributes.toBuilder().put(OUTCOME_KEY, "success").build(); | ||
return count(executionCounter, attributes, userConfig.getSuccessListener()); | ||
} | ||
|
||
static <R> EventListener<CircuitBreakerStateChangedEvent> buildInstrumentedOpenListener( | ||
CircuitBreakerConfig<R> userConfig, | ||
LongCounter stateChangesCounter, | ||
Attributes commonAttributes) { | ||
Attributes attributes = commonAttributes.toBuilder().put(STATE_KEY, "open").build(); | ||
return count(stateChangesCounter, attributes, userConfig.getOpenListener()); | ||
} | ||
|
||
static <R> EventListener<CircuitBreakerStateChangedEvent> buildInstrumentedHalfOpenListener( | ||
CircuitBreakerConfig<R> userConfig, | ||
LongCounter stateChangesCounter, | ||
Attributes commonAttributes) { | ||
Attributes attributes = commonAttributes.toBuilder().put(STATE_KEY, "half_open").build(); | ||
return count(stateChangesCounter, attributes, userConfig.getHalfOpenListener()); | ||
} | ||
|
||
static <R> EventListener<CircuitBreakerStateChangedEvent> buildInstrumentedCloseListener( | ||
CircuitBreakerConfig<R> userConfig, | ||
LongCounter stateChangesCounter, | ||
Attributes commonAttributes) { | ||
Attributes attributes = commonAttributes.toBuilder().put(STATE_KEY, "closed").build(); | ||
return count(stateChangesCounter, attributes, userConfig.getCloseListener()); | ||
} | ||
|
||
private static <T> EventListener<T> count( | ||
LongCounter counter, Attributes attributes, EventListener<T> delegate) { | ||
return e -> { | ||
counter.add(1, attributes); | ||
if (delegate != null) { | ||
delegate.accept(e); | ||
} | ||
}; | ||
} | ||
} |
73 changes: 73 additions & 0 deletions
73
...brary/src/main/java/io/opentelemetry/instrumentation/failsafe/v3_0/FailsafeTelemetry.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.instrumentation.failsafe.v3_0; | ||
|
||
import static io.opentelemetry.instrumentation.failsafe.v3_0.CircuitBreakerEventListenerBuilders.buildInstrumentedCloseListener; | ||
import static io.opentelemetry.instrumentation.failsafe.v3_0.CircuitBreakerEventListenerBuilders.buildInstrumentedFailureListener; | ||
import static io.opentelemetry.instrumentation.failsafe.v3_0.CircuitBreakerEventListenerBuilders.buildInstrumentedHalfOpenListener; | ||
import static io.opentelemetry.instrumentation.failsafe.v3_0.CircuitBreakerEventListenerBuilders.buildInstrumentedOpenListener; | ||
import static io.opentelemetry.instrumentation.failsafe.v3_0.CircuitBreakerEventListenerBuilders.buildInstrumentedSuccessListener; | ||
|
||
import dev.failsafe.CircuitBreaker; | ||
import dev.failsafe.CircuitBreakerConfig; | ||
import io.opentelemetry.api.OpenTelemetry; | ||
import io.opentelemetry.api.common.AttributeKey; | ||
import io.opentelemetry.api.common.Attributes; | ||
import io.opentelemetry.api.metrics.LongCounter; | ||
import io.opentelemetry.api.metrics.Meter; | ||
|
||
/** Entrypoint for instrumenting Failsafe components. */ | ||
public final class FailsafeTelemetry { | ||
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.failsafe-3.0"; | ||
|
||
private static final AttributeKey<String> CIRCUIT_BREAKER_NAME = | ||
AttributeKey.stringKey("failsafe.circuit_breaker.name"); | ||
|
||
/** Returns a new {@link FailsafeTelemetry} configured with the given {@link OpenTelemetry}. */ | ||
public static FailsafeTelemetry create(OpenTelemetry openTelemetry) { | ||
return new FailsafeTelemetry(openTelemetry); | ||
} | ||
|
||
private final OpenTelemetry openTelemetry; | ||
|
||
private FailsafeTelemetry(OpenTelemetry openTelemetry) { | ||
this.openTelemetry = openTelemetry; | ||
} | ||
|
||
/** | ||
* Returns an instrumented {@link CircuitBreaker} by given values. | ||
* | ||
* @param delegate user configured {@link CircuitBreaker} to be instrumented | ||
* @param circuitBreakerName identifier of given {@link CircuitBreaker} | ||
* @param <R> {@link CircuitBreaker}'s result type | ||
* @return instrumented {@link CircuitBreaker} | ||
*/ | ||
public <R> CircuitBreaker<R> createCircuitBreaker( | ||
CircuitBreaker<R> delegate, String circuitBreakerName) { | ||
CircuitBreakerConfig<R> userConfig = delegate.getConfig(); | ||
Meter meter = openTelemetry.getMeter(INSTRUMENTATION_NAME); | ||
LongCounter executionCounter = | ||
meter | ||
.counterBuilder("failsafe.circuit_breaker.execution.count") | ||
.setDescription("Count of circuit breaker executions.") | ||
laurit marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.setUnit("{execution}") | ||
.build(); | ||
LongCounter stateChangesCounter = | ||
meter | ||
.counterBuilder("failsafe.circuit_breaker.state_changes.count") | ||
.setDescription("Count of circuit breaker state changes.") | ||
.setUnit("{execution}") | ||
.build(); | ||
Attributes attributes = Attributes.of(CIRCUIT_BREAKER_NAME, circuitBreakerName); | ||
return CircuitBreaker.builder(userConfig) | ||
.onFailure(buildInstrumentedFailureListener(userConfig, executionCounter, attributes)) | ||
.onSuccess(buildInstrumentedSuccessListener(userConfig, executionCounter, attributes)) | ||
.onOpen(buildInstrumentedOpenListener(userConfig, stateChangesCounter, attributes)) | ||
.onHalfOpen(buildInstrumentedHalfOpenListener(userConfig, stateChangesCounter, attributes)) | ||
.onClose(buildInstrumentedCloseListener(userConfig, stateChangesCounter, attributes)) | ||
.build(); | ||
} | ||
} |
97 changes: 97 additions & 0 deletions
97
...y/src/test/java/io/opentelemetry/instrumentation/failsafe/v3_0/FailsafeTelemetryTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.instrumentation.failsafe.v3_0; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
||
import dev.failsafe.CircuitBreaker; | ||
import dev.failsafe.CircuitBreakerOpenException; | ||
import dev.failsafe.Failsafe; | ||
import io.opentelemetry.api.common.Attributes; | ||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; | ||
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; | ||
import io.opentelemetry.sdk.testing.assertj.LongPointAssert; | ||
import java.time.Duration; | ||
import java.util.Objects; | ||
import java.util.function.Consumer; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
|
||
final class FailsafeTelemetryTest { | ||
@RegisterExtension | ||
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); | ||
|
||
@Test | ||
void captureCircuitBreakerMetrics() { | ||
// given | ||
CircuitBreaker<Object> userCircuitBreaker = | ||
dev.failsafe.CircuitBreaker.builder() | ||
.handleResultIf(Objects::isNull) | ||
.withFailureThreshold(2) | ||
.withDelay(Duration.ZERO) | ||
.withSuccessThreshold(2) | ||
.build(); | ||
FailsafeTelemetry failsafeTelemetry = FailsafeTelemetry.create(testing.getOpenTelemetry()); | ||
CircuitBreaker<Object> instrumentedCircuitBreaker = | ||
failsafeTelemetry.createCircuitBreaker(userCircuitBreaker, "testing"); | ||
|
||
// when | ||
for (int i = 0; i < 5; i++) { | ||
try { | ||
int temp = i; | ||
Failsafe.with(instrumentedCircuitBreaker).get(() -> temp < 2 ? null : new Object()); | ||
} catch (CircuitBreakerOpenException e) { | ||
assertThat(i).isEqualTo(2); | ||
} | ||
} | ||
|
||
// then | ||
testing.waitAndAssertMetrics( | ||
"io.opentelemetry.failsafe-3.0", | ||
metricAssert -> | ||
metricAssert | ||
.hasName("failsafe.circuit_breaker.execution.count") | ||
.hasLongSumSatisfying( | ||
sum -> | ||
sum.isMonotonic() | ||
.hasPointsSatisfying( | ||
buildCircuitBreakerAssertion( | ||
2, "failsafe.circuit_breaker.outcome", "failure"), | ||
buildCircuitBreakerAssertion( | ||
3, "failsafe.circuit_breaker.outcome", "success")))); | ||
testing.waitAndAssertMetrics( | ||
"io.opentelemetry.failsafe-3.0", | ||
metricAssert -> | ||
metricAssert | ||
.hasName("failsafe.circuit_breaker.state_changes.count") | ||
.hasLongSumSatisfying( | ||
sum -> | ||
sum.isMonotonic() | ||
.hasPointsSatisfying( | ||
buildCircuitBreakerAssertion( | ||
1, "failsafe.circuit_breaker.state", "open"), | ||
buildCircuitBreakerAssertion( | ||
1, "failsafe.circuit_breaker.state", "half_open"), | ||
buildCircuitBreakerAssertion( | ||
1, "failsafe.circuit_breaker.state", "closed")))); | ||
} | ||
|
||
private static Consumer<LongPointAssert> buildCircuitBreakerAssertion( | ||
long expectedValue, String expectedAttributeKey, String expectedAttributeValue) { | ||
return longSumAssert -> | ||
longSumAssert | ||
.hasValue(expectedValue) | ||
.hasAttributesSatisfying( | ||
attributes -> | ||
assertEquals( | ||
Attributes.builder() | ||
.put("failsafe.circuit_breaker.name", "testing") | ||
.put(expectedAttributeKey, expectedAttributeValue) | ||
.build(), | ||
attributes)); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.