Skip to content

Commit 92fe20c

Browse files
committed
Merge remote-tracking branch 'origin/main' into observability/tracing-attr-error-type-transfer
2 parents a32da4b + 320f92d commit 92fe20c

File tree

9 files changed

+360
-36
lines changed

9 files changed

+360
-36
lines changed

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/GoldenSignalsMetricsTracer.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,14 @@ class GoldenSignalsMetricsTracer implements ApiTracer {
4949
private final Stopwatch clientRequestTimer;
5050
private final GoldenSignalsMetricsRecorder metricsRecorder;
5151
private final Map<String, Object> attributes;
52+
private final ApiTracerContext.Transport transport;
5253

5354
GoldenSignalsMetricsTracer(
5455
GoldenSignalsMetricsRecorder metricsRecorder, ApiTracerContext apiTracerContext) {
5556
this.clientRequestTimer = Stopwatch.createStarted();
5657
this.metricsRecorder = metricsRecorder;
5758
this.attributes = apiTracerContext.getMetricsAttributes();
59+
this.transport = apiTracerContext.transport();
5860
}
5961

6062
@VisibleForTesting
@@ -65,6 +67,7 @@ class GoldenSignalsMetricsTracer implements ApiTracer {
6567
this.clientRequestTimer = Stopwatch.createStarted(ticker);
6668
this.metricsRecorder = metricsRecorder;
6769
this.attributes = new HashMap<>(apiTracerContext.getMetricsAttributes());
70+
this.transport = apiTracerContext.transport();
6871
}
6972

7073
/**
@@ -88,7 +91,7 @@ public void operationCancelled() {
8891

8992
@Override
9093
public void operationFailed(Throwable error) {
91-
attributes.put(RPC_RESPONSE_STATUS_ATTRIBUTE, ObservabilityUtils.extractStatus(error));
94+
ObservabilityUtils.populateStatusAttributes(attributes, error, transport);
9295
metricsRecorder.recordOperationLatency(
9396
clientRequestTimer.elapsed(TimeUnit.NANOSECONDS) / 1_000_000_000.0, attributes);
9497
}

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/LoggingTracer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ void recordActionableError(Throwable error) {
8080

8181
logContext.put(
8282
ObservabilityAttributes.RPC_RESPONSE_STATUS_ATTRIBUTE,
83-
ObservabilityUtils.extractStatus(error));
83+
ObservabilityUtils.extractStatus(error).toString());
8484

8585
ErrorInfo errorInfo = ObservabilityUtils.extractErrorInfo(error);
8686
if (errorInfo != null) {

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracer.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ public void operationFailed(Throwable error) {
120120
if (operationFinished.getAndSet(true)) {
121121
throw new IllegalStateException(OPERATION_FINISHED_STATUS_MESSAGE);
122122
}
123-
attributes.put(STATUS_ATTRIBUTE, ObservabilityUtils.extractStatus(error));
123+
// Uses the GRPC status code representation.
124+
attributes.put(STATUS_ATTRIBUTE, ObservabilityUtils.extractStatus(error).toString());
124125
metricsRecorder.recordOperationLatency(
125126
operationTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
126127
metricsRecorder.recordOperationCount(1, attributes);
@@ -172,7 +173,7 @@ public void attemptCancelled() {
172173
*/
173174
@Override
174175
public void attemptFailedDuration(Throwable error, java.time.Duration delay) {
175-
attributes.put(STATUS_ATTRIBUTE, ObservabilityUtils.extractStatus(error));
176+
attributes.put(STATUS_ATTRIBUTE, ObservabilityUtils.extractStatus(error).toString());
176177
metricsRecorder.recordAttemptLatency(attemptTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
177178
metricsRecorder.recordAttemptCount(1, attributes);
178179
}
@@ -196,7 +197,7 @@ public void attemptFailed(Throwable error, org.threeten.bp.Duration delay) {
196197
*/
197198
@Override
198199
public void attemptFailedRetriesExhausted(Throwable error) {
199-
attributes.put(STATUS_ATTRIBUTE, ObservabilityUtils.extractStatus(error));
200+
attributes.put(STATUS_ATTRIBUTE, ObservabilityUtils.extractStatus(error).toString());
200201
metricsRecorder.recordAttemptLatency(attemptTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
201202
metricsRecorder.recordAttemptCount(1, attributes);
202203
}
@@ -210,7 +211,7 @@ public void attemptFailedRetriesExhausted(Throwable error) {
210211
*/
211212
@Override
212213
public void attemptPermanentFailure(Throwable error) {
213-
attributes.put(STATUS_ATTRIBUTE, ObservabilityUtils.extractStatus(error));
214+
attributes.put(STATUS_ATTRIBUTE, ObservabilityUtils.extractStatus(error).toString());
214215
metricsRecorder.recordAttemptLatency(attemptTimer.elapsed(TimeUnit.MILLISECONDS), attributes);
215216
metricsRecorder.recordAttemptCount(1, attributes);
216217
}

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/ObservabilityAttributes.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ public class ObservabilityAttributes {
9494
/** Size of the response body in bytes. */
9595
public static final String HTTP_RESPONSE_BODY_SIZE = "http.response.body.size";
9696

97+
/** The HTTP status code of the request (e.g., 200, 404). */
98+
public static final String HTTP_RESPONSE_STATUS_ATTRIBUTE = "http.response.status_code";
99+
97100
/** The resend count of the request. Only used in HTTP transport. */
98101
public static final String HTTP_RESEND_COUNT_ATTRIBUTE = "http.request.resend_count";
99102

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/ObservabilityUtils.java

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@ final class ObservabilityUtils {
5050
*/
5151
static String extractErrorType(@Nullable Throwable error) {
5252
return ErrorTypeUtil.extractErrorType(error);
53+
}
54+
55+
/** Function to extract the status of the error as a canonical code. */
56+
static StatusCode.Code extractStatus(@Nullable Throwable error) {
57+
if (error == null) {
58+
return StatusCode.Code.OK;
59+
} else if (error instanceof CancellationException) {
60+
return StatusCode.Code.CANCELLED;
61+
} else if (error instanceof ApiException) {
62+
return ((ApiException) error).getStatusCode().getCode();
63+
} else {
64+
return StatusCode.Code.UNKNOWN;
65+
}
5366
}
5467

5568
/** Constant for redacted values. */
@@ -157,26 +170,17 @@ private static String redactSensitiveQueryValues(final String rawQuery) {
157170
return Joiner.on('&').join(redactedParams);
158171
}
159172

160-
/**
161-
* Function to extract the status of the error as a string.
162-
*
163-
* @param error the thrown throwable error
164-
* @return the extracted status string
165-
*/
166-
static String extractStatus(@Nullable final Throwable error) {
167-
final String statusString;
168-
169-
if (error == null) {
170-
return StatusCode.Code.OK.toString();
171-
} else if (error instanceof CancellationException) {
172-
statusString = StatusCode.Code.CANCELLED.toString();
173-
} else if (error instanceof ApiException) {
174-
statusString = ((ApiException) error).getStatusCode().getCode().toString();
173+
static void populateStatusAttributes(
174+
Map<String, Object> attributes,
175+
@Nullable Throwable error,
176+
ApiTracerContext.Transport transport) {
177+
StatusCode.Code code = extractStatus(error);
178+
if (transport == ApiTracerContext.Transport.HTTP) {
179+
attributes.put(
180+
ObservabilityAttributes.HTTP_RESPONSE_STATUS_ATTRIBUTE, (long) code.getHttpStatusCode());
175181
} else {
176-
statusString = StatusCode.Code.UNKNOWN.toString();
182+
attributes.put(ObservabilityAttributes.RPC_RESPONSE_STATUS_ATTRIBUTE, code.toString());
177183
}
178-
179-
return statusString;
180184
}
181185

182186
/** Function to extract the ErrorInfo payload from the error, if available */

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/SpanTracer.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import io.opentelemetry.api.trace.Tracer;
4040
import java.util.HashMap;
4141
import java.util.Map;
42+
import java.util.concurrent.CancellationException;
4243

4344
/** An implementation of {@link ApiTracer} that uses OpenTelemetry to record traces. */
4445
@BetaApi
@@ -134,7 +135,7 @@ public void attemptStarted(Object request, int attemptNumber) {
134135

135136
@Override
136137
public void attemptSucceeded() {
137-
endAttempt();
138+
recordErrorAndEndAttempt(null);
138139
}
139140

140141
@Override
@@ -181,7 +182,7 @@ private long extractContentLength(java.util.Map<String, Object> headers) {
181182

182183
@Override
183184
public void attemptCancelled() {
184-
endAttempt();
185+
recordErrorAndEndAttempt(new CancellationException());
185186
}
186187

187188
@Override
@@ -220,14 +221,23 @@ private void recordErrorAndEndAttempt(Throwable error) {
220221
ObservabilityAttributes.STATUS_MESSAGE_ATTRIBUTE, error.getMessage());
221222
}
222223

224+
Map<String, Object> endAttributes = new HashMap<>();
225+
ObservabilityUtils.populateStatusAttributes(
226+
endAttributes, error, this.apiTracerContext.transport());
227+
if (!endAttributes.isEmpty()) {
228+
attemptSpan.setAllAttributes(ObservabilityUtils.toOtelAttributes(endAttributes));
229+
}
230+
223231
endAttempt();
224232
}
225233

226234
private void endAttempt() {
227-
if (attemptSpan != null) {
228-
attemptSpan.end();
229-
attemptSpan = null;
230-
}
235+
if (attemptSpan == null) {
236+
return;
237+
}
238+
239+
attemptSpan.end();
240+
attemptSpan = null;
231241
}
232242

233243
@Override

sdk-platform-java/gax-java/gax/src/test/java/com/google/api/gax/tracing/ObservabilityUtilsTest.java

Lines changed: 106 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,23 +47,23 @@ void testExtractStatus_errorConversion_apiExceptions() {
4747
ApiException error =
4848
new ApiException(
4949
"fake_error", null, new FakeStatusCode(StatusCode.Code.INVALID_ARGUMENT), false);
50-
String errorCode = ObservabilityUtils.extractStatus(error);
51-
assertThat(errorCode).isEqualTo(StatusCode.Code.INVALID_ARGUMENT.toString());
50+
StatusCode.Code errorCode = ObservabilityUtils.extractStatus(error);
51+
assertThat(errorCode).isEqualTo(StatusCode.Code.INVALID_ARGUMENT);
5252
}
5353

5454
@Test
5555
void testExtractStatus_errorConversion_noError() {
5656
// test "OK", which corresponds to a "null" error.
57-
String successCode = ObservabilityUtils.extractStatus(null);
58-
assertThat(successCode).isEqualTo(StatusCode.Code.OK.toString());
57+
StatusCode.Code successCode = ObservabilityUtils.extractStatus(null);
58+
assertThat(successCode).isEqualTo(StatusCode.Code.OK);
5959
}
6060

6161
@Test
6262
void testExtractStatus_errorConversion_unknownException() {
6363
// test "UNKNOWN"
6464
Throwable unknownException = new RuntimeException();
65-
String errorCode2 = ObservabilityUtils.extractStatus(unknownException);
66-
assertThat(errorCode2).isEqualTo(StatusCode.Code.UNKNOWN.toString());
65+
StatusCode.Code errorCode2 = ObservabilityUtils.extractStatus(unknownException);
66+
assertThat(errorCode2).isEqualTo(StatusCode.Code.UNKNOWN);
6767
}
6868

6969
@Test
@@ -114,6 +114,106 @@ void testToOtelAttributes_shouldMapIntAttributes() {
114114
.isEqualTo((long) attribute2Value);
115115
}
116116

117+
@Test
118+
void testPopulateStatusAttributes_grpc_success() {
119+
Map<String, Object> attributes = new java.util.HashMap<>();
120+
ObservabilityUtils.populateStatusAttributes(attributes, null, ApiTracerContext.Transport.GRPC);
121+
assertThat(attributes)
122+
.containsEntry(ObservabilityAttributes.RPC_RESPONSE_STATUS_ATTRIBUTE, "OK");
123+
}
124+
125+
@Test
126+
void testPopulateStatusAttributes_grpc_apiException() {
127+
Map<String, Object> attributes = new java.util.HashMap<>();
128+
ApiException error =
129+
new ApiException("fake_error", null, new FakeStatusCode(StatusCode.Code.NOT_FOUND), false);
130+
ObservabilityUtils.populateStatusAttributes(attributes, error, ApiTracerContext.Transport.GRPC);
131+
assertThat(attributes)
132+
.containsEntry(ObservabilityAttributes.RPC_RESPONSE_STATUS_ATTRIBUTE, "NOT_FOUND");
133+
}
134+
135+
@Test
136+
void testPopulateStatusAttributes_grpc_cancellationException() {
137+
Map<String, Object> attributes = new java.util.HashMap<>();
138+
Throwable error = new java.util.concurrent.CancellationException();
139+
ObservabilityUtils.populateStatusAttributes(attributes, error, ApiTracerContext.Transport.GRPC);
140+
assertThat(attributes)
141+
.containsEntry(ObservabilityAttributes.RPC_RESPONSE_STATUS_ATTRIBUTE, "CANCELLED");
142+
}
143+
144+
@Test
145+
void testPopulateStatusAttributes_http_success() {
146+
Map<String, Object> attributes = new java.util.HashMap<>();
147+
ObservabilityUtils.populateStatusAttributes(attributes, null, ApiTracerContext.Transport.HTTP);
148+
assertThat(attributes)
149+
.containsEntry(
150+
ObservabilityAttributes.HTTP_RESPONSE_STATUS_ATTRIBUTE,
151+
(long) StatusCode.Code.OK.getHttpStatusCode());
152+
}
153+
154+
@Test
155+
void testPopulateStatusAttributes_http_apiExceptionWithIntegerTransportCode() {
156+
Map<String, Object> attributes = new java.util.HashMap<>();
157+
ApiException error =
158+
new ApiException(
159+
"fake_error",
160+
null,
161+
new com.google.api.gax.rpc.StatusCode() {
162+
@Override
163+
public Code getCode() {
164+
return Code.NOT_FOUND;
165+
}
166+
167+
@Override
168+
public Object getTransportCode() {
169+
return StatusCode.Code.NOT_FOUND.getHttpStatusCode();
170+
}
171+
},
172+
false);
173+
ObservabilityUtils.populateStatusAttributes(attributes, error, ApiTracerContext.Transport.HTTP);
174+
assertThat(attributes)
175+
.containsEntry(
176+
ObservabilityAttributes.HTTP_RESPONSE_STATUS_ATTRIBUTE,
177+
(long) StatusCode.Code.NOT_FOUND.getHttpStatusCode());
178+
}
179+
180+
@Test
181+
void testPopulateStatusAttributes_http_apiExceptionWithNonIntegerTransportCode() {
182+
Map<String, Object> attributes = new java.util.HashMap<>();
183+
ApiException error =
184+
new ApiException(
185+
"fake_error",
186+
null,
187+
new com.google.api.gax.rpc.StatusCode() {
188+
@Override
189+
public Code getCode() {
190+
return Code.NOT_FOUND;
191+
}
192+
193+
@Override
194+
public Object getTransportCode() {
195+
return "Not Found";
196+
}
197+
},
198+
false);
199+
ObservabilityUtils.populateStatusAttributes(attributes, error, ApiTracerContext.Transport.HTTP);
200+
assertThat(attributes)
201+
.containsEntry(
202+
ObservabilityAttributes.HTTP_RESPONSE_STATUS_ATTRIBUTE,
203+
(long) StatusCode.Code.NOT_FOUND.getHttpStatusCode());
204+
}
205+
206+
@Test
207+
void testPopulateStatusAttributes_http_cancellationException() {
208+
Map<String, Object> attributes = new java.util.HashMap<>();
209+
Throwable error = new java.util.concurrent.CancellationException();
210+
ObservabilityUtils.populateStatusAttributes(attributes, error, ApiTracerContext.Transport.HTTP);
211+
assertThat(attributes)
212+
.containsEntry(
213+
ObservabilityAttributes.HTTP_RESPONSE_STATUS_ATTRIBUTE,
214+
(long) StatusCode.Code.CANCELLED.getHttpStatusCode());
215+
}
216+
117217
@Test
118218
void testToOtelAttributes_shouldReturnEmptyAttributes_nullInput() {
119219
assertThat(ObservabilityUtils.toOtelAttributes(null)).isEqualTo(Attributes.empty());

0 commit comments

Comments
 (0)