Skip to content

Commit 2b966ec

Browse files
committed
Merge branch 'main' into grpc-req-res-size
2 parents 4d63ab1 + 1b2ae93 commit 2b966ec

File tree

17 files changed

+341
-20
lines changed

17 files changed

+341
-20
lines changed

.fossa.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ targets:
100100
- type: gradle
101101
path: ./
102102
target: ':instrumentation:external-annotations:javaagent'
103+
- type: gradle
104+
path: ./
105+
target: ':instrumentation:failsafe-3.0:library'
103106
- type: gradle
104107
path: ./
105108
target: ':instrumentation:finagle-http-23.11:javaagent'

conventions/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ dependencies {
5555

5656
// When updating, update above in plugins too
5757
implementation("com.diffplug.spotless:spotless-plugin-gradle:7.2.1")
58-
implementation("com.google.guava:guava:33.4.8-jre")
58+
implementation("com.google.guava:guava:33.5.0-jre")
5959
implementation("com.gradleup.shadow:shadow-gradle-plugin:9.1.0")
6060
implementation("org.apache.httpcomponents:httpclient:4.5.14")
6161
implementation("com.gradle.develocity:com.gradle.develocity.gradle.plugin:4.2")

dependencyManagement/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ val DEPENDENCY_BOMS = listOf(
2828
// even if they are only used by test dependencies, so not using junit bom since it is LGPL
2929

3030
"com.fasterxml.jackson:jackson-bom:2.20.0",
31-
"com.google.guava:guava-bom:33.4.8-jre",
31+
"com.google.guava:guava-bom:33.5.0-jre",
3232
"org.apache.groovy:groovy-bom:${groovyVersion}",
3333
"io.opentelemetry:opentelemetry-bom:${otelSdkVersion}",
3434
"io.opentelemetry:opentelemetry-bom-alpha:${otelSdkAlphaVersion}",

docs/supported-libraries.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ These are the supported libraries and frameworks:
6666
| [Elasticsearch API Client](https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/index.html) | 7.16 - 7.17.19,<br>8.0 - 8.9.+ [4] | N/A | [Elasticsearch Client Spans] |
6767
| [Elasticsearch REST Client](https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/index.html) | 5.0+ | N/A | [Database Client Spans], [Database Client Metrics]&nbsp;[6] |
6868
| [Elasticsearch Transport Client](https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/index.html) | 5.0+ | N/A | [Database Client Spans], [Database Client Metrics]&nbsp;[6] |
69+
| [Failsafe](https://failsafe.dev/) | N/A | [opentelemetry-failsafe-3.0](../instrumentation/failsafe-3.0/library) | none |
6970
| [Finagle](https://github.com/twitter/finagle) | 23.11+ | N/A | none |
7071
| [Finatra](https://github.com/twitter/finatra) | 2.9+ | N/A | Provides `http.route` [2], Controller Spans [3] |
7172
| [Geode Client](https://geode.apache.org/) | 1.4+ | N/A | [Database Client Spans], [Database Client Metrics]&nbsp;[6] |

gradle-plugins/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ val byteBuddyVersion = "1.17.7"
2929
val aetherVersion = "1.1.0"
3030

3131
dependencies {
32-
implementation("com.google.guava:guava:33.4.8-jre")
32+
implementation("com.google.guava:guava:33.5.0-jre")
3333
// we need to use byte buddy variant that does not shade asm
3434
implementation("net.bytebuddy:byte-buddy-gradle-plugin:${byteBuddyVersion}") {
3535
exclude(group = "net.bytebuddy", module = "byte-buddy")

instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ public final class InstrumenterBuilder<REQUEST, RESPONSE> {
7676
boolean propagateOperationListenersToOnEnd = false;
7777
boolean enabled = true;
7878

79-
{
79+
static {
8080
Experimental.internalAddOperationListenerAttributesExtractor(
8181
(builder, operationListenerAttributesExtractor) ->
82-
this.operationListenerAttributesExtractors.add(
82+
builder.operationListenerAttributesExtractors.add(
8383
requireNonNull(
8484
operationListenerAttributesExtractor, "operationListenerAttributesExtractor")));
8585
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Library Instrumentation for Failsafe version 3.0.1 and higher
2+
3+
Provides OpenTelemetry instrumentation for [Failsafe](https://failsafe.dev/).
4+
5+
## Quickstart
6+
7+
### Add these dependencies to your project
8+
9+
Replace `OPENTELEMETRY_VERSION` with the [latest release](https://central.sonatype.com/artifact/io.opentelemetry.instrumentation/opentelemetry-failsafe-3.0).
10+
11+
For Maven, add to your `pom.xml` dependencies:
12+
13+
```xml
14+
<dependencies>
15+
<dependency>
16+
<groupId>io.opentelemetry.instrumentation</groupId>
17+
<artifactId>opentelemetry-failsafe-3.0</artifactId>
18+
<version>OPENTELEMETRY_VERSION</version>
19+
</dependency>
20+
</dependencies>
21+
```
22+
23+
For Gradle, add to your dependencies:
24+
25+
```groovy
26+
implementation("io.opentelemetry.instrumentation:opentelemetry-failsafe-3.0:OPENTELEMETRY_VERSION")
27+
```
28+
29+
### Usage
30+
31+
The instrumentation library allows creating instrumented `CircuitBreaker` instances for collecting
32+
OpenTelemetry-based metrics.
33+
34+
```java
35+
<R> CircuitBreaker<R> configure(OpenTelemetry openTelemetry, CircuitBreaker<R> circuitBreaker) {
36+
FailsafeTelemetry failsafeTelemetry = FailsafeTelemetry.create(openTelemetry);
37+
return failsafeTelemetry.createCircuitBreaker(circuitBreaker, "my-circuit-breaker");
38+
}
39+
```
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
plugins {
2+
id("otel.library-instrumentation")
3+
}
4+
5+
dependencies {
6+
library("dev.failsafe:failsafe:3.0.1")
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.failsafe.v3_0;
7+
8+
import static io.opentelemetry.api.common.AttributeKey.stringKey;
9+
10+
import dev.failsafe.CircuitBreakerConfig;
11+
import dev.failsafe.event.CircuitBreakerStateChangedEvent;
12+
import dev.failsafe.event.EventListener;
13+
import dev.failsafe.event.ExecutionCompletedEvent;
14+
import io.opentelemetry.api.common.AttributeKey;
15+
import io.opentelemetry.api.common.Attributes;
16+
import io.opentelemetry.api.metrics.LongCounter;
17+
18+
final class CircuitBreakerEventListenerBuilders {
19+
private static final AttributeKey<String> OUTCOME_KEY =
20+
stringKey("failsafe.circuit_breaker.outcome");
21+
private static final AttributeKey<String> STATE_KEY = stringKey("failsafe.circuit_breaker.state");
22+
23+
private CircuitBreakerEventListenerBuilders() {}
24+
25+
static <R> EventListener<ExecutionCompletedEvent<R>> buildInstrumentedFailureListener(
26+
CircuitBreakerConfig<R> userConfig,
27+
LongCounter executionCounter,
28+
Attributes commonAttributes) {
29+
Attributes attributes = commonAttributes.toBuilder().put(OUTCOME_KEY, "failure").build();
30+
return count(executionCounter, attributes, userConfig.getFailureListener());
31+
}
32+
33+
static <R> EventListener<ExecutionCompletedEvent<R>> buildInstrumentedSuccessListener(
34+
CircuitBreakerConfig<R> userConfig,
35+
LongCounter executionCounter,
36+
Attributes commonAttributes) {
37+
Attributes attributes = commonAttributes.toBuilder().put(OUTCOME_KEY, "success").build();
38+
return count(executionCounter, attributes, userConfig.getSuccessListener());
39+
}
40+
41+
static <R> EventListener<CircuitBreakerStateChangedEvent> buildInstrumentedOpenListener(
42+
CircuitBreakerConfig<R> userConfig,
43+
LongCounter stateChangesCounter,
44+
Attributes commonAttributes) {
45+
Attributes attributes = commonAttributes.toBuilder().put(STATE_KEY, "open").build();
46+
return count(stateChangesCounter, attributes, userConfig.getOpenListener());
47+
}
48+
49+
static <R> EventListener<CircuitBreakerStateChangedEvent> buildInstrumentedHalfOpenListener(
50+
CircuitBreakerConfig<R> userConfig,
51+
LongCounter stateChangesCounter,
52+
Attributes commonAttributes) {
53+
Attributes attributes = commonAttributes.toBuilder().put(STATE_KEY, "half_open").build();
54+
return count(stateChangesCounter, attributes, userConfig.getHalfOpenListener());
55+
}
56+
57+
static <R> EventListener<CircuitBreakerStateChangedEvent> buildInstrumentedCloseListener(
58+
CircuitBreakerConfig<R> userConfig,
59+
LongCounter stateChangesCounter,
60+
Attributes commonAttributes) {
61+
Attributes attributes = commonAttributes.toBuilder().put(STATE_KEY, "closed").build();
62+
return count(stateChangesCounter, attributes, userConfig.getCloseListener());
63+
}
64+
65+
private static <T> EventListener<T> count(
66+
LongCounter counter, Attributes attributes, EventListener<T> delegate) {
67+
return e -> {
68+
counter.add(1, attributes);
69+
if (delegate != null) {
70+
delegate.accept(e);
71+
}
72+
};
73+
}
74+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.failsafe.v3_0;
7+
8+
import static io.opentelemetry.instrumentation.failsafe.v3_0.CircuitBreakerEventListenerBuilders.buildInstrumentedCloseListener;
9+
import static io.opentelemetry.instrumentation.failsafe.v3_0.CircuitBreakerEventListenerBuilders.buildInstrumentedFailureListener;
10+
import static io.opentelemetry.instrumentation.failsafe.v3_0.CircuitBreakerEventListenerBuilders.buildInstrumentedHalfOpenListener;
11+
import static io.opentelemetry.instrumentation.failsafe.v3_0.CircuitBreakerEventListenerBuilders.buildInstrumentedOpenListener;
12+
import static io.opentelemetry.instrumentation.failsafe.v3_0.CircuitBreakerEventListenerBuilders.buildInstrumentedSuccessListener;
13+
14+
import dev.failsafe.CircuitBreaker;
15+
import dev.failsafe.CircuitBreakerConfig;
16+
import io.opentelemetry.api.OpenTelemetry;
17+
import io.opentelemetry.api.common.AttributeKey;
18+
import io.opentelemetry.api.common.Attributes;
19+
import io.opentelemetry.api.metrics.LongCounter;
20+
import io.opentelemetry.api.metrics.Meter;
21+
22+
/** Entrypoint for instrumenting Failsafe components. */
23+
public final class FailsafeTelemetry {
24+
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.failsafe-3.0";
25+
26+
private static final AttributeKey<String> CIRCUIT_BREAKER_NAME =
27+
AttributeKey.stringKey("failsafe.circuit_breaker.name");
28+
29+
/** Returns a new {@link FailsafeTelemetry} configured with the given {@link OpenTelemetry}. */
30+
public static FailsafeTelemetry create(OpenTelemetry openTelemetry) {
31+
return new FailsafeTelemetry(openTelemetry);
32+
}
33+
34+
private final OpenTelemetry openTelemetry;
35+
36+
private FailsafeTelemetry(OpenTelemetry openTelemetry) {
37+
this.openTelemetry = openTelemetry;
38+
}
39+
40+
/**
41+
* Returns an instrumented {@link CircuitBreaker} by given values.
42+
*
43+
* @param delegate user configured {@link CircuitBreaker} to be instrumented
44+
* @param circuitBreakerName identifier of given {@link CircuitBreaker}
45+
* @param <R> {@link CircuitBreaker}'s result type
46+
* @return instrumented {@link CircuitBreaker}
47+
*/
48+
public <R> CircuitBreaker<R> createCircuitBreaker(
49+
CircuitBreaker<R> delegate, String circuitBreakerName) {
50+
CircuitBreakerConfig<R> userConfig = delegate.getConfig();
51+
Meter meter = openTelemetry.getMeter(INSTRUMENTATION_NAME);
52+
LongCounter executionCounter =
53+
meter
54+
.counterBuilder("failsafe.circuit_breaker.execution.count")
55+
.setDescription("Count of circuit breaker executions.")
56+
.setUnit("{execution}")
57+
.build();
58+
LongCounter stateChangesCounter =
59+
meter
60+
.counterBuilder("failsafe.circuit_breaker.state_changes.count")
61+
.setDescription("Count of circuit breaker state changes.")
62+
.setUnit("{execution}")
63+
.build();
64+
Attributes attributes = Attributes.of(CIRCUIT_BREAKER_NAME, circuitBreakerName);
65+
return CircuitBreaker.builder(userConfig)
66+
.onFailure(buildInstrumentedFailureListener(userConfig, executionCounter, attributes))
67+
.onSuccess(buildInstrumentedSuccessListener(userConfig, executionCounter, attributes))
68+
.onOpen(buildInstrumentedOpenListener(userConfig, stateChangesCounter, attributes))
69+
.onHalfOpen(buildInstrumentedHalfOpenListener(userConfig, stateChangesCounter, attributes))
70+
.onClose(buildInstrumentedCloseListener(userConfig, stateChangesCounter, attributes))
71+
.build();
72+
}
73+
}

0 commit comments

Comments
 (0)