Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f91e1ff
fix(o11y): Fully implement metrics attributes according to requirements
blakeli0 Apr 1, 2026
33bb5f5
test(o11y): add tests to verify error.type extraction in metrics tracer
blakeli0 Apr 1, 2026
ddf185f
feat(o11y): configure instrumentation version on open telemetry meter
blakeli0 Apr 1, 2026
78f7c6b
refactor(o11y): pass LibraryMetadata directly to GoldenSignalsMetrics…
blakeli0 Apr 1, 2026
77287da
refactor(o11y): simplify GoldenSignalsMetricsRecorder to act as a no-op
blakeli0 Apr 1, 2026
c5dab4a
refactor(o11y): consolidate metrics recorder validation
blakeli0 Apr 1, 2026
8af6e80
test(o11y): add static factory creation tests for metrics recorder
blakeli0 Apr 1, 2026
f6b2b66
style: replace fully qualified names with imports in o11y logic
blakeli0 Apr 1, 2026
26827d9
test(o11y): add showcase test for golden signals metrics
blakeli0 Apr 1, 2026
51b930e
fix: format
blakeli0 Apr 1, 2026
721400e
fix: format
blakeli0 Apr 2, 2026
1601568
test(observability): fix GoldenSignalsMetricsTracerTest error attribu…
blakeli0 Apr 2, 2026
6c5362d
Merge branch 'main' into metrics-attributes-update
blakeli0 Apr 2, 2026
18c0a49
test(observability): expect rpc.method for HTTP json transport
blakeli0 Apr 2, 2026
ef38a31
fix: remove version test
blakeli0 Apr 2, 2026
b8f35a4
test(observability): fix GoldenSignalsMetricsTracerTest error attribu…
blakeli0 Apr 2, 2026
232aeb8
chore: remove intermediate draft testing files and debug logs
blakeli0 Apr 2, 2026
20d3858
fix: add sleep. Add service name
blakeli0 Apr 2, 2026
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
Expand Up @@ -29,12 +29,16 @@
*/
package com.google.api.gax.tracing;

import com.google.api.gax.rpc.LibraryMetadata;
import com.google.common.base.Strings;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.MeterBuilder;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

/**
* This class takes an OpenTelemetry object, and creates instruments (meters, histograms etc.) from
Expand All @@ -53,8 +57,23 @@ class GoldenSignalsMetricsRecorder {
900.0, 3600.0);
final DoubleHistogram clientRequestDurationRecorder;

GoldenSignalsMetricsRecorder(OpenTelemetry openTelemetry, String libraryName) {
Meter meter = openTelemetry.meterBuilder(libraryName).build();
@Nullable
static GoldenSignalsMetricsRecorder create(
OpenTelemetry openTelemetry, LibraryMetadata libraryMetadata) {
if (libraryMetadata == null || Strings.isNullOrEmpty(libraryMetadata.artifactName())) {
return null;
}
return new GoldenSignalsMetricsRecorder(openTelemetry, libraryMetadata);
}

private GoldenSignalsMetricsRecorder(
OpenTelemetry openTelemetry, LibraryMetadata libraryMetadata) {
MeterBuilder meterBuilder = openTelemetry.meterBuilder(libraryMetadata.artifactName());
String libraryVersion = libraryMetadata.version();
if (!Strings.isNullOrEmpty(libraryVersion)) {
meterBuilder.setInstrumentationVersion(libraryVersion);
}
Meter meter = meterBuilder.build();

this.clientRequestDurationRecorder =
meter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,12 @@
*/
package com.google.api.gax.tracing;

import static com.google.api.gax.tracing.ObservabilityAttributes.RPC_RESPONSE_STATUS_ATTRIBUTE;

import com.google.api.gax.rpc.StatusCode;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Stopwatch;
import com.google.common.base.Ticker;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeUnit;

/**
Expand Down Expand Up @@ -77,21 +75,25 @@ class GoldenSignalsMetricsTracer implements ApiTracer {
*/
@Override
public void operationSucceeded() {
attributes.put(RPC_RESPONSE_STATUS_ATTRIBUTE, StatusCode.Code.OK.toString());
ObservabilityUtils.populateStatusAttributes(attributes, null, transport);
metricsRecorder.recordOperationLatency(
clientRequestTimer.elapsed(TimeUnit.NANOSECONDS) / 1_000_000_000.0, attributes);
}

@Override
public void operationCancelled() {
attributes.put(RPC_RESPONSE_STATUS_ATTRIBUTE, StatusCode.Code.CANCELLED.toString());
metricsRecorder.recordOperationLatency(
clientRequestTimer.elapsed(TimeUnit.NANOSECONDS) / 1_000_000_000.0, attributes);
recordError(new CancellationException());
}

@Override
public void operationFailed(Throwable error) {
recordError(error);
}

private void recordError(Throwable error) {
ObservabilityUtils.populateStatusAttributes(attributes, error, transport);
attributes.put(
ObservabilityAttributes.ERROR_TYPE_ATTRIBUTE, ObservabilityUtils.extractErrorType(error));
metricsRecorder.recordOperationLatency(
clientRequestTimer.elapsed(TimeUnit.NANOSECONDS) / 1_000_000_000.0, attributes);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,8 @@
*/
package com.google.api.gax.tracing;

import com.google.api.client.util.Strings;
import com.google.api.core.BetaApi;
import com.google.api.core.InternalApi;
import com.google.api.gax.rpc.LibraryMetadata;
import io.opentelemetry.api.OpenTelemetry;

/**
Expand Down Expand Up @@ -85,14 +83,12 @@ public ApiTracerFactory withContext(ApiTracerContext context) {
if (context == null) {
return new BaseApiTracerFactory();
}
LibraryMetadata metadata = context.libraryMetadata();
if (metadata == null || metadata.isEmpty() || Strings.isNullOrEmpty(metadata.artifactName())) {
this.metricsRecorder =
GoldenSignalsMetricsRecorder.create(openTelemetry, context.libraryMetadata());
if (this.metricsRecorder == null) {
return new BaseApiTracerFactory();
}
this.clientLevelTracerContext = context;
this.metricsRecorder =
new GoldenSignalsMetricsRecorder(
openTelemetry, clientLevelTracerContext.libraryMetadata().artifactName());
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,10 @@ static void populateStatusAttributes(
@Nullable Throwable error,
ApiTracerContext.Transport transport) {
StatusCode.Code code = extractStatus(error);
attributes.put(ObservabilityAttributes.RPC_RESPONSE_STATUS_ATTRIBUTE, code.toString());
if (transport == ApiTracerContext.Transport.HTTP) {
attributes.put(
ObservabilityAttributes.HTTP_RESPONSE_STATUS_ATTRIBUTE, (long) code.getHttpStatusCode());
} else {
attributes.put(ObservabilityAttributes.RPC_RESPONSE_STATUS_ATTRIBUTE, code.toString());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import static com.google.api.gax.tracing.GoldenSignalsMetricsRecorder.CLIENT_REQUEST_DURATION_METRIC_NAME;
import static com.google.common.truth.Truth.assertThat;

import com.google.api.gax.rpc.LibraryMetadata;
import com.google.common.collect.ImmutableMap;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
Expand Down Expand Up @@ -62,7 +63,13 @@ void setUp() {
SdkMeterProvider.builder().registerMetricReader(metricReader).build();
OpenTelemetry openTelemetry =
OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build();
recorder = new GoldenSignalsMetricsRecorder(openTelemetry, ARTIFACT_NAME);
recorder =
GoldenSignalsMetricsRecorder.create(
openTelemetry,
LibraryMetadata.newBuilder()
.setArtifactName(ARTIFACT_NAME)
.setVersion("1.2.3")
.build());
}

@Test
Expand Down Expand Up @@ -114,4 +121,27 @@ void recordOperationLatency_shouldRecordMetricAttributes() {
assertThat(metricData.getHistogramData().getPoints().iterator().next().getAttributes())
.isEqualTo(Attributes.of(AttributeKey.stringKey(ATTRIBUTE_1), VALUE_1));
}

@Test
void create_shouldReturnNull_whenLibraryMetadataIsNull() {
GoldenSignalsMetricsRecorder actual =
GoldenSignalsMetricsRecorder.create(OpenTelemetry.noop(), null);
assertThat(actual).isNull();
}

@Test
void create_shouldReturnNull_whenArtifactNameIsNull() {
LibraryMetadata metadata = LibraryMetadata.newBuilder().setVersion("1.0.0").build();
GoldenSignalsMetricsRecorder actual =
GoldenSignalsMetricsRecorder.create(OpenTelemetry.noop(), metadata);
assertThat(actual).isNull();
}

@Test
void create_shouldReturnNull_whenArtifactNameIsEmpty() {
LibraryMetadata metadata = LibraryMetadata.newBuilder().setArtifactName("").build();
GoldenSignalsMetricsRecorder actual =
GoldenSignalsMetricsRecorder.create(OpenTelemetry.noop(), metadata);
assertThat(actual).isNull();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import static com.google.common.truth.Truth.assertThat;

import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.LibraryMetadata;
import com.google.api.gax.rpc.StatusCode;
import com.google.api.gax.rpc.testing.FakeStatusCode;
import com.google.common.testing.FakeTicker;
Expand Down Expand Up @@ -68,7 +69,12 @@ void setUp() {
ticker = new FakeTicker();
tracer =
new GoldenSignalsMetricsTracer(
new GoldenSignalsMetricsRecorder(openTelemetry, ARTIFACT_NAME),
GoldenSignalsMetricsRecorder.create(
openTelemetry,
LibraryMetadata.newBuilder()
.setArtifactName(ARTIFACT_NAME)
.setVersion("1.2.3")
.build()),
ApiTracerContext.empty(),
ticker);
}
Expand Down Expand Up @@ -129,6 +135,8 @@ void operationCancelled_shouldRecordsOKStatus() {
assertThat(metricData.getHistogramData().getPoints().iterator().next().getAttributes())
.isEqualTo(
Attributes.of(
AttributeKey.stringKey(ObservabilityAttributes.ERROR_TYPE_ATTRIBUTE),
"CancellationException",
AttributeKey.stringKey(RPC_RESPONSE_STATUS_ATTRIBUTE),
StatusCode.Code.CANCELLED.toString()));
}
Expand Down Expand Up @@ -163,7 +171,67 @@ void operationFailed_shouldRecordsOKStatus() {
assertThat(metricData.getHistogramData().getPoints().iterator().next().getAttributes())
.isEqualTo(
Attributes.of(
AttributeKey.stringKey(ObservabilityAttributes.ERROR_TYPE_ATTRIBUTE),
"INTERNAL",
AttributeKey.stringKey(RPC_RESPONSE_STATUS_ATTRIBUTE),
StatusCode.Code.INTERNAL.toString()));
}

@Test
void operationFailed_shouldRecordCancellationException() {
java.util.concurrent.CancellationException error =
new java.util.concurrent.CancellationException("test cancellation");
tracer.operationFailed(error);

Collection<MetricData> metrics = metricReader.collectAllMetrics();
assertThat(metrics).hasSize(1);
MetricData metricData = metrics.iterator().next();

assertThat(metricData.getHistogramData().getPoints()).hasSize(1);
assertThat(metricData.getHistogramData().getPoints().iterator().next().getAttributes())
.isEqualTo(
Attributes.of(
AttributeKey.stringKey(ObservabilityAttributes.ERROR_TYPE_ATTRIBUTE),
"CancellationException",
AttributeKey.stringKey(RPC_RESPONSE_STATUS_ATTRIBUTE),
StatusCode.Code.CANCELLED.toString()));
}

@Test
void operationFailed_shouldRecordClientTimeout() {
java.net.SocketTimeoutException error = new java.net.SocketTimeoutException("test timeout");
tracer.operationFailed(error);

Collection<MetricData> metrics = metricReader.collectAllMetrics();
assertThat(metrics).hasSize(1);
MetricData metricData = metrics.iterator().next();

assertThat(metricData.getHistogramData().getPoints()).hasSize(1);
assertThat(metricData.getHistogramData().getPoints().iterator().next().getAttributes())
.isEqualTo(
Attributes.of(
AttributeKey.stringKey(ObservabilityAttributes.ERROR_TYPE_ATTRIBUTE),
"CLIENT_TIMEOUT",
AttributeKey.stringKey(RPC_RESPONSE_STATUS_ATTRIBUTE),
StatusCode.Code.UNKNOWN.toString()));
}

@Test
void operationFailed_shouldRecordClientRequestError() {
IllegalArgumentException error = new IllegalArgumentException("test illegal argument");
tracer.operationFailed(error);

Collection<MetricData> metrics = metricReader.collectAllMetrics();
assertThat(metrics).hasSize(1);
MetricData metricData = metrics.iterator().next();

assertThat(metricData.getHistogramData().getPoints()).hasSize(1);
assertThat(metricData.getHistogramData().getPoints().iterator().next().getAttributes())
.isEqualTo(
Attributes.of(
AttributeKey.stringKey(ObservabilityAttributes.ERROR_TYPE_ATTRIBUTE),
"CLIENT_REQUEST_ERROR",
AttributeKey.stringKey(RPC_RESPONSE_STATUS_ATTRIBUTE),
StatusCode.Code.UNKNOWN.toString()));
}
}
Loading
Loading