From 08cb1b89b7e381ba014119e768e5f0bd7e5acb28 Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Fri, 26 May 2023 16:34:08 -0400 Subject: [PATCH 01/34] feat: migrate exporter to OTEL --- google-cloud-bigtable/pom.xml | 40 +++ .../BigtableCloudMonitoringExporter.java | 125 ++++++++++ .../stub/metrics/BigtableExporterUtils.java | 234 ++++++++++++++++++ .../metrics/BuiltinMetricsAttributes.java | 34 +++ .../BigtableCloudMonitoringExporterTest.java | 217 ++++++++++++++++ 5 files changed, 650 insertions(+) create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java create mode 100644 google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java diff --git a/google-cloud-bigtable/pom.xml b/google-cloud-bigtable/pom.xml index b293e75554..90d15cdacd 100644 --- a/google-cloud-bigtable/pom.xml +++ b/google-cloud-bigtable/pom.xml @@ -40,6 +40,7 @@ 1.55.1 3.23.2 + 1.26.0 @@ -332,6 +333,45 @@ mockito-core test + + io.opentelemetry + opentelemetry-api + ${opentelemetry.version} + + + io.opentelemetry + opentelemetry-sdk-metrics + ${opentelemetry.version} + + + io.opentelemetry + opentelemetry-sdk-common + ${opentelemetry.version} + + + com.google.cloud + google-cloud-monitoring + + + + com.google.http-client + google-http-client-gson + + + com.google.http-client + google-http-client + + + + io.perfmark + perfmark-api + + + + + com.google.api.grpc + proto-google-cloud-monitoring-v3 + diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java new file mode 100644 index 0000000000..df9765fe3f --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java @@ -0,0 +1,125 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub.metrics; + +import com.google.api.MonitoredResource; +import com.google.api.gax.core.FixedCredentialsProvider; +import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; +import com.google.auth.Credentials; +import com.google.cloud.monitoring.v3.MetricServiceClient; +import com.google.cloud.monitoring.v3.MetricServiceSettings; +import com.google.common.annotations.VisibleForTesting; +import com.google.monitoring.v3.CreateTimeSeriesRequest; +import com.google.monitoring.v3.ProjectName; +import com.google.monitoring.v3.TimeSeries; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import org.threeten.bp.Duration; + +final class BigtableCloudMonitoringExporter implements MetricExporter { + + private static final Logger logger = + Logger.getLogger(BigtableCloudMonitoringExporter.class.getName()); + private final MetricServiceClient client; + private final String taskId; + private final MonitoredResource monitoredResource; + + private static final String RESOURCE_TYPE = "bigtable_client_raw"; + + BigtableCloudMonitoringExporter(Credentials credentials) throws Exception { + MetricServiceSettings.Builder settingsBuilder = + MetricServiceSettings.newBuilder() + .setTransportChannelProvider(InstantiatingGrpcChannelProvider.newBuilder().build()); + settingsBuilder.setCredentialsProvider(FixedCredentialsProvider.create(credentials)); + + org.threeten.bp.Duration timeout = Duration.ofMinutes(1); + settingsBuilder.createServiceTimeSeriesSettings().setSimpleTimeoutNoRetries(timeout); + this.client = MetricServiceClient.create(settingsBuilder.build()); + this.taskId = BigtableExporterUtils.getDefaultTaskValue(); + this.monitoredResource = MonitoredResource.newBuilder().setType(RESOURCE_TYPE).build(); + } + + @VisibleForTesting + BigtableCloudMonitoringExporter( + MetricServiceClient client, MonitoredResource monitoredResource, String taskId) { + this.client = client; + this.monitoredResource = monitoredResource; + this.taskId = taskId; + } + + @Override + public CompletableResultCode export(Collection collection) { + Map> projectToTimeSeries; + + for (MetricData metricData : collection) { + projectToTimeSeries = + metricData.getData().getPoints().stream() + .collect( + Collectors.groupingBy( + BigtableExporterUtils::getProjectId, + Collectors.mapping( + pointData -> + BigtableExporterUtils.convertPointToTimeSeries( + metricData, pointData, taskId, monitoredResource), + Collectors.toList()))); + + for (Map.Entry> entry : projectToTimeSeries.entrySet()) { + ProjectName projectName = ProjectName.of(entry.getKey()); + CreateTimeSeriesRequest request = + CreateTimeSeriesRequest.newBuilder() + .setName(projectName.toString()) + .addAllTimeSeries(entry.getValue()) + .build(); + + try { + this.client.createServiceTimeSeries(request); + } catch (Throwable e) { + logger.log( + Level.WARNING, + "Exception thrown when exporting TimeSeries for projectName=" + + projectName.getProject(), + e); + } + } + } + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + client.shutdown(); + return CompletableResultCode.ofSuccess(); + } + + @Override + public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { + return AggregationTemporality.CUMULATIVE; + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java new file mode 100644 index 0000000000..94f926eb29 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java @@ -0,0 +1,234 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub.metrics; + +import static com.google.api.Distribution.BucketOptions; +import static com.google.api.Distribution.BucketOptions.Explicit; +import static com.google.api.MetricDescriptor.MetricKind; +import static com.google.api.MetricDescriptor.MetricKind.CUMULATIVE; +import static com.google.api.MetricDescriptor.MetricKind.GAUGE; +import static com.google.api.MetricDescriptor.MetricKind.UNRECOGNIZED; +import static com.google.api.MetricDescriptor.ValueType; +import static com.google.api.MetricDescriptor.ValueType.DISTRIBUTION; +import static com.google.api.MetricDescriptor.ValueType.DOUBLE; +import static com.google.api.MetricDescriptor.ValueType.INT64; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_UID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLUSTER_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.INSTANCE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.PROJECT_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.TABLE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ZONE_ID; + +import com.google.api.Distribution; +import com.google.api.Metric; +import com.google.api.MonitoredResource; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.monitoring.v3.Point; +import com.google.monitoring.v3.TimeInterval; +import com.google.monitoring.v3.TimeSeries; +import com.google.monitoring.v3.TypedValue; +import com.google.protobuf.Timestamp; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.data.DoublePointData; +import io.opentelemetry.sdk.metrics.data.HistogramData; +import io.opentelemetry.sdk.metrics.data.HistogramPointData; +import io.opentelemetry.sdk.metrics.data.LongPointData; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.data.MetricDataType; +import io.opentelemetry.sdk.metrics.data.PointData; +import io.opentelemetry.sdk.metrics.data.SumData; +import java.lang.management.ManagementFactory; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.SecureRandom; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; + +class BigtableExporterUtils { + + private static final Logger logger = Logger.getLogger(BigtableExporterUtils.class.getName()); + private static final long NANO_PER_SECOND = (long) 1e9; + + static String getDefaultTaskValue() { + // Something like '@' + final String jvmName = ManagementFactory.getRuntimeMXBean().getName(); + // If not the expected format then generate a random number. + if (jvmName.indexOf('@') < 1) { + String hostname = "localhost"; + try { + hostname = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + logger.log(Level.INFO, "Unable to get the hostname.", e); + } + // Generate a random number and use the same format "random_number@hostname". + return "java-" + new SecureRandom().nextInt() + "@" + hostname; + } + return "java-" + UUID.randomUUID() + jvmName; + } + + private static final Set> PROMOTED_RESOURCE_LABELS = + ImmutableSet.of(PROJECT_ID, INSTANCE_ID, TABLE_ID, CLUSTER_ID, ZONE_ID); + + static TimeSeries convertPointToTimeSeries( + MetricData metricData, + PointData pointData, + String taskId, + MonitoredResource monitoredResource) { + Map, Object> attributes = pointData.getAttributes().asMap(); + MonitoredResource.Builder monitoredResourceBuilder = monitoredResource.toBuilder(); + ImmutableMap.Builder metricLabels = new ImmutableMap.Builder<>(); + for (AttributeKey attributeKey : attributes.keySet()) { + if (PROMOTED_RESOURCE_LABELS.contains(attributeKey)) { + monitoredResourceBuilder.putLabels( + attributeKey.getKey(), String.valueOf(attributes.get(attributeKey))); + } else { + if (attributes.get(attributeKey) != null) { + metricLabels.put(attributeKey.getKey(), String.valueOf(attributes.get(attributeKey))); + } + } + } + metricLabels.put(CLIENT_UID.getKey(), taskId); + + TimeSeries.Builder builder = + TimeSeries.newBuilder() + .setResource(monitoredResourceBuilder.build()) + .setMetricKind(convertMetricKind(metricData)) + .setMetric( + Metric.newBuilder() + .setType(metricData.getName()) + .putAllLabels(metricLabels.build()) + .build()) + .setValueType(convertValueType(metricData.getType())); + + TimeInterval timeInterval = + TimeInterval.newBuilder() + .setStartTime(convertTimestamp(pointData.getStartEpochNanos())) + .setEndTime(convertTimestamp(pointData.getEpochNanos())) + .build(); + + builder.addPoints(createPoint(metricData.getType(), pointData, timeInterval)); + + return builder.build(); + } + + static String getProjectId(PointData pointData) { + return pointData.getAttributes().get(PROJECT_ID); + } + + private static MetricKind convertMetricKind(MetricData metricData) { + switch (metricData.getType()) { + case HISTOGRAM: + case EXPONENTIAL_HISTOGRAM: + return convertHistogramDataType(metricData.getHistogramData()); + case LONG_GAUGE: + case DOUBLE_GAUGE: + return GAUGE; + case LONG_SUM: + return convertSumDataType(metricData.getLongSumData()); + case DOUBLE_SUM: + return convertSumDataType(metricData.getDoubleSumData()); + default: + return UNRECOGNIZED; + } + } + + private static MetricKind convertHistogramDataType(HistogramData histogramData) { + if (histogramData.getAggregationTemporality() == AggregationTemporality.CUMULATIVE) { + return CUMULATIVE; + } + return UNRECOGNIZED; + } + + private static MetricKind convertSumDataType(SumData sum) { + if (!sum.isMonotonic()) { + return GAUGE; + } + if (sum.getAggregationTemporality() == AggregationTemporality.CUMULATIVE) { + return CUMULATIVE; + } + return UNRECOGNIZED; + } + + private static ValueType convertValueType(MetricDataType metricDataType) { + switch (metricDataType) { + case LONG_GAUGE: + case LONG_SUM: + return INT64; + case DOUBLE_GAUGE: + case DOUBLE_SUM: + return DOUBLE; + case HISTOGRAM: + case EXPONENTIAL_HISTOGRAM: + return DISTRIBUTION; + default: + return ValueType.UNRECOGNIZED; + } + } + + private static Timestamp convertTimestamp(long epochNanos) { + return Timestamp.newBuilder() + .setSeconds(epochNanos / NANO_PER_SECOND) + .setNanos((int) (epochNanos % NANO_PER_SECOND)) + .build(); + } + + private static Point createPoint( + MetricDataType type, PointData pointData, TimeInterval timeInterval) { + Point.Builder builder = Point.newBuilder().setInterval(timeInterval); + switch (type) { + case HISTOGRAM: + case EXPONENTIAL_HISTOGRAM: + return builder + .setValue( + TypedValue.newBuilder() + .setDistributionValue(convertHistogramData((HistogramPointData) pointData)) + .build()) + .build(); + case DOUBLE_GAUGE: + case DOUBLE_SUM: + return builder + .setValue( + TypedValue.newBuilder() + .setDoubleValue(((DoublePointData) pointData).getValue()) + .build()) + .build(); + case LONG_GAUGE: + case LONG_SUM: + return builder + .setValue(TypedValue.newBuilder().setInt64Value(((LongPointData) pointData).getValue())) + .build(); + default: + logger.log(Level.WARNING, "unsupported metric type"); + return builder.build(); + } + } + + private static Distribution convertHistogramData(HistogramPointData pointData) { + return Distribution.newBuilder() + .setCount(pointData.getCount()) + .setMean(pointData.getCount() == 0L ? 0.0D : pointData.getSum() / pointData.getCount()) + .setBucketOptions( + BucketOptions.newBuilder() + .setExplicitBuckets(Explicit.newBuilder().addAllBounds(pointData.getBoundaries()))) + .addAllBucketCounts(pointData.getCounts()) + .build(); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java new file mode 100644 index 0000000000..070544cbd8 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub.metrics; + +import io.opentelemetry.api.common.AttributeKey; + +class BuiltinMetricsAttributes { + + public static final AttributeKey PROJECT_ID = AttributeKey.stringKey("project_id"); + public static final AttributeKey INSTANCE_ID = AttributeKey.stringKey("instance"); + public static final AttributeKey TABLE_ID = AttributeKey.stringKey("table"); + public static final AttributeKey CLUSTER_ID = AttributeKey.stringKey("cluster"); + public static final AttributeKey ZONE_ID = AttributeKey.stringKey("zone"); + static final AttributeKey CLIENT_UID = AttributeKey.stringKey("client_uid"); + + static final AttributeKey APP_PROFILE = AttributeKey.stringKey("app_profile"); + static final AttributeKey STREAMING = AttributeKey.booleanKey("streaming"); + static final AttributeKey METHOD = AttributeKey.stringKey("method"); + static final AttributeKey STATUS = AttributeKey.stringKey("status"); + static final AttributeKey CLIENT_NAME = AttributeKey.stringKey("client_name"); +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java new file mode 100644 index 0000000000..16a4e64778 --- /dev/null +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java @@ -0,0 +1,217 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub.metrics; + +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APP_PROFILE; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_UID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLUSTER_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.INSTANCE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.PROJECT_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.TABLE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ZONE_ID; +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.api.Distribution; +import com.google.api.MonitoredResource; +import com.google.api.gax.rpc.UnaryCallable; +import com.google.cloud.monitoring.v3.MetricServiceClient; +import com.google.cloud.monitoring.v3.stub.MetricServiceStub; +import com.google.common.collect.ImmutableList; +import com.google.monitoring.v3.CreateTimeSeriesRequest; +import com.google.monitoring.v3.TimeSeries; +import com.google.protobuf.Empty; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.data.HistogramPointData; +import io.opentelemetry.sdk.metrics.data.LongPointData; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.internal.data.ImmutableHistogramData; +import io.opentelemetry.sdk.metrics.internal.data.ImmutableHistogramPointData; +import io.opentelemetry.sdk.metrics.internal.data.ImmutableLongPointData; +import io.opentelemetry.sdk.metrics.internal.data.ImmutableMetricData; +import io.opentelemetry.sdk.metrics.internal.data.ImmutableSumData; +import io.opentelemetry.sdk.resources.Resource; +import java.util.Arrays; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class BigtableCloudMonitoringExporterTest { + private static final String projectId = "fake-project"; + private static final String instanceId = "fake-instance"; + private static final String appProfileId = "default"; + private static final String tableId = "fake-table"; + private static final String zone = "us-east-1"; + private static final String cluster = "cluster-1"; + + private static final String taskId = "fake-task-id"; + + @Rule public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock private MetricServiceStub mockMetricServiceStub; + private MetricServiceClient fakeMetricServiceClient; + private BigtableCloudMonitoringExporter exporter; + + private Attributes attributes; + private Resource resource; + private InstrumentationScopeInfo scope; + + @Before + public void setUp() { + + fakeMetricServiceClient = new FakeMetricServiceClient(mockMetricServiceStub); + + exporter = + new BigtableCloudMonitoringExporter( + fakeMetricServiceClient, + MonitoredResource.newBuilder().setType("bigtable-table").build(), + taskId); + + attributes = + Attributes.builder() + .put(PROJECT_ID, projectId) + .put(INSTANCE_ID, instanceId) + .put(TABLE_ID, tableId) + .put(CLUSTER_ID, cluster) + .put(ZONE_ID, zone) + .put(APP_PROFILE, appProfileId) + .build(); + + resource = Resource.create(Attributes.empty()); + + scope = InstrumentationScopeInfo.create("bigtable"); + } + + @After + public void tearDown() {} + + @Test + public void testExportingSumData() { + ArgumentCaptor argumentCaptor = + ArgumentCaptor.forClass(CreateTimeSeriesRequest.class); + + UnaryCallable mockCallable = mock(UnaryCallable.class); + when(mockMetricServiceStub.createServiceTimeSeriesCallable()).thenReturn(mockCallable); + when(mockCallable.call(argumentCaptor.capture())).thenReturn(Empty.getDefaultInstance()); + + long fakeValue = 11L; + + LongPointData longPointData = ImmutableLongPointData.create(0, 1, attributes, fakeValue); + + MetricData longData = + ImmutableMetricData.createLongSum( + resource, + scope, + "bigtable/test/long", + "description", + "1", + ImmutableSumData.create( + true, AggregationTemporality.CUMULATIVE, ImmutableList.of(longPointData))); + + exporter.export(Arrays.asList(longData)); + + CreateTimeSeriesRequest request = argumentCaptor.getValue(); + + assertThat(request.getTimeSeriesList()).hasSize(1); + + TimeSeries timeSeries = request.getTimeSeriesList().get(0); + + assertThat(timeSeries.getResource().getLabelsMap()) + .containsExactly( + PROJECT_ID.getKey(), projectId, + INSTANCE_ID.getKey(), instanceId, + TABLE_ID.getKey(), tableId, + CLUSTER_ID.getKey(), cluster, + ZONE_ID.getKey(), zone); + + assertThat(timeSeries.getMetric().getLabelsMap()).hasSize(2); + assertThat(timeSeries.getMetric().getLabelsMap()) + .containsAtLeast(APP_PROFILE.getKey(), appProfileId); + assertThat(timeSeries.getMetric().getLabelsMap()).containsAtLeast(CLIENT_UID.getKey(), taskId); + assertThat(timeSeries.getPoints(0).getValue().getInt64Value()).isEqualTo(fakeValue); + } + + @Test + public void testExportingHistogramData() { + ArgumentCaptor argumentCaptor = + ArgumentCaptor.forClass(CreateTimeSeriesRequest.class); + + UnaryCallable mockCallable = mock(UnaryCallable.class); + when(mockMetricServiceStub.createServiceTimeSeriesCallable()).thenReturn(mockCallable); + when(mockCallable.call(argumentCaptor.capture())).thenReturn(Empty.getDefaultInstance()); + + HistogramPointData histogramPointData = + ImmutableHistogramPointData.create( + 0, + 1, + attributes, + 3d, + true, + 1d, // min + true, + 2d, // max + Arrays.asList(1.0), + Arrays.asList(1L, 2L)); + + MetricData histogramData = + ImmutableMetricData.createDoubleHistogram( + resource, + scope, + "bigtable/test/histogram", + "description", + "ms", + ImmutableHistogramData.create( + AggregationTemporality.CUMULATIVE, ImmutableList.of(histogramPointData))); + + exporter.export(Arrays.asList(histogramData)); + + CreateTimeSeriesRequest request = argumentCaptor.getValue(); + + assertThat(request.getTimeSeriesList()).hasSize(1); + + TimeSeries timeSeries = request.getTimeSeriesList().get(0); + + assertThat(timeSeries.getResource().getLabelsMap()) + .containsExactly( + PROJECT_ID.getKey(), projectId, + INSTANCE_ID.getKey(), instanceId, + TABLE_ID.getKey(), tableId, + CLUSTER_ID.getKey(), cluster, + ZONE_ID.getKey(), zone); + + assertThat(timeSeries.getMetric().getLabelsMap()).hasSize(2); + assertThat(timeSeries.getMetric().getLabelsMap()) + .containsAtLeast(APP_PROFILE.getKey(), appProfileId); + assertThat(timeSeries.getMetric().getLabelsMap()).containsAtLeast(CLIENT_UID.getKey(), taskId); + Distribution distribution = timeSeries.getPoints(0).getValue().getDistributionValue(); + assertThat(distribution.getCount()).isEqualTo(3); + } + + private static class FakeMetricServiceClient extends MetricServiceClient { + + protected FakeMetricServiceClient(MetricServiceStub stub) { + super(stub); + } + } +} From 26a648acb0140e9fa94cc7a81dbe28a90c482938 Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Fri, 16 Jun 2023 11:44:55 -0400 Subject: [PATCH 02/34] address comments --- google-cloud-bigtable-deps-bom/pom.xml | 20 +++++ google-cloud-bigtable/pom.xml | 20 ----- .../BigtableCloudMonitoringExporter.java | 76 +++++++++---------- .../stub/metrics/BigtableExporterUtils.java | 45 +++++------ .../BigtableCloudMonitoringExporterTest.java | 1 + 5 files changed, 79 insertions(+), 83 deletions(-) diff --git a/google-cloud-bigtable-deps-bom/pom.xml b/google-cloud-bigtable-deps-bom/pom.xml index acdd1b011b..fdca70aef5 100644 --- a/google-cloud-bigtable-deps-bom/pom.xml +++ b/google-cloud-bigtable-deps-bom/pom.xml @@ -84,6 +84,26 @@ pom import + + io.opentelemetry + opentelemetry-api + 1.26.0 + + + io.opentelemetry + opentelemetry-sdk-metrics + 1.26.0 + + + io.opentelemetry + opentelemetry-sdk-testing + 1.26.0 + + + io.opentelemetry + opentelemetry-sdk-common + 1.26.0 + diff --git a/google-cloud-bigtable/pom.xml b/google-cloud-bigtable/pom.xml index 90d15cdacd..5cd52199c4 100644 --- a/google-cloud-bigtable/pom.xml +++ b/google-cloud-bigtable/pom.xml @@ -40,7 +40,6 @@ 1.55.1 3.23.2 - 1.26.0 @@ -336,37 +335,18 @@ io.opentelemetry opentelemetry-api - ${opentelemetry.version} io.opentelemetry opentelemetry-sdk-metrics - ${opentelemetry.version} io.opentelemetry opentelemetry-sdk-common - ${opentelemetry.version} com.google.cloud google-cloud-monitoring - - - - com.google.http-client - google-http-client-gson - - - com.google.http-client - google-http-client - - - - io.perfmark - perfmark-api - - com.google.api.grpc diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java index df9765fe3f..07bbe005a7 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java @@ -17,11 +17,11 @@ import com.google.api.MonitoredResource; import com.google.api.gax.core.FixedCredentialsProvider; -import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; import com.google.auth.Credentials; import com.google.cloud.monitoring.v3.MetricServiceClient; import com.google.cloud.monitoring.v3.MetricServiceSettings; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; import com.google.monitoring.v3.CreateTimeSeriesRequest; import com.google.monitoring.v3.ProjectName; import com.google.monitoring.v3.TimeSeries; @@ -30,80 +30,78 @@ import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.metrics.export.MetricExporter; +import java.io.IOException; import java.util.Collection; import java.util.List; -import java.util.Map; -import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import org.threeten.bp.Duration; +/** Bigtable Cloud Monitoring OpenTelemetry Exporter. */ final class BigtableCloudMonitoringExporter implements MetricExporter { private static final Logger logger = Logger.getLogger(BigtableCloudMonitoringExporter.class.getName()); private final MetricServiceClient client; + + private final String projectId; private final String taskId; private final MonitoredResource monitoredResource; private static final String RESOURCE_TYPE = "bigtable_client_raw"; - BigtableCloudMonitoringExporter(Credentials credentials) throws Exception { - MetricServiceSettings.Builder settingsBuilder = - MetricServiceSettings.newBuilder() - .setTransportChannelProvider(InstantiatingGrpcChannelProvider.newBuilder().build()); + static BigtableCloudMonitoringExporter create(String projectId, Credentials credentials) + throws IOException { + MetricServiceSettings.Builder settingsBuilder = MetricServiceSettings.newBuilder(); settingsBuilder.setCredentialsProvider(FixedCredentialsProvider.create(credentials)); org.threeten.bp.Duration timeout = Duration.ofMinutes(1); settingsBuilder.createServiceTimeSeriesSettings().setSimpleTimeoutNoRetries(timeout); - this.client = MetricServiceClient.create(settingsBuilder.build()); - this.taskId = BigtableExporterUtils.getDefaultTaskValue(); - this.monitoredResource = MonitoredResource.newBuilder().setType(RESOURCE_TYPE).build(); + return new BigtableCloudMonitoringExporter( + projectId, + MetricServiceClient.create(settingsBuilder.build()), + MonitoredResource.newBuilder().setType(RESOURCE_TYPE).build(), + BigtableExporterUtils.getDefaultTaskValue()); } @VisibleForTesting BigtableCloudMonitoringExporter( - MetricServiceClient client, MonitoredResource monitoredResource, String taskId) { + String projectId, + MetricServiceClient client, + MonitoredResource monitoredResource, + String taskId) { this.client = client; this.monitoredResource = monitoredResource; this.taskId = taskId; + this.projectId = projectId; } @Override public CompletableResultCode export(Collection collection) { - Map> projectToTimeSeries; - + Preconditions.checkArgument( + collection.stream() + .flatMap(metricData -> metricData.getData().getPoints().stream()) + .allMatch(pd -> BigtableExporterUtils.getProjectId(pd).equals(projectId)), + "Some metric data has unexpected projectId"); for (MetricData metricData : collection) { - projectToTimeSeries = + List timeSeries = metricData.getData().getPoints().stream() - .collect( - Collectors.groupingBy( - BigtableExporterUtils::getProjectId, - Collectors.mapping( - pointData -> - BigtableExporterUtils.convertPointToTimeSeries( - metricData, pointData, taskId, monitoredResource), - Collectors.toList()))); + .map( + pointData -> + BigtableExporterUtils.convertPointToTimeSeries( + metricData, pointData, taskId, monitoredResource)) + .collect(Collectors.toList()); - for (Map.Entry> entry : projectToTimeSeries.entrySet()) { - ProjectName projectName = ProjectName.of(entry.getKey()); - CreateTimeSeriesRequest request = - CreateTimeSeriesRequest.newBuilder() - .setName(projectName.toString()) - .addAllTimeSeries(entry.getValue()) - .build(); + ProjectName projectName = ProjectName.of(projectId); + CreateTimeSeriesRequest request = + CreateTimeSeriesRequest.newBuilder() + .setName(projectName.toString()) + .addAllTimeSeries(timeSeries) + .build(); - try { - this.client.createServiceTimeSeries(request); - } catch (Throwable e) { - logger.log( - Level.WARNING, - "Exception thrown when exporting TimeSeries for projectName=" - + projectName.getProject(), - e); - } - } + this.client.createServiceTimeSeries(request); } + return CompletableResultCode.ofSuccess(); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java index 94f926eb29..7a6ade43ef 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java @@ -41,8 +41,9 @@ import com.google.monitoring.v3.TimeInterval; import com.google.monitoring.v3.TimeSeries; import com.google.monitoring.v3.TypedValue; -import com.google.protobuf.Timestamp; +import com.google.protobuf.util.Timestamps; import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.data.DoublePointData; import io.opentelemetry.sdk.metrics.data.HistogramData; @@ -56,16 +57,15 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.security.SecureRandom; -import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; +/** Utils to convert OpenTelemetry types to Cloud Monitoring API types. */ class BigtableExporterUtils { private static final Logger logger = Logger.getLogger(BigtableExporterUtils.class.getName()); - private static final long NANO_PER_SECOND = (long) 1e9; static String getDefaultTaskValue() { // Something like '@' @@ -92,19 +92,23 @@ static TimeSeries convertPointToTimeSeries( PointData pointData, String taskId, MonitoredResource monitoredResource) { - Map, Object> attributes = pointData.getAttributes().asMap(); + Attributes attributes = pointData.getAttributes(); MonitoredResource.Builder monitoredResourceBuilder = monitoredResource.toBuilder(); ImmutableMap.Builder metricLabels = new ImmutableMap.Builder<>(); - for (AttributeKey attributeKey : attributes.keySet()) { - if (PROMOTED_RESOURCE_LABELS.contains(attributeKey)) { - monitoredResourceBuilder.putLabels( - attributeKey.getKey(), String.valueOf(attributes.get(attributeKey))); - } else { - if (attributes.get(attributeKey) != null) { - metricLabels.put(attributeKey.getKey(), String.valueOf(attributes.get(attributeKey))); - } - } + + // Populated monitored resource schema + for (AttributeKey attributeKey : PROMOTED_RESOURCE_LABELS) { + monitoredResourceBuilder.putLabels( + attributeKey.getKey(), String.valueOf(attributes.get(attributeKey))); } + + // Populate the rest of the metric labels + attributes.forEach( + (key, value) -> { + if (!PROMOTED_RESOURCE_LABELS.contains(key) && value != null) { + metricLabels.put(key.getKey(), String.valueOf(value)); + } + }); metricLabels.put(CLIENT_UID.getKey(), taskId); TimeSeries.Builder builder = @@ -120,8 +124,8 @@ static TimeSeries convertPointToTimeSeries( TimeInterval timeInterval = TimeInterval.newBuilder() - .setStartTime(convertTimestamp(pointData.getStartEpochNanos())) - .setEndTime(convertTimestamp(pointData.getEpochNanos())) + .setStartTime(Timestamps.fromNanos(pointData.getStartEpochNanos())) + .setEndTime(Timestamps.fromNanos(pointData.getEpochNanos())) .build(); builder.addPoints(createPoint(metricData.getType(), pointData, timeInterval)); @@ -137,7 +141,7 @@ private static MetricKind convertMetricKind(MetricData metricData) { switch (metricData.getType()) { case HISTOGRAM: case EXPONENTIAL_HISTOGRAM: - return convertHistogramDataType(metricData.getHistogramData()); + return convertHistogramType(metricData.getHistogramData()); case LONG_GAUGE: case DOUBLE_GAUGE: return GAUGE; @@ -150,7 +154,7 @@ private static MetricKind convertMetricKind(MetricData metricData) { } } - private static MetricKind convertHistogramDataType(HistogramData histogramData) { + private static MetricKind convertHistogramType(HistogramData histogramData) { if (histogramData.getAggregationTemporality() == AggregationTemporality.CUMULATIVE) { return CUMULATIVE; } @@ -183,13 +187,6 @@ private static ValueType convertValueType(MetricDataType metricDataType) { } } - private static Timestamp convertTimestamp(long epochNanos) { - return Timestamp.newBuilder() - .setSeconds(epochNanos / NANO_PER_SECOND) - .setNanos((int) (epochNanos % NANO_PER_SECOND)) - .build(); - } - private static Point createPoint( MetricDataType type, PointData pointData, TimeInterval timeInterval) { Point.Builder builder = Point.newBuilder().setInterval(timeInterval); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java index 16a4e64778..6219562670 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java @@ -84,6 +84,7 @@ public void setUp() { exporter = new BigtableCloudMonitoringExporter( + projectId, fakeMetricServiceClient, MonitoredResource.newBuilder().setType("bigtable-table").build(), taskId); From 91fcd80c692057ed9deced7f24fe0225687c641f Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Wed, 28 Jun 2023 11:52:49 -0400 Subject: [PATCH 03/34] filter out only bigtable metrics --- .../data/v2/stub/metrics/BigtableCloudMonitoringExporter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java index 07bbe005a7..22080a6009 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java @@ -84,6 +84,9 @@ public CompletableResultCode export(Collection collection) { .allMatch(pd -> BigtableExporterUtils.getProjectId(pd).equals(projectId)), "Some metric data has unexpected projectId"); for (MetricData metricData : collection) { + if (!metricData.getInstrumentationScopeInfo().getName().equals("bigtable.googleapis.com")) { + continue; + } List timeSeries = metricData.getData().getPoints().stream() .map( From 32ac2bee9d71c8721a99536c3c168d40b082081b Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Wed, 28 Jun 2023 11:58:28 -0400 Subject: [PATCH 04/34] fix test --- .../v2/stub/metrics/BigtableCloudMonitoringExporterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java index 6219562670..685d80b296 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java @@ -101,7 +101,7 @@ public void setUp() { resource = Resource.create(Attributes.empty()); - scope = InstrumentationScopeInfo.create("bigtable"); + scope = InstrumentationScopeInfo.create("bigtable.googleapis.com"); } @After From af6cbb28dac102f1fa11fc720d4a2401dfdfc5f9 Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Wed, 28 Jun 2023 14:59:09 -0400 Subject: [PATCH 05/34] use the bom --- google-cloud-bigtable-deps-bom/pom.xml | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/google-cloud-bigtable-deps-bom/pom.xml b/google-cloud-bigtable-deps-bom/pom.xml index fdca70aef5..83e5c30a05 100644 --- a/google-cloud-bigtable-deps-bom/pom.xml +++ b/google-cloud-bigtable-deps-bom/pom.xml @@ -86,23 +86,10 @@ io.opentelemetry - opentelemetry-api - 1.26.0 - - - io.opentelemetry - opentelemetry-sdk-metrics - 1.26.0 - - - io.opentelemetry - opentelemetry-sdk-testing - 1.26.0 - - - io.opentelemetry - opentelemetry-sdk-common - 1.26.0 + opentelemetry-bom + 1.27.0 + pom + import From e87cf914463b9e48aae5d536ae718aa6d187e27e Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Fri, 30 Jun 2023 16:34:13 -0400 Subject: [PATCH 06/34] update --- .../v2/stub/metrics/BigtableCloudMonitoringExporter.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java index 22080a6009..7e7912ed82 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java @@ -47,6 +47,7 @@ final class BigtableCloudMonitoringExporter implements MetricExporter { private final String projectId; private final String taskId; private final MonitoredResource monitoredResource; + private boolean isShutdown = false; private static final String RESOURCE_TYPE = "bigtable_client_raw"; @@ -78,6 +79,9 @@ static BigtableCloudMonitoringExporter create(String projectId, Credentials cred @Override public CompletableResultCode export(Collection collection) { + if (isShutdown) { + return CompletableResultCode.ofFailure(); + } Preconditions.checkArgument( collection.stream() .flatMap(metricData -> metricData.getData().getPoints().stream()) @@ -102,7 +106,7 @@ public CompletableResultCode export(Collection collection) { .addAllTimeSeries(timeSeries) .build(); - this.client.createServiceTimeSeries(request); + this.client.createServiceTimeSeriesCallable().futureCall(request); } return CompletableResultCode.ofSuccess(); @@ -116,6 +120,7 @@ public CompletableResultCode flush() { @Override public CompletableResultCode shutdown() { client.shutdown(); + isShutdown = true; return CompletableResultCode.ofSuccess(); } From e2b7e5859a664b290ef605b45c28ad8d585d3fbd Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Wed, 5 Jul 2023 13:30:12 -0400 Subject: [PATCH 07/34] update --- .../stub/metrics/BigtableCloudMonitoringExporterTest.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java index 685d80b296..26b8dc8cd7 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java @@ -28,6 +28,8 @@ import com.google.api.Distribution; import com.google.api.MonitoredResource; +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.monitoring.v3.MetricServiceClient; import com.google.cloud.monitoring.v3.stub.MetricServiceStub; @@ -114,7 +116,8 @@ public void testExportingSumData() { UnaryCallable mockCallable = mock(UnaryCallable.class); when(mockMetricServiceStub.createServiceTimeSeriesCallable()).thenReturn(mockCallable); - when(mockCallable.call(argumentCaptor.capture())).thenReturn(Empty.getDefaultInstance()); + ApiFuture future = ApiFutures.immediateFuture(Empty.getDefaultInstance()); + when(mockCallable.futureCall(argumentCaptor.capture())).thenReturn(future); long fakeValue = 11L; @@ -160,7 +163,8 @@ public void testExportingHistogramData() { UnaryCallable mockCallable = mock(UnaryCallable.class); when(mockMetricServiceStub.createServiceTimeSeriesCallable()).thenReturn(mockCallable); - when(mockCallable.call(argumentCaptor.capture())).thenReturn(Empty.getDefaultInstance()); + ApiFuture future = ApiFutures.immediateFuture(Empty.getDefaultInstance()); + when(mockCallable.futureCall(argumentCaptor.capture())).thenReturn(future); HistogramPointData histogramPointData = ImmutableHistogramPointData.create( From 6d127e3823396baa64efd8d6aaa8f112c006f7ee Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Thu, 15 Jun 2023 13:31:15 -0400 Subject: [PATCH 08/34] feat: migrate builtin metrics to OTEl --- google-cloud-bigtable/pom.xml | 9 + .../data/v2/BigtableDataSettings.java | 40 +- .../data/v2/stub/EnhancedBigtableStub.java | 8 +- .../v2/stub/EnhancedBigtableStubSettings.java | 22 + .../stub/metrics/BigtableExporterUtils.java | 4 +- .../stub/metrics/BigtableMetricsRecorder.java | 37 ++ .../metrics/BuiltinInMetricsRecorder.java | 148 +++++ .../metrics/BuiltinMetricsAttributes.java | 128 ++++- .../v2/stub/metrics/BuiltinMetricsTracer.java | 99 +++- .../metrics/BuiltinMetricsTracerFactory.java | 84 ++- .../EnhancedBigtableStubSettingsTest.java | 1 + .../metrics/BuiltinMetricsTracerTest.java | 528 +++++++++++++----- 12 files changed, 915 insertions(+), 193 deletions(-) create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableMetricsRecorder.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinInMetricsRecorder.java diff --git a/google-cloud-bigtable/pom.xml b/google-cloud-bigtable/pom.xml index 5cd52199c4..9302900da4 100644 --- a/google-cloud-bigtable/pom.xml +++ b/google-cloud-bigtable/pom.xml @@ -336,6 +336,15 @@ io.opentelemetry opentelemetry-api + + io.opentelemetry + opentelemetry-sdk + + + io.opentelemetry + opentelemetry-sdk-testing + test + io.opentelemetry opentelemetry-sdk-metrics diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java index 701a5e8e49..4729c3fd19 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java @@ -25,13 +25,10 @@ import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; import com.google.api.gax.rpc.UnaryCallSettings; import com.google.auth.Credentials; -import com.google.auth.oauth2.GoogleCredentials; import com.google.cloud.bigtable.data.v2.models.Query; import com.google.cloud.bigtable.data.v2.models.Row; import com.google.cloud.bigtable.data.v2.stub.BigtableBatchingCallSettings; import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings; -import com.google.cloud.bigtable.stats.BigtableStackdriverStatsExporter; -import com.google.cloud.bigtable.stats.BuiltinViews; import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import io.grpc.ManagedChannelBuilder; @@ -197,23 +194,26 @@ public static void enableGfeOpenCensusStats() { com.google.cloud.bigtable.data.v2.stub.metrics.RpcViews.registerBigtableClientGfeViews(); } - /** Register built in metrics. */ + /** + * Register built in metrics. + * + * @deprecated Please use {@link BigtableDataSettings.Builder#setBuiltinMetricsEnabled(boolean)} + * to enable or disable built-in metrics. + */ + @Deprecated public static void enableBuiltinMetrics() throws IOException { - if (BUILTIN_METRICS_REGISTERED.compareAndSet(false, true)) { - BuiltinViews.registerBigtableBuiltinViews(); - BigtableStackdriverStatsExporter.register(GoogleCredentials.getApplicationDefault()); - } + BUILTIN_METRICS_REGISTERED.compareAndSet(false, true); } /** * Register built in metrics with credentials. The credentials need to have metric write access * for all the projects you're publishing to. + * + * @deprecated Please use {@link BigtableDataSettings.Builder#setBuiltinMetricsEnabled(boolean)} + * to enable or disable built-in metrics. */ public static void enableBuiltinMetrics(Credentials credentials) throws IOException { - if (BUILTIN_METRICS_REGISTERED.compareAndSet(false, true)) { - BuiltinViews.registerBigtableBuiltinViews(); - BigtableStackdriverStatsExporter.register(credentials); - } + BUILTIN_METRICS_REGISTERED.compareAndSet(false, true); } /** Returns the target project id. */ @@ -278,6 +278,11 @@ public boolean isBulkMutationFlowControlEnabled() { return stubSettings.bulkMutateRowsSettings().isServerInitiatedFlowControlEnabled(); } + /** Gets if built-in metrics is enabled. */ + public boolean isBuiltinMetricsEnabled() { + return stubSettings.isBuiltinMetricsEnabled(); + } + /** Returns the underlying RPC settings. */ public EnhancedBigtableStubSettings getStubSettings() { return stubSettings; @@ -527,6 +532,17 @@ public boolean isBulkMutationFlowControlEnabled() { return stubSettings.bulkMutateRowsSettings().isServerInitiatedFlowControlEnabled(); } + /** Set if built-in metrics will be enabled. */ + public Builder setBuiltinMetricsEnabled(boolean enable) { + stubSettings.setBuiltinMetricsEnabled(enable); + return this; + } + + /** Gets if built-in metrics is enabled. */ + public boolean isBuiltinMetricsEnabled() { + return stubSettings.isBuiltinMetricsEnabled(); + } + /** * Returns the underlying settings for making RPC calls. The settings should be changed with * care. diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java index c46539cddf..3fbec75742 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java @@ -220,12 +220,6 @@ public static EnhancedBigtableStubSettings finalizeSettings( RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID, TagValue.create(settings.getAppProfileId())) .build(); - ImmutableMap builtinAttributes = - ImmutableMap.builder() - .put("project_id", settings.getProjectId()) - .put("instance", settings.getInstanceId()) - .put("app_profile", settings.getAppProfileId()) - .build(); // Inject Opencensus instrumentation builder.setTracerFactory( new CompositeTracerFactory( @@ -250,7 +244,7 @@ public static EnhancedBigtableStubSettings finalizeSettings( .build()), // Add OpenCensus Metrics MetricsTracerFactory.create(tagger, stats, attributes), - BuiltinMetricsTracerFactory.create(builtinAttributes), + BuiltinMetricsTracerFactory.create(settings), // Add user configured tracer settings.getTracerFactory()))); return builder.build(); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java index 9e1ba64222..23de512b16 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java @@ -228,6 +228,8 @@ public class EnhancedBigtableStubSettings extends StubSettings getJwtAudienceMapping() { return jwtAudienceMapping; } + public boolean isBuiltinMetricsEnabled() { + return isBuiltinMetricsEnabled; + } + /** Returns a builder for the default ChannelProvider for this service. */ public static InstantiatingGrpcChannelProvider.Builder defaultGrpcTransportProviderBuilder() { return BigtableStubSettings.defaultGrpcTransportProviderBuilder() @@ -613,6 +620,8 @@ public static class Builder extends StubSettings.Builder jwtAudienceMapping) { return this; } + /** Returns true if builtin metrics is enabled. */ + public boolean isBuiltinMetricsEnabled() { + return isBuiltinMetricsEnabled; + } + + /** Set if builtin metrics will be enabled. */ + public Builder setBuiltinMetricsEnabled(boolean enable) { + this.isBuiltinMetricsEnabled = enable; + return this; + } + @InternalApi("Used for internal testing") public Map getJwtAudienceMapping() { return jwtAudienceMapping; @@ -1030,6 +1051,7 @@ public String toString() { generateInitialChangeStreamPartitionsSettings) .add("readChangeStreamSettings", readChangeStreamSettings) .add("pingAndWarmSettings", pingAndWarmSettings) + .add("isBuiltinMetricsEnabled", isBuiltinMetricsEnabled) .add("parent", super.toString()) .toString(); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java index 7a6ade43ef..9f9d49fe19 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java @@ -67,6 +67,8 @@ class BigtableExporterUtils { private static final Logger logger = Logger.getLogger(BigtableExporterUtils.class.getName()); + private static final String METRIC_PREFIX = "bigtable.googleapis.com/internal/client/"; + static String getDefaultTaskValue() { // Something like '@' final String jvmName = ManagementFactory.getRuntimeMXBean().getName(); @@ -117,7 +119,7 @@ static TimeSeries convertPointToTimeSeries( .setMetricKind(convertMetricKind(metricData)) .setMetric( Metric.newBuilder() - .setType(metricData.getName()) + .setType(METRIC_PREFIX + metricData.getName()) .putAllLabels(metricLabels.build()) .build()) .setValueType(convertValueType(metricData.getType())); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableMetricsRecorder.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableMetricsRecorder.java new file mode 100644 index 0000000000..ae4d7d301c --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableMetricsRecorder.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub.metrics; + +import io.opentelemetry.api.common.Attributes; + +public class BigtableMetricsRecorder { + + void recordOperationLatencies(long value, Attributes attributes) {} + + void recordAttemptLatencies(long value, Attributes attributes) {} + + void recordFirstResponseLatencies(long value, Attributes attributes) {} + + void recordRetryCount(long value, Attributes attributes) {} + + void recordServerLatencies(long value, Attributes attributes) {} + + void recordConnectivityErrorCount(long value, Attributes attributes) {} + + void recordApplicationBlockingLatencies(long value, Attributes attributes) {} + + void recordClientBlockingLatencies(long value, Attributes attributes) {} +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinInMetricsRecorder.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinInMetricsRecorder.java new file mode 100644 index 0000000000..a4b48c6283 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinInMetricsRecorder.java @@ -0,0 +1,148 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub.metrics; + +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_NAME; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.Meter; + +class BuiltinInMetricsRecorder extends BigtableMetricsRecorder { + + private static final String MILLISECOND = "ms"; + private static final String COUNT = "1"; + + private final LongHistogram operationLatencies; + private final LongHistogram attemptLatencies; + private final LongHistogram serverLatencies; + private final LongHistogram firstResponseLatencies; + private final LongHistogram clientBlockingLatencies; + private final LongHistogram applicationBlockingLatencies; + private final LongCounter connectivityErrorCount; + private final LongCounter retryCount; + + BuiltinInMetricsRecorder(Meter meter) { + operationLatencies = + meter + .histogramBuilder(OPERATION_LATENCIES_NAME) + .ofLongs() + .setDescription( + "Total time until final operation success or failure, including retries and backoff.") + .setUnit(MILLISECOND) + .build(); + attemptLatencies = + meter + .histogramBuilder(ATTEMPT_LATENCIES_NAME) + .ofLongs() + .setDescription("Client observed latency per RPC attempt.") + .setUnit(MILLISECOND) + .build(); + serverLatencies = + meter + .histogramBuilder(SERVER_LATENCIES_NAME) + .ofLongs() + .setDescription( + "The latency measured from the moment that the RPC entered the Google data center until the RPC was completed.") + .setUnit(MILLISECOND) + .build(); + firstResponseLatencies = + meter + .histogramBuilder(FIRST_RESPONSE_LATENCIES_NAME) + .ofLongs() + .setDescription( + "Latency from operation start until the response headers were received. The publishing of the measurement will be delayed until the attempt response has been received.") + .setUnit(MILLISECOND) + .build(); + clientBlockingLatencies = + meter + .histogramBuilder(CLIENT_BLOCKING_LATENCIES_NAME) + .ofLongs() + .setDescription( + "The artificial latency introduced by the client to limit the number of outstanding requests. The publishing of the measurement will be delayed until the attempt trailers have been received.") + .setUnit(MILLISECOND) + .build(); + applicationBlockingLatencies = + meter + .histogramBuilder(APPLICATION_BLOCKING_LATENCIES_NAME) + .ofLongs() + .setDescription( + "The latency of the client application consuming available response data.") + .setUnit(MILLISECOND) + .build(); + connectivityErrorCount = + meter + .counterBuilder(CONNECTIVITY_ERROR_COUNT_NAME) + .setDescription( + "Number of requests that failed to reach the Google datacenter. (Requests without google response headers") + .setUnit(COUNT) + .build(); + retryCount = + meter + .counterBuilder(RETRY_COUNT_NAME) + .setDescription("The number of additional RPCs sent after the initial attempt.") + .setUnit(COUNT) + .build(); + } + + @Override + void recordOperationLatencies(long value, Attributes attributes) { + operationLatencies.record(value, attributes); + } + + @Override + void recordAttemptLatencies(long value, Attributes attributes) { + attemptLatencies.record(value, attributes); + } + + @Override + void recordFirstResponseLatencies(long value, Attributes attributes) { + firstResponseLatencies.record(value, attributes); + } + + @Override + void recordRetryCount(long value, Attributes attributes) { + retryCount.add(value, attributes); + } + + @Override + void recordServerLatencies(long value, Attributes attributes) { + serverLatencies.record(value, attributes); + } + + @Override + void recordConnectivityErrorCount(long value, Attributes attributes) { + connectivityErrorCount.add(value, attributes); + } + + @Override + void recordApplicationBlockingLatencies(long value, Attributes attributes) { + applicationBlockingLatencies.record(value, attributes); + } + + @Override + void recordClientBlockingLatencies(long value, Attributes attributes) { + clientBlockingLatencies.record(value, attributes); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java index 070544cbd8..f8a20d961e 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java @@ -15,9 +15,14 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; +import com.google.common.collect.ImmutableList; import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.InstrumentSelector; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.View; -class BuiltinMetricsAttributes { +public class BuiltinMetricsAttributes { public static final AttributeKey PROJECT_ID = AttributeKey.stringKey("project_id"); public static final AttributeKey INSTANCE_ID = AttributeKey.stringKey("instance"); @@ -26,9 +31,128 @@ class BuiltinMetricsAttributes { public static final AttributeKey ZONE_ID = AttributeKey.stringKey("zone"); static final AttributeKey CLIENT_UID = AttributeKey.stringKey("client_uid"); - static final AttributeKey APP_PROFILE = AttributeKey.stringKey("app_profile"); + public static final AttributeKey APP_PROFILE = AttributeKey.stringKey("app_profile"); static final AttributeKey STREAMING = AttributeKey.booleanKey("streaming"); static final AttributeKey METHOD = AttributeKey.stringKey("method"); static final AttributeKey STATUS = AttributeKey.stringKey("status"); static final AttributeKey CLIENT_NAME = AttributeKey.stringKey("client_name"); + + static final String OPERATION_LATENCIES_NAME = "operation_latencies"; + static final String ATTEMPT_LATENCIES_NAME = "attempt_latencies"; + static final String RETRY_COUNT_NAME = "retry_count"; + static final String CONNECTIVITY_ERROR_COUNT_NAME = "connectivity_error_count"; + static final String SERVER_LATENCIES_NAME = "server_latencies"; + static final String FIRST_RESPONSE_LATENCIES_NAME = "first_response_latencies"; + static final String APPLICATION_BLOCKING_LATENCIES_NAME = "application_latencies"; + static final String CLIENT_BLOCKING_LATENCIES_NAME = "throttling_latencies"; + + private static final Aggregation AGGREGATION_WITH_MILLIS_HISTOGRAM = + Aggregation.explicitBucketHistogram( + ImmutableList.of( + 0.0, 0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0, + 16.0, 20.0, 25.0, 30.0, 40.0, 50.0, 65.0, 80.0, 100.0, 130.0, 160.0, 200.0, 250.0, + 300.0, 400.0, 500.0, 650.0, 800.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0, 50000.0, + 100000.0)); + + static final String SCOPE = "bigtable.googleapis.com"; + + static final InstrumentSelector OPERATION_LATENCIES_SELECTOR = + InstrumentSelector.builder() + .setName(OPERATION_LATENCIES_NAME) + .setMeterName(SCOPE) + .setType(InstrumentType.HISTOGRAM) + .setUnit("ms") + .build(); + static final InstrumentSelector ATTEMPT_LATENCIES_SELECTOR = + InstrumentSelector.builder() + .setName(ATTEMPT_LATENCIES_NAME) + .setMeterName(SCOPE) + .setType(InstrumentType.HISTOGRAM) + .setUnit("ms") + .build(); + + static final InstrumentSelector RETRY_COUNT_SELECTOR = + InstrumentSelector.builder() + .setName(RETRY_COUNT_NAME) + .setMeterName(SCOPE) + .setType(InstrumentType.COUNTER) + .setUnit("1") + .build(); + + static final InstrumentSelector SERVER_LATENCIES_SELECTOR = + InstrumentSelector.builder() + .setName(SERVER_LATENCIES_NAME) + .setMeterName(SCOPE) + .setType(InstrumentType.HISTOGRAM) + .setUnit("ms") + .build(); + static final InstrumentSelector FIRST_RESPONSE_LATENCIES_SELECTOR = + InstrumentSelector.builder() + .setName(FIRST_RESPONSE_LATENCIES_NAME) + .setMeterName(SCOPE) + .setType(InstrumentType.HISTOGRAM) + .setUnit("ms") + .build(); + static final InstrumentSelector CLIENT_BLOCKING_LATENCIES_SELECTOR = + InstrumentSelector.builder() + .setName(CLIENT_BLOCKING_LATENCIES_NAME) + .setMeterName(SCOPE) + .setType(InstrumentType.HISTOGRAM) + .setUnit("ms") + .build(); + static final InstrumentSelector APPLICATION_BLOCKING_LATENCIES_SELECTOR = + InstrumentSelector.builder() + .setName(APPLICATION_BLOCKING_LATENCIES_NAME) + .setMeterName(SCOPE) + .setType(InstrumentType.HISTOGRAM) + .setUnit("ms") + .build(); + static final InstrumentSelector CONNECTIVITY_ERROR_COUNT_SELECTOR = + InstrumentSelector.builder() + .setName(CONNECTIVITY_ERROR_COUNT_NAME) + .setMeterName(SCOPE) + .setType(InstrumentType.COUNTER) + .setUnit("1") + .build(); + + static final View OPERATION_LATENCIES_VIEW = + View.builder() + .setName("bigtable.googleapis.com/internal/client/operation_latencies") + .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) + .build(); + static final View ATTEMPT_LATENCIES_VIEW = + View.builder() + .setName("bigtable.googleapis.com/internal/client/attempt_latencies") + .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) + .build(); + static final View SERVER_LATENCIES_VIEW = + View.builder() + .setName("bigtable.googleapis.com/internal/client/server_latencies") + .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) + .build(); + static final View FIRST_RESPONSE_LATENCIES_VIEW = + View.builder() + .setName("bigtable.googleapis.com/internal/client/first_response_latencies") + .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) + .build(); + static final View APPLICATION_BLOCKING_LATENCIES_VIEW = + View.builder() + .setName("bigtable.googleapis.com/internal/client/application_latencies") + .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) + .build(); + static final View CLIENT_BLOCKING_LATENCIES_VIEW = + View.builder() + .setName("bigtable.googleapis.com/internal/client/throttling_latencies") + .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) + .build(); + static final View RETRY_COUNT_VIEW = + View.builder() + .setName("bigtable.googleapis.com/internal/client/retry_count") + .setAggregation(Aggregation.sum()) + .build(); + static final View CONNECTIVITY_ERROR_COUNT_VIEW = + View.builder() + .setName("bigtable.googleapis.com/internal/client/connectivity_error_count") + .setAggregation(Aggregation.sum()) + .build(); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java index a8b8148d3e..41361d8d80 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java @@ -16,13 +16,19 @@ package com.google.cloud.bigtable.data.v2.stub.metrics; import static com.google.api.gax.tracing.ApiTracerFactory.OperationType; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLUSTER_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.METHOD; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.STATUS; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.STREAMING; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.TABLE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ZONE_ID; import com.google.api.gax.retrying.ServerStreamingAttemptException; import com.google.api.gax.tracing.SpanName; -import com.google.cloud.bigtable.stats.StatsRecorderWrapper; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Stopwatch; import com.google.common.math.IntMath; +import io.opentelemetry.api.common.Attributes; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -37,11 +43,12 @@ */ class BuiltinMetricsTracer extends BigtableTracer { - private final StatsRecorderWrapper recorder; - + private static final String NAME = "java-bigtable"; private final OperationType operationType; private final SpanName spanName; + private final BigtableMetricsRecorder instruments; + // Operation level metrics private final AtomicBoolean opFinished = new AtomicBoolean(); private final Stopwatch operationTimer = Stopwatch.createStarted(); @@ -73,12 +80,20 @@ class BuiltinMetricsTracer extends BigtableTracer { private AtomicLong totalClientBlockingTime = new AtomicLong(0); - @VisibleForTesting + private final Attributes baseAttributes; + + private Long serverLatencies = null; + BuiltinMetricsTracer( - OperationType operationType, SpanName spanName, StatsRecorderWrapper recorder) { + OperationType operationType, + SpanName spanName, + BigtableMetricsRecorder instruments, + Attributes attributes) { this.operationType = operationType; this.spanName = spanName; - this.recorder = recorder; + + this.instruments = instruments; + this.baseAttributes = attributes; } @Override @@ -203,13 +218,8 @@ public int getAttempt() { @Override public void recordGfeMetadata(@Nullable Long latency, @Nullable Throwable throwable) { - // Record the metrics and put in the map after the attempt is done, so we can have cluster and - // zone information if (latency != null) { - recorder.putGfeLatencies(latency); - recorder.putGfeMissingHeaders(0); - } else { - recorder.putGfeMissingHeaders(1); + serverLatencies = latency; } } @@ -239,26 +249,44 @@ private void recordOperationCompletion(@Nullable Throwable status) { return; } operationTimer.stop(); + + boolean isStreaming = operationType == OperationType.ServerStreaming; + String statusStr = Util.extractStatus(status); + + Attributes attributes = + baseAttributes + .toBuilder() + .put(TABLE_ID, tableId) + .put(CLUSTER_ID, cluster) + .put(ZONE_ID, zone) + .put(METHOD, spanName.toString()) + .put(CLIENT_NAME, NAME) + .build(); + long operationLatency = operationTimer.elapsed(TimeUnit.MILLISECONDS); long operationLatencyNano = operationTimer.elapsed(TimeUnit.NANOSECONDS); // Only record when retry count is greater than 0 so the retry // graph will be less confusing if (attemptCount > 1) { - recorder.putRetryCount(attemptCount - 1); + instruments.recordRetryCount( + attemptCount - 1, attributes.toBuilder().put(STATUS, statusStr).build()); } // serverLatencyTimer should already be stopped in recordAttemptCompletion - recorder.putOperationLatencies(operationLatency); - recorder.putApplicationLatencies( - Duration.ofNanos(operationLatencyNano - totalServerLatencyNano.get()).toMillis()); + instruments.recordOperationLatencies( + operationLatency, + attributes.toBuilder().put(STREAMING, isStreaming).put(STATUS, statusStr).build()); + instruments.recordApplicationBlockingLatencies( + Duration.ofNanos(operationLatencyNano - totalServerLatencyNano.get()).toMillis(), + attributes); if (operationType == OperationType.ServerStreaming && spanName.getMethodName().equals("ReadRows")) { - recorder.putFirstResponseLatencies(firstResponsePerOpTimer.elapsed(TimeUnit.MILLISECONDS)); + instruments.recordFirstResponseLatencies( + firstResponsePerOpTimer.elapsed(TimeUnit.MILLISECONDS), + attributes.toBuilder().put(STATUS, Util.extractStatus(status)).build()); } - - recorder.recordOperation(Util.extractStatus(status), tableId, zone, cluster); } private void recordAttemptCompletion(@Nullable Throwable status) { @@ -273,7 +301,19 @@ private void recordAttemptCompletion(@Nullable Throwable status) { } } - recorder.putClientBlockingLatencies(totalClientBlockingTime.get()); + boolean isStreaming = operationType == OperationType.ServerStreaming; + + Attributes attributes = + baseAttributes + .toBuilder() + .put(TABLE_ID, tableId) + .put(CLUSTER_ID, cluster) + .put(ZONE_ID, zone) + .put(METHOD, spanName.toString()) + .put(CLIENT_NAME, NAME) + .build(); + + instruments.recordClientBlockingLatencies(totalClientBlockingTime.get(), attributes); // Patch the status until it's fixed in gax. When an attempt failed, // it'll throw a ServerStreamingAttemptException. Unwrap the exception @@ -282,7 +322,20 @@ private void recordAttemptCompletion(@Nullable Throwable status) { status = status.getCause(); } - recorder.putAttemptLatencies(attemptTimer.elapsed(TimeUnit.MILLISECONDS)); - recorder.recordAttempt(Util.extractStatus(status), tableId, zone, cluster); + String statusStr = Util.extractStatus(status); + + instruments.recordAttemptLatencies( + attemptTimer.elapsed(TimeUnit.MILLISECONDS), + attributes.toBuilder().put(STREAMING, isStreaming).put(STATUS, statusStr).build()); + + if (serverLatencies != null) { + instruments.recordServerLatencies( + serverLatencies, attributes.toBuilder().put(STATUS, statusStr).build()); + instruments.recordConnectivityErrorCount( + 0, attributes.toBuilder().put(STATUS, statusStr).build()); + } else { + instruments.recordConnectivityErrorCount( + 1, attributes.toBuilder().put(STATUS, statusStr).build()); + } } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java index 794997071d..aab9433bbd 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java @@ -15,36 +15,94 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APP_PROFILE; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.INSTANCE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.PROJECT_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SCOPE; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_VIEW; + import com.google.api.core.InternalApi; import com.google.api.gax.tracing.ApiTracer; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.BaseApiTracerFactory; import com.google.api.gax.tracing.SpanName; -import com.google.cloud.bigtable.stats.StatsWrapper; -import com.google.common.collect.ImmutableMap; +import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.resources.Resource; +import java.io.IOException; /** - * {@link ApiTracerFactory} that will generate OpenCensus metrics by using the {@link ApiTracer} + * {@link ApiTracerFactory} that will generate OpenTelemetry metrics by using the {@link ApiTracer} * api. */ @InternalApi("For internal use only") public class BuiltinMetricsTracerFactory extends BaseApiTracerFactory { + private final Attributes attributes; + private final BigtableMetricsRecorder bigtableMetricsRecorder; - private final ImmutableMap statsAttributes; - - public static BuiltinMetricsTracerFactory create(ImmutableMap statsAttributes) { - return new BuiltinMetricsTracerFactory(statsAttributes); + public static BuiltinMetricsTracerFactory create(EnhancedBigtableStubSettings settings) + throws IOException { + return new BuiltinMetricsTracerFactory(settings); } - private BuiltinMetricsTracerFactory(ImmutableMap statsAttributes) { - this.statsAttributes = statsAttributes; + BuiltinMetricsTracerFactory(EnhancedBigtableStubSettings settings) throws IOException { + this.attributes = + Attributes.builder() + .put(PROJECT_ID, settings.getProjectId()) + .put(INSTANCE_ID, settings.getInstanceId()) + .put(APP_PROFILE, settings.getAppProfileId()) + .build(); + + if (settings.isBuiltinMetricsEnabled()) { + Resource resource = Resource.create(attributes); + MetricExporter metricExporter = + BigtableCloudMonitoringExporter.create( + settings.getProjectId(), settings.getCredentialsProvider().getCredentials()); + + SdkMeterProvider meterProvider = + SdkMeterProvider.builder() + .setResource(resource) + .registerMetricReader(PeriodicMetricReader.create(metricExporter)) + .registerView(OPERATION_LATENCIES_SELECTOR, OPERATION_LATENCIES_VIEW) + .registerView(ATTEMPT_LATENCIES_SELECTOR, ATTEMPT_LATENCIES_VIEW) + .registerView(SERVER_LATENCIES_SELECTOR, SERVER_LATENCIES_VIEW) + .registerView(FIRST_RESPONSE_LATENCIES_SELECTOR, FIRST_RESPONSE_LATENCIES_VIEW) + .registerView( + APPLICATION_BLOCKING_LATENCIES_SELECTOR, APPLICATION_BLOCKING_LATENCIES_VIEW) + .registerView(CLIENT_BLOCKING_LATENCIES_SELECTOR, CLIENT_BLOCKING_LATENCIES_VIEW) + .registerView(RETRY_COUNT_SELECTOR, RETRY_COUNT_VIEW) + .registerView(CONNECTIVITY_ERROR_COUNT_SELECTOR, CONNECTIVITY_ERROR_COUNT_VIEW) + .build(); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build(); + bigtableMetricsRecorder = new BuiltinInMetricsRecorder(openTelemetry.getMeter(SCOPE)); + } else { + bigtableMetricsRecorder = new BigtableMetricsRecorder(); + } } @Override public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType operationType) { - return new BuiltinMetricsTracer( - operationType, - spanName, - StatsWrapper.createRecorder(operationType, spanName, statsAttributes)); + return new BuiltinMetricsTracer(operationType, spanName, bigtableMetricsRecorder, attributes); } } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java index fbd6442e0c..262c583d67 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java @@ -799,6 +799,7 @@ public void isRefreshingChannelFalseValueTest() { "generateInitialChangeStreamPartitionsSettings", "readChangeStreamSettings", "pingAndWarmSettings", + "isBuiltinMetricsEnabled", }; @Test diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java index 3bc283a7f7..23d36a39e3 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java @@ -15,7 +15,13 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import static com.google.api.gax.tracing.ApiTracerFactory.OperationType; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLUSTER_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.METHOD; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.STATUS; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.STREAMING; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.TABLE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ZONE_ID; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.timeout; @@ -34,6 +40,7 @@ import com.google.api.gax.rpc.NotFoundException; import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.StreamController; +import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.SpanName; import com.google.bigtable.v2.BigtableGrpc; import com.google.bigtable.v2.MutateRowRequest; @@ -51,7 +58,6 @@ import com.google.cloud.bigtable.data.v2.models.RowMutationEntry; import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub; import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings; -import com.google.cloud.bigtable.stats.StatsRecorderWrapper; import com.google.common.base.Stopwatch; import com.google.common.collect.Range; import com.google.protobuf.ByteString; @@ -74,6 +80,7 @@ import io.grpc.StatusRuntimeException; import io.grpc.stub.ServerCallStreamObserver; import io.grpc.stub.StreamObserver; +import io.opentelemetry.api.common.Attributes; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -81,6 +88,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -89,11 +97,9 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.ArgumentCaptor; -import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import org.mockito.stubbing.Answer; import org.threeten.bp.Duration; @RunWith(JUnit4.class) @@ -101,7 +107,7 @@ public class BuiltinMetricsTracerTest { private static final String PROJECT_ID = "fake-project"; private static final String INSTANCE_ID = "fake-instance"; private static final String APP_PROFILE_ID = "default"; - private static final String TABLE_ID = "fake-table"; + private static final String TABLE = "fake-table"; private static final String BAD_TABLE_ID = "non-exist-table"; private static final String ZONE = "us-west-1"; @@ -119,16 +125,14 @@ public class BuiltinMetricsTracerTest { private EnhancedBigtableStub stub; - @Mock private BuiltinMetricsTracerFactory mockFactory; - @Mock private StatsRecorderWrapper statsRecorderWrapper; + @Mock private BigtableMetricsRecorder mockInstruments; - @Captor private ArgumentCaptor status; - @Captor private ArgumentCaptor tableId; - @Captor private ArgumentCaptor zone; - @Captor private ArgumentCaptor cluster; + @Mock private BuiltinMetricsTracerFactory mockFactory; private int batchElementCount = 2; + private Attributes baseAttributes; + @Before public void setUp() throws Exception { // Add an interceptor to add server-timing in headers @@ -211,6 +215,7 @@ public void sendMessage(ReqT message) { .setMaxOutstandingRequestBytes(1000L) .build()) .build()); + stubSettingsBuilder.setTracerFactory(mockFactory); InstantiatingGrpcChannelProvider.Builder channelProvider = @@ -232,6 +237,13 @@ public void sendMessage(ReqT message) { EnhancedBigtableStubSettings stubSettings = stubSettingsBuilder.build(); stub = new EnhancedBigtableStub(stubSettings, ClientContext.create(stubSettings)); + + baseAttributes = + Attributes.builder() + .put(BuiltinMetricsAttributes.PROJECT_ID, PROJECT_ID) + .put(BuiltinMetricsAttributes.INSTANCE_ID, INSTANCE_ID) + .put(BuiltinMetricsAttributes.APP_PROFILE, APP_PROFILE_ID) + .build(); } @After @@ -243,85 +255,132 @@ public void tearDown() { @Test public void testReadRowsOperationLatencies() { when(mockFactory.newTracer(any(), any(), any())) - .thenAnswer( - (Answer) - invocationOnMock -> - new BuiltinMetricsTracer( - OperationType.ServerStreaming, - SpanName.of("Bigtable", "ReadRows"), - statsRecorderWrapper)); - ArgumentCaptor operationLatency = ArgumentCaptor.forClass(Long.class); + .thenReturn( + new BuiltinMetricsTracer( + ApiTracerFactory.OperationType.ServerStreaming, + SpanName.of("Bigtable", "ReadRows"), + mockInstruments, + baseAttributes)); Stopwatch stopwatch = Stopwatch.createStarted(); - Lists.newArrayList(stub.readRowsCallable().call(Query.create(TABLE_ID)).iterator()); + Lists.newArrayList(stub.readRowsCallable().call(Query.create(TABLE)).iterator()); long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS); - verify(statsRecorderWrapper).putOperationLatencies(operationLatency.capture()); - // verify record operation is only called once - verify(statsRecorderWrapper) - .recordOperation(status.capture(), tableId.capture(), zone.capture(), cluster.capture()); - - assertThat(operationLatency.getValue()).isIn(Range.closed(SERVER_LATENCY, elapsed)); - assertThat(status.getAllValues()).containsExactly("OK"); - assertThat(tableId.getAllValues()).containsExactly(TABLE_ID); - assertThat(zone.getAllValues()).containsExactly(ZONE); - assertThat(cluster.getAllValues()).containsExactly(CLUSTER); + ArgumentCaptor value = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor attributes = ArgumentCaptor.forClass(Attributes.class); + verify(mockInstruments).recordOperationLatencies(value.capture(), attributes.capture()); + + // TODO why is it operation latency always elapsed + 1? + assertThat(value.getValue()).isIn(Range.closed(SERVER_LATENCY, elapsed + 1)); + assertThat(attributes.getValue().get(STATUS)).isEqualTo("OK"); + assertThat(attributes.getValue().get(TABLE_ID)).isEqualTo(TABLE); + assertThat(attributes.getValue().get(ZONE_ID)).isEqualTo(ZONE); + assertThat(attributes.getValue().get(CLUSTER_ID)).isEqualTo(CLUSTER); + assertThat(attributes.getValue().get(METHOD)).isEqualTo("Bigtable.ReadRows"); + assertThat(attributes.getValue().get(STREAMING)).isTrue(); } @Test public void testGfeMetrics() { when(mockFactory.newTracer(any(), any(), any())) - .thenAnswer( - (Answer) - invocationOnMock -> - new BuiltinMetricsTracer( - OperationType.ServerStreaming, - SpanName.of("Bigtable", "ReadRows"), - statsRecorderWrapper)); - ArgumentCaptor gfeLatency = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor gfeMissingHeaders = ArgumentCaptor.forClass(Long.class); - - Lists.newArrayList(stub.readRowsCallable().call(Query.create(TABLE_ID))); + .thenReturn( + new BuiltinMetricsTracer( + ApiTracerFactory.OperationType.ServerStreaming, + SpanName.of("Bigtable", "ReadRows"), + mockInstruments, + baseAttributes)); + + Lists.newArrayList(stub.readRowsCallable().call(Query.create(TABLE))); // Verify record attempt are called multiple times - verify(statsRecorderWrapper, times(fakeService.getAttemptCounter().get())) - .recordAttempt(status.capture(), tableId.capture(), zone.capture(), cluster.capture()); + ArgumentCaptor value = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor attributes = ArgumentCaptor.forClass(Attributes.class); + + verify(mockInstruments, times(fakeService.getAttemptCounter().get())) + .recordAttemptLatencies(value.capture(), attributes.capture()); // The request was retried and gfe latency is only recorded in the retry attempt - verify(statsRecorderWrapper).putGfeLatencies(gfeLatency.capture()); - assertThat(gfeLatency.getValue()).isEqualTo(FAKE_SERVER_TIMING); + ArgumentCaptor serverLatencies = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor serverLatenciesAttributes = + ArgumentCaptor.forClass(Attributes.class); + + verify(mockInstruments) + .recordServerLatencies(serverLatencies.capture(), serverLatenciesAttributes.capture()); + assertThat(serverLatencies.getValue()).isEqualTo(FAKE_SERVER_TIMING); + assertThat( + serverLatenciesAttributes.getAllValues().stream() + .map(a -> a.get(STATUS)) + .collect(Collectors.toList())) + .containsExactly("OK"); + assertThat( + serverLatenciesAttributes.getAllValues().stream() + .map(a -> a.get(TABLE_ID)) + .collect(Collectors.toList())) + .containsExactly(TABLE); + assertThat( + serverLatenciesAttributes.getAllValues().stream() + .map(a -> a.get(ZONE_ID)) + .collect(Collectors.toList())) + .containsExactly(ZONE); + assertThat( + serverLatenciesAttributes.getAllValues().stream() + .map(a -> a.get(CLUSTER_ID)) + .collect(Collectors.toList())) + .containsExactly(CLUSTER); // The first time the request was retried, it'll increment missing header counter - verify(statsRecorderWrapper, times(fakeService.getAttemptCounter().get())) - .putGfeMissingHeaders(gfeMissingHeaders.capture()); - assertThat(gfeMissingHeaders.getAllValues()).containsExactly(1L, 0L); - - assertThat(status.getAllValues()).containsExactly("UNAVAILABLE", "OK"); - assertThat(tableId.getAllValues()).containsExactly(TABLE_ID, TABLE_ID); - assertThat(zone.getAllValues()).containsExactly("global", ZONE); - assertThat(cluster.getAllValues()).containsExactly("unspecified", CLUSTER); + ArgumentCaptor connectivityErrorCount = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor errorCountAttributes = ArgumentCaptor.forClass(Attributes.class); + + verify(mockInstruments, times(fakeService.getAttemptCounter().get())) + .recordConnectivityErrorCount( + connectivityErrorCount.capture(), errorCountAttributes.capture()); + assertThat(connectivityErrorCount.getAllValues()).containsExactly(1L, 0L); + + assertThat( + errorCountAttributes.getAllValues().stream() + .map(a -> a.get(STATUS)) + .collect(Collectors.toList())) + .containsExactly("UNAVAILABLE", "OK"); + assertThat( + errorCountAttributes.getAllValues().stream() + .map(a -> a.get(TABLE_ID)) + .collect(Collectors.toList())) + .containsExactly(TABLE, TABLE); + assertThat( + errorCountAttributes.getAllValues().stream() + .map(a -> a.get(ZONE_ID)) + .collect(Collectors.toList())) + .containsExactly("global", ZONE); + assertThat( + errorCountAttributes.getAllValues().stream() + .map(a -> a.get(CLUSTER_ID)) + .collect(Collectors.toList())) + .containsExactly("unspecified", CLUSTER); } @Test public void testReadRowsApplicationLatencyWithAutoFlowControl() throws Exception { when(mockFactory.newTracer(any(), any(), any())) - .thenAnswer( - (Answer) - invocationOnMock -> - new BuiltinMetricsTracer( - OperationType.ServerStreaming, - SpanName.of("Bigtable", "ReadRows"), - statsRecorderWrapper)); + .thenReturn( + new BuiltinMetricsTracer( + ApiTracerFactory.OperationType.ServerStreaming, + SpanName.of("Bigtable", "ReadRows"), + mockInstruments, + baseAttributes)); ArgumentCaptor applicationLatency = ArgumentCaptor.forClass(Long.class); ArgumentCaptor operationLatency = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor opLatencyAttributes = ArgumentCaptor.forClass(Attributes.class); + ArgumentCaptor applicationLatencyAttributes = + ArgumentCaptor.forClass(Attributes.class); final SettableApiFuture future = SettableApiFuture.create(); final AtomicInteger counter = new AtomicInteger(0); // For auto flow control, application latency is the time application spent in onResponse. stub.readRowsCallable() .call( - Query.create(TABLE_ID), + Query.create(TABLE), new ResponseObserver() { @Override public void onStart(StreamController streamController) {} @@ -347,10 +406,11 @@ public void onComplete() { }); future.get(); - verify(statsRecorderWrapper).putApplicationLatencies(applicationLatency.capture()); - verify(statsRecorderWrapper).putOperationLatencies(operationLatency.capture()); - verify(statsRecorderWrapper) - .recordOperation(status.capture(), tableId.capture(), zone.capture(), cluster.capture()); + verify(mockInstruments) + .recordApplicationBlockingLatencies( + applicationLatency.capture(), applicationLatencyAttributes.capture()); + verify(mockInstruments) + .recordOperationLatencies(operationLatency.capture(), opLatencyAttributes.capture()); assertThat(counter.get()).isEqualTo(fakeService.getResponseCounter().get()); assertThat(applicationLatency.getValue()).isAtLeast(APPLICATION_LATENCY * counter.get()); @@ -361,19 +421,22 @@ public void onComplete() { @Test public void testReadRowsApplicationLatencyWithManualFlowControl() throws Exception { when(mockFactory.newTracer(any(), any(), any())) - .thenAnswer( - (Answer) - invocationOnMock -> - new BuiltinMetricsTracer( - OperationType.ServerStreaming, - SpanName.of("Bigtable", "ReadRows"), - statsRecorderWrapper)); + .thenReturn( + new BuiltinMetricsTracer( + ApiTracerFactory.OperationType.ServerStreaming, + SpanName.of("Bigtable", "ReadRows"), + mockInstruments, + baseAttributes)); ArgumentCaptor applicationLatency = ArgumentCaptor.forClass(Long.class); ArgumentCaptor operationLatency = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor opLatencyAttributes = ArgumentCaptor.forClass(Attributes.class); + ArgumentCaptor applicationLatencyAttributes = + ArgumentCaptor.forClass(Attributes.class); + int counter = 0; - Iterator rows = stub.readRowsCallable().call(Query.create(TABLE_ID)).iterator(); + Iterator rows = stub.readRowsCallable().call(Query.create(TABLE)).iterator(); while (rows.hasNext()) { counter++; @@ -381,13 +444,14 @@ public void testReadRowsApplicationLatencyWithManualFlowControl() throws Excepti rows.next(); } - verify(statsRecorderWrapper).putApplicationLatencies(applicationLatency.capture()); - verify(statsRecorderWrapper).putOperationLatencies(operationLatency.capture()); - verify(statsRecorderWrapper) - .recordOperation(status.capture(), tableId.capture(), zone.capture(), cluster.capture()); + verify(mockInstruments) + .recordApplicationBlockingLatencies( + applicationLatency.capture(), applicationLatencyAttributes.capture()); + verify(mockInstruments) + .recordOperationLatencies(operationLatency.capture(), opLatencyAttributes.capture()); - // For manual flow control, the last application latency shouldn't count, because at that point - // the server already sent back all the responses. + // For manual flow control, the last application latency shouldn't count, because at that + // point the server already sent back all the responses. assertThat(counter).isEqualTo(fakeService.getResponseCounter().get()); assertThat(applicationLatency.getValue()) .isAtLeast(APPLICATION_LATENCY * (counter - 1) - SERVER_LATENCY); @@ -398,25 +462,26 @@ public void testReadRowsApplicationLatencyWithManualFlowControl() throws Excepti @Test public void testRetryCount() { when(mockFactory.newTracer(any(), any(), any())) - .thenAnswer( - (Answer) - invocationOnMock -> - new BuiltinMetricsTracer( - OperationType.ServerStreaming, - SpanName.of("Bigtable", "MutateRow"), - statsRecorderWrapper)); + .thenReturn( + new BuiltinMetricsTracer( + ApiTracerFactory.OperationType.ServerStreaming, + SpanName.of("Bigtable", "ReadRows"), + mockInstruments, + baseAttributes)); - ArgumentCaptor retryCount = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor retryCount = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor retryCountAttribute = ArgumentCaptor.forClass(Attributes.class); stub.mutateRowCallable() - .call(RowMutation.create(TABLE_ID, "random-row").setCell("cf", "q", "value")); + .call(RowMutation.create(TABLE, "random-row").setCell("cf", "q", "value")); // In TracedUnaryCallable, we create a future and add a TraceFinisher to the callback. Main // thread is blocked on waiting for the future to be completed. When onComplete is called on // the grpc thread, the future is completed, however we might not have enough time for - // TraceFinisher to run. Add a 1 second time out to wait for the callback. This shouldn't have - // any impact on production code. - verify(statsRecorderWrapper, timeout(1000)).putRetryCount(retryCount.capture()); + // TraceFinisher to run. Add a 1 second time out to wait for the callback. This shouldn't + // have any impact on production code. + verify(mockInstruments, timeout(1000)) + .recordRetryCount(retryCount.capture(), retryCountAttribute.capture()); assertThat(retryCount.getValue()).isEqualTo(fakeService.getAttemptCounter().get() - 1); } @@ -426,22 +491,70 @@ public void testMutateRowAttemptsTagValues() { when(mockFactory.newTracer(any(), any(), any())) .thenReturn( new BuiltinMetricsTracer( - OperationType.Unary, SpanName.of("Bigtable", "MutateRow"), statsRecorderWrapper)); + ApiTracerFactory.OperationType.Unary, + SpanName.of("Bigtable", "MutateRow"), + mockInstruments, + baseAttributes)); stub.mutateRowCallable() - .call(RowMutation.create(TABLE_ID, "random-row").setCell("cf", "q", "value")); + .call(RowMutation.create(TABLE, "random-row").setCell("cf", "q", "value")); + + ArgumentCaptor value = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor attributes = ArgumentCaptor.forClass(Attributes.class); // Set a timeout to reduce flakiness of this test. BasicRetryingFuture will set // attempt succeeded and set the response which will call complete() in AbstractFuture which // calls releaseWaiters(). onOperationComplete() is called in TracerFinisher which will be - // called after the mutateRow call is returned. So there's a race between when the call returns - // and when the record() is called in onOperationCompletion(). - verify(statsRecorderWrapper, timeout(50).times(fakeService.getAttemptCounter().get())) - .recordAttempt(status.capture(), tableId.capture(), zone.capture(), cluster.capture()); - assertThat(zone.getAllValues()).containsExactly("global", "global", ZONE); - assertThat(cluster.getAllValues()).containsExactly("unspecified", "unspecified", CLUSTER); - assertThat(status.getAllValues()).containsExactly("UNAVAILABLE", "UNAVAILABLE", "OK"); - assertThat(tableId.getAllValues()).containsExactly(TABLE_ID, TABLE_ID, TABLE_ID); + // called after the mutateRow call is returned. So there's a race between when the call + // returns and when the record() is called in onOperationCompletion(). + verify(mockInstruments, timeout(50).times(fakeService.getAttemptCounter().get())) + .recordAttemptLatencies(value.capture(), attributes.capture()); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(BuiltinMetricsAttributes.PROJECT_ID)) + .collect(Collectors.toList())) + .containsExactly(PROJECT_ID, PROJECT_ID, PROJECT_ID); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(BuiltinMetricsAttributes.INSTANCE_ID)) + .collect(Collectors.toList())) + .containsExactly(INSTANCE_ID, INSTANCE_ID, INSTANCE_ID); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(BuiltinMetricsAttributes.APP_PROFILE)) + .collect(Collectors.toList())) + .containsExactly(APP_PROFILE_ID, APP_PROFILE_ID, APP_PROFILE_ID); + assertThat( + attributes.getAllValues().stream().map(a -> a.get(STATUS)).collect(Collectors.toList())) + .containsExactly("UNAVAILABLE", "UNAVAILABLE", "OK"); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(TABLE_ID)) + .collect(Collectors.toList())) + .containsExactly(TABLE, TABLE, TABLE); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(ZONE_ID)) + .collect(Collectors.toList())) + .containsExactly("global", "global", ZONE); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(CLUSTER_ID)) + .collect(Collectors.toList())) + .containsExactly("unspecified", "unspecified", CLUSTER); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(CLIENT_NAME)) + .collect(Collectors.toList())) + .containsExactly("java-bigtable", "java-bigtable", "java-bigtable"); + assertThat( + attributes.getAllValues().stream().map(a -> a.get(METHOD)).collect(Collectors.toList())) + .containsExactly("Bigtable.MutateRow", "Bigtable.MutateRow", "Bigtable.MutateRow"); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(STREAMING)) + .collect(Collectors.toList())) + .containsExactly(false, false, false); } @Test @@ -449,22 +562,69 @@ public void testReadRowsAttemptsTagValues() { when(mockFactory.newTracer(any(), any(), any())) .thenReturn( new BuiltinMetricsTracer( - OperationType.ServerStreaming, + ApiTracerFactory.OperationType.ServerStreaming, SpanName.of("Bigtable", "ReadRows"), - statsRecorderWrapper)); + mockInstruments, + baseAttributes)); Lists.newArrayList(stub.readRowsCallable().call(Query.create("fake-table")).iterator()); + ArgumentCaptor value = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor attributes = ArgumentCaptor.forClass(Attributes.class); + // Set a timeout to reduce flakiness of this test. BasicRetryingFuture will set // attempt succeeded and set the response which will call complete() in AbstractFuture which // calls releaseWaiters(). onOperationComplete() is called in TracerFinisher which will be - // called after the mutateRow call is returned. So there's a race between when the call returns - // and when the record() is called in onOperationCompletion(). - verify(statsRecorderWrapper, timeout(50).times(fakeService.getAttemptCounter().get())) - .recordAttempt(status.capture(), tableId.capture(), zone.capture(), cluster.capture()); - assertThat(zone.getAllValues()).containsExactly("global", ZONE); - assertThat(cluster.getAllValues()).containsExactly("unspecified", CLUSTER); - assertThat(status.getAllValues()).containsExactly("UNAVAILABLE", "OK"); + // called after the mutateRow call is returned. So there's a race between when the call + // returns and when the record() is called in onOperationCompletion(). + verify(mockInstruments, timeout(50).times(fakeService.getAttemptCounter().get())) + .recordAttemptLatencies(value.capture(), attributes.capture()); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(BuiltinMetricsAttributes.PROJECT_ID)) + .collect(Collectors.toList())) + .containsExactly(PROJECT_ID, PROJECT_ID); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(BuiltinMetricsAttributes.INSTANCE_ID)) + .collect(Collectors.toList())) + .containsExactly(INSTANCE_ID, INSTANCE_ID); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(BuiltinMetricsAttributes.APP_PROFILE)) + .collect(Collectors.toList())) + .containsExactly(APP_PROFILE_ID, APP_PROFILE_ID); + assertThat( + attributes.getAllValues().stream().map(a -> a.get(STATUS)).collect(Collectors.toList())) + .containsExactly("UNAVAILABLE", "OK"); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(TABLE_ID)) + .collect(Collectors.toList())) + .containsExactly(TABLE, TABLE); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(ZONE_ID)) + .collect(Collectors.toList())) + .containsExactly("global", ZONE); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(CLUSTER_ID)) + .collect(Collectors.toList())) + .containsExactly("unspecified", CLUSTER); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(CLIENT_NAME)) + .collect(Collectors.toList())) + .containsExactly("java-bigtable", "java-bigtable"); + assertThat( + attributes.getAllValues().stream().map(a -> a.get(METHOD)).collect(Collectors.toList())) + .containsExactly("Bigtable.ReadRows", "Bigtable.ReadRows"); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(STREAMING)) + .collect(Collectors.toList())) + .containsExactly(true, true); } @Test @@ -472,16 +632,22 @@ public void testBatchBlockingLatencies() throws InterruptedException { when(mockFactory.newTracer(any(), any(), any())) .thenReturn( new BuiltinMetricsTracer( - OperationType.Unary, SpanName.of("Bigtable", "MutateRows"), statsRecorderWrapper)); - try (Batcher batcher = stub.newMutateRowsBatcher(TABLE_ID, null)) { + ApiTracerFactory.OperationType.ServerStreaming, + SpanName.of("Bigtable", "MutateRows"), + mockInstruments, + baseAttributes)); + + try (Batcher batcher = stub.newMutateRowsBatcher(TABLE, null)) { for (int i = 0; i < 6; i++) { batcher.add(RowMutationEntry.create("key").setCell("f", "q", "v")); } int expectedNumRequests = 6 / batchElementCount; ArgumentCaptor throttledTime = ArgumentCaptor.forClass(Long.class); - verify(statsRecorderWrapper, timeout(1000).times(expectedNumRequests)) - .putClientBlockingLatencies(throttledTime.capture()); + ArgumentCaptor attributes = ArgumentCaptor.forClass(Attributes.class); + + verify(mockInstruments, timeout(5000).times(expectedNumRequests)) + .recordClientBlockingLatencies(throttledTime.capture(), attributes.capture()); // Adding the first 2 elements should not get throttled since the batch is empty assertThat(throttledTime.getAllValues().get(0)).isEqualTo(0); @@ -489,6 +655,49 @@ public void testBatchBlockingLatencies() throws InterruptedException { // Blocking latency should be around server latency. assertThat(throttledTime.getAllValues().get(1)).isAtLeast(SERVER_LATENCY - 10); assertThat(throttledTime.getAllValues().get(2)).isAtLeast(SERVER_LATENCY - 10); + + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(BuiltinMetricsAttributes.PROJECT_ID)) + .collect(Collectors.toList())) + .containsExactly(PROJECT_ID, PROJECT_ID, PROJECT_ID); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(BuiltinMetricsAttributes.INSTANCE_ID)) + .collect(Collectors.toList())) + .containsExactly(INSTANCE_ID, INSTANCE_ID, INSTANCE_ID); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(BuiltinMetricsAttributes.APP_PROFILE)) + .collect(Collectors.toList())) + .containsExactly(APP_PROFILE_ID, APP_PROFILE_ID, APP_PROFILE_ID); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(TABLE_ID)) + .collect(Collectors.toList())) + .containsExactly(TABLE, TABLE, TABLE); + // TODO: the order of batched tracer unary callable seems to be incorrect, causing the first + // attempt returning global + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(ZONE_ID)) + .collect(Collectors.toList())) + .containsExactly("global", ZONE, ZONE); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(CLUSTER_ID)) + .collect(Collectors.toList())) + .containsExactly("unspecified", CLUSTER, CLUSTER); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(CLIENT_NAME)) + .collect(Collectors.toList())) + .containsExactly("java-bigtable", "java-bigtable", "java-bigtable"); + assertThat( + attributes.getAllValues().stream() + .map(a -> a.get(METHOD)) + .collect(Collectors.toList())) + .containsExactly("Bigtable.MutateRows", "Bigtable.MutateRows", "Bigtable.MutateRows"); } } @@ -497,16 +706,18 @@ public void testQueuedOnChannelServerStreamLatencies() throws InterruptedExcepti when(mockFactory.newTracer(any(), any(), any())) .thenReturn( new BuiltinMetricsTracer( - OperationType.ServerStreaming, + ApiTracerFactory.OperationType.ServerStreaming, SpanName.of("Bigtable", "ReadRows"), - statsRecorderWrapper)); + mockInstruments, + baseAttributes)); - stub.readRowsCallable().all().call(Query.create(TABLE_ID)); + stub.readRowsCallable().all().call(Query.create(TABLE)); ArgumentCaptor blockedTime = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor attributes = ArgumentCaptor.forClass(Attributes.class); - verify(statsRecorderWrapper, timeout(1000).times(fakeService.attemptCounter.get())) - .putClientBlockingLatencies(blockedTime.capture()); + verify(mockInstruments, timeout(1000).times(fakeService.attemptCounter.get())) + .recordClientBlockingLatencies(blockedTime.capture(), attributes.capture()); assertThat(blockedTime.getAllValues().get(1)).isAtLeast(CHANNEL_BLOCKING_LATENCY); } @@ -516,13 +727,18 @@ public void testQueuedOnChannelUnaryLatencies() throws InterruptedException { when(mockFactory.newTracer(any(), any(), any())) .thenReturn( new BuiltinMetricsTracer( - OperationType.Unary, SpanName.of("Bigtable", "MutateRow"), statsRecorderWrapper)); - stub.mutateRowCallable().call(RowMutation.create(TABLE_ID, "a-key").setCell("f", "q", "v")); + ApiTracerFactory.OperationType.ServerStreaming, + SpanName.of("Bigtable", "MutateRow"), + mockInstruments, + baseAttributes)); + + stub.mutateRowCallable().call(RowMutation.create(TABLE, "a-key").setCell("f", "q", "v")); ArgumentCaptor blockedTime = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor attributes = ArgumentCaptor.forClass(Attributes.class); - verify(statsRecorderWrapper, timeout(1000).times(fakeService.attemptCounter.get())) - .putClientBlockingLatencies(blockedTime.capture()); + verify(mockInstruments, timeout(1000).times(fakeService.attemptCounter.get())) + .recordClientBlockingLatencies(blockedTime.capture(), attributes.capture()); assertThat(blockedTime.getAllValues().get(1)).isAtLeast(CHANNEL_BLOCKING_LATENCY); assertThat(blockedTime.getAllValues().get(2)).isAtLeast(CHANNEL_BLOCKING_LATENCY); @@ -533,9 +749,10 @@ public void testPermanentFailure() { when(mockFactory.newTracer(any(), any(), any())) .thenReturn( new BuiltinMetricsTracer( - OperationType.ServerStreaming, + ApiTracerFactory.OperationType.ServerStreaming, SpanName.of("Bigtable", "ReadRows"), - statsRecorderWrapper)); + mockInstruments, + baseAttributes)); try { Lists.newArrayList(stub.readRowsCallable().call(Query.create(BAD_TABLE_ID)).iterator()); @@ -545,16 +762,57 @@ public void testPermanentFailure() { ArgumentCaptor attemptLatency = ArgumentCaptor.forClass(Long.class); ArgumentCaptor operationLatency = ArgumentCaptor.forClass(Long.class); - - verify(statsRecorderWrapper, timeout(50)).putAttemptLatencies(attemptLatency.capture()); - verify(statsRecorderWrapper, timeout(50)).putOperationLatencies(operationLatency.capture()); - verify(statsRecorderWrapper, timeout(50)) - .recordAttempt(status.capture(), tableId.capture(), zone.capture(), cluster.capture()); - - assertThat(status.getValue()).isEqualTo("NOT_FOUND"); - assertThat(tableId.getValue()).isEqualTo(BAD_TABLE_ID); - assertThat(cluster.getValue()).isEqualTo("unspecified"); - assertThat(zone.getValue()).isEqualTo("global"); + ArgumentCaptor attemptAttributes = ArgumentCaptor.forClass(Attributes.class); + ArgumentCaptor operationAttributes = ArgumentCaptor.forClass(Attributes.class); + + verify(mockInstruments, timeout(50)) + .recordAttemptLatencies(attemptLatency.capture(), attemptAttributes.capture()); + verify(mockInstruments, timeout(50)) + .recordOperationLatencies(operationLatency.capture(), operationAttributes.capture()); + + // verify attempt attributes + assertThat( + attemptAttributes.getAllValues().stream() + .map(a -> a.get(STATUS)) + .collect(Collectors.toList())) + .containsExactly("NOT_FOUND"); + assertThat( + attemptAttributes.getAllValues().stream() + .map(a -> a.get(TABLE_ID)) + .collect(Collectors.toList())) + .containsExactly(BAD_TABLE_ID); + assertThat( + attemptAttributes.getAllValues().stream() + .map(a -> a.get(ZONE_ID)) + .collect(Collectors.toList())) + .containsExactly("global"); + assertThat( + attemptAttributes.getAllValues().stream() + .map(a -> a.get(CLUSTER_ID)) + .collect(Collectors.toList())) + .containsExactly("unspecified"); + + // verify operation attributes + assertThat( + operationAttributes.getAllValues().stream() + .map(a -> a.get(STATUS)) + .collect(Collectors.toList())) + .containsExactly("NOT_FOUND"); + assertThat( + operationAttributes.getAllValues().stream() + .map(a -> a.get(TABLE_ID)) + .collect(Collectors.toList())) + .containsExactly(BAD_TABLE_ID); + assertThat( + operationAttributes.getAllValues().stream() + .map(a -> a.get(ZONE_ID)) + .collect(Collectors.toList())) + .containsExactly("global"); + assertThat( + operationAttributes.getAllValues().stream() + .map(a -> a.get(CLUSTER_ID)) + .collect(Collectors.toList())) + .containsExactly("unspecified"); } private static class FakeService extends BigtableGrpc.BigtableImplBase { From e8be9c2fe252156c50a38fe2b53adcccfef62b10 Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Wed, 12 Jul 2023 13:06:14 -0400 Subject: [PATCH 09/34] update completeResultCode --- .../BigtableCloudMonitoringExporter.java | 77 ++++++++++++++----- .../stub/metrics/BigtableExporterUtils.java | 3 +- 2 files changed, 59 insertions(+), 21 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java index 7e7912ed82..39c182adad 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java @@ -16,23 +16,30 @@ package com.google.cloud.bigtable.data.v2.stub.metrics; import com.google.api.MonitoredResource; +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutureCallback; +import com.google.api.core.ApiFutures; import com.google.api.gax.core.FixedCredentialsProvider; import com.google.auth.Credentials; import com.google.cloud.monitoring.v3.MetricServiceClient; import com.google.cloud.monitoring.v3.MetricServiceSettings; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.MoreExecutors; import com.google.monitoring.v3.CreateTimeSeriesRequest; import com.google.monitoring.v3.ProjectName; import com.google.monitoring.v3.TimeSeries; +import com.google.protobuf.Empty; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.metrics.export.MetricExporter; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import org.threeten.bp.Duration; @@ -47,10 +54,12 @@ final class BigtableCloudMonitoringExporter implements MetricExporter { private final String projectId; private final String taskId; private final MonitoredResource monitoredResource; - private boolean isShutdown = false; + private AtomicBoolean isShutdown = new AtomicBoolean(false); private static final String RESOURCE_TYPE = "bigtable_client_raw"; + private CompletableResultCode lastCode; + static BigtableCloudMonitoringExporter create(String projectId, Credentials credentials) throws IOException { MetricServiceSettings.Builder settingsBuilder = MetricServiceSettings.newBuilder(); @@ -79,18 +88,26 @@ static BigtableCloudMonitoringExporter create(String projectId, Credentials cred @Override public CompletableResultCode export(Collection collection) { - if (isShutdown) { + if (isShutdown.get()) { + return CompletableResultCode.ofFailure(); + } + if (!collection.stream() + .flatMap(metricData -> metricData.getData().getPoints().stream()) + .allMatch(pd -> BigtableExporterUtils.getProjectId(pd).equals(projectId))) { + logger.log(Level.WARNING, "Metric data has different a projectId. Skip exporting."); return CompletableResultCode.ofFailure(); } - Preconditions.checkArgument( - collection.stream() - .flatMap(metricData -> metricData.getData().getPoints().stream()) - .allMatch(pd -> BigtableExporterUtils.getProjectId(pd).equals(projectId)), - "Some metric data has unexpected projectId"); + + lastCode = new CompletableResultCode(); + + List allTimeSeries = new ArrayList<>(); for (MetricData metricData : collection) { if (!metricData.getInstrumentationScopeInfo().getName().equals("bigtable.googleapis.com")) { continue; } + + CompletableResultCode code = new CompletableResultCode(); + List timeSeries = metricData.getData().getPoints().stream() .map( @@ -98,30 +115,52 @@ public CompletableResultCode export(Collection collection) { BigtableExporterUtils.convertPointToTimeSeries( metricData, pointData, taskId, monitoredResource)) .collect(Collectors.toList()); + allTimeSeries.addAll(timeSeries); + } - ProjectName projectName = ProjectName.of(projectId); - CreateTimeSeriesRequest request = - CreateTimeSeriesRequest.newBuilder() - .setName(projectName.toString()) - .addAllTimeSeries(timeSeries) - .build(); + ProjectName projectName = ProjectName.of(projectId); + CreateTimeSeriesRequest request = + CreateTimeSeriesRequest.newBuilder() + .setName(projectName.toString()) + .addAllTimeSeries(allTimeSeries) + .build(); - this.client.createServiceTimeSeriesCallable().futureCall(request); - } + ApiFuture future = this.client.createServiceTimeSeriesCallable().futureCall(request); - return CompletableResultCode.ofSuccess(); + ApiFutures.addCallback( + future, + new ApiFutureCallback() { + @Override + public void onFailure(Throwable throwable) { + lastCode.fail(); + } + + @Override + public void onSuccess(Empty empty) { + lastCode.succeed(); + } + }, + MoreExecutors.directExecutor()); + + return lastCode; } @Override public CompletableResultCode flush() { + if (lastCode != null) { + return lastCode; + } return CompletableResultCode.ofSuccess(); } @Override public CompletableResultCode shutdown() { + if (!isShutdown.compareAndSet(false, true)) { + logger.log(Level.INFO, "shutdown is called multiple times"); + return CompletableResultCode.ofSuccess(); + } client.shutdown(); - isShutdown = true; - return CompletableResultCode.ofSuccess(); + return flush(); } @Override diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java index 7a6ade43ef..fff2cc52d1 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java @@ -56,7 +56,6 @@ import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.net.UnknownHostException; -import java.security.SecureRandom; import java.util.Set; import java.util.UUID; import java.util.logging.Level; @@ -79,7 +78,7 @@ static String getDefaultTaskValue() { logger.log(Level.INFO, "Unable to get the hostname.", e); } // Generate a random number and use the same format "random_number@hostname". - return "java-" + new SecureRandom().nextInt() + "@" + hostname; + return "java-" + UUID.randomUUID() + "@" + hostname; } return "java-" + UUID.randomUUID() + jvmName; } From cd042528d7c2dff5a53bc938717e9e0a69ac99d1 Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Wed, 12 Jul 2023 15:30:48 -0400 Subject: [PATCH 10/34] add a comment --- .../data/v2/stub/metrics/BigtableCloudMonitoringExporter.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java index 39c182adad..bd32f51d3b 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java @@ -66,6 +66,8 @@ static BigtableCloudMonitoringExporter create(String projectId, Credentials cred settingsBuilder.setCredentialsProvider(FixedCredentialsProvider.create(credentials)); org.threeten.bp.Duration timeout = Duration.ofMinutes(1); + // TODO: createServiceTimeSeries needs special handling if the request failed. Leaving + // it as not retried for now. settingsBuilder.createServiceTimeSeriesSettings().setSimpleTimeoutNoRetries(timeout); return new BigtableCloudMonitoringExporter( projectId, From 3d9b30db69ab61c7bb53350e31bc86ab0b5094bd Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Wed, 23 Aug 2023 17:18:04 -0400 Subject: [PATCH 11/34] udpate --- .../clirr-ignored-differences.xml | 6 + .../data/v2/stub/EnhancedBigtableStub.java | 122 ++++++++++++++---- .../v2/stub/EnhancedBigtableStubSettings.java | 23 ++++ .../BigtableCloudMonitoringExporter.java | 6 +- .../metrics/BuiltinMetricsAttributes.java | 34 ++--- .../metrics/BuiltinMetricsTracerFactory.java | 71 ++-------- .../v2/stub/metrics/BuiltinMetricsView.java | 66 ++++++++++ .../EnhancedBigtableStubSettingsTest.java | 1 + .../metrics/BuiltinMetricsTracerTest.java | 6 +- 9 files changed, 224 insertions(+), 111 deletions(-) create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java diff --git a/google-cloud-bigtable/clirr-ignored-differences.xml b/google-cloud-bigtable/clirr-ignored-differences.xml index 4bb4684c38..4f363b94f9 100644 --- a/google-cloud-bigtable/clirr-ignored-differences.xml +++ b/google-cloud-bigtable/clirr-ignored-differences.xml @@ -150,4 +150,10 @@ 8001 com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerBatchedUnaryCallable + + + 7004 + com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory + * + diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java index b367401b17..7c2e64d3c2 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java @@ -15,6 +15,26 @@ */ package com.google.cloud.bigtable.data.v2.stub; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APP_PROFILE; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.INSTANCE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.PROJECT_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_VIEW; + import com.google.api.core.BetaApi; import com.google.api.core.InternalApi; import com.google.api.gax.batching.Batcher; @@ -38,6 +58,7 @@ import com.google.api.gax.rpc.ServerStreamingCallSettings; import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.UnaryCallable; +import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.OpencensusTracerFactory; import com.google.api.gax.tracing.SpanName; import com.google.api.gax.tracing.TracedServerStreamingCallable; @@ -87,6 +108,7 @@ import com.google.cloud.bigtable.data.v2.stub.changestream.GenerateInitialChangeStreamPartitionsUserCallable; import com.google.cloud.bigtable.data.v2.stub.changestream.ReadChangeStreamResumptionStrategy; import com.google.cloud.bigtable.data.v2.stub.changestream.ReadChangeStreamUserCallable; +import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableCloudMonitoringExporter; import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracerStreamingCallable; import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracerUnaryCallable; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTracerFactory; @@ -118,6 +140,13 @@ import io.opencensus.tags.TagValue; import io.opencensus.tags.Tagger; import io.opencensus.tags.Tags; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.resources.Resource; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; @@ -219,36 +248,77 @@ public static EnhancedBigtableStubSettings finalizeSettings( RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID, TagValue.create(settings.getAppProfileId())) .build(); + + ImmutableList.Builder tracerFactories = ImmutableList.builder(); + tracerFactories + .add( + // Add OpenCensus Tracing + new OpencensusTracerFactory( + ImmutableMap.builder() + // Annotate traces with the same tags as metrics + .put(RpcMeasureConstants.BIGTABLE_PROJECT_ID.getName(), settings.getProjectId()) + .put( + RpcMeasureConstants.BIGTABLE_INSTANCE_ID.getName(), + settings.getInstanceId()) + .put( + RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID.getName(), + settings.getAppProfileId()) + // Also annotate traces with library versions + .put("gax", GaxGrpcProperties.getGaxGrpcVersion()) + .put("grpc", GaxGrpcProperties.getGrpcVersion()) + .put("gapic", Version.VERSION) + .build())) + // Add OpenCensus Metrics + .add(MetricsTracerFactory.create(tagger, stats, attributes)) + // Add user configured tracer + .add(settings.getTracerFactory()); + Attributes otelAttributes = + Attributes.of( + PROJECT_ID, + settings.getProjectId(), + INSTANCE_ID, + settings.getInstanceId(), + APP_PROFILE, + settings.getAppProfileId()); + setupBuiltinMetricsTracerFactory(builder, tracerFactories, otelAttributes); // Inject Opencensus instrumentation - builder.setTracerFactory( - new CompositeTracerFactory( - ImmutableList.of( - // Add OpenCensus Tracing - new OpencensusTracerFactory( - ImmutableMap.builder() - // Annotate traces with the same tags as metrics - .put( - RpcMeasureConstants.BIGTABLE_PROJECT_ID.getName(), - settings.getProjectId()) - .put( - RpcMeasureConstants.BIGTABLE_INSTANCE_ID.getName(), - settings.getInstanceId()) - .put( - RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID.getName(), - settings.getAppProfileId()) - // Also annotate traces with library versions - .put("gax", GaxGrpcProperties.getGaxGrpcVersion()) - .put("grpc", GaxGrpcProperties.getGrpcVersion()) - .put("gapic", Version.VERSION) - .build()), - // Add OpenCensus Metrics - MetricsTracerFactory.create(tagger, stats, attributes), - BuiltinMetricsTracerFactory.create(settings), - // Add user configured tracer - settings.getTracerFactory()))); + builder.setTracerFactory(new CompositeTracerFactory(tracerFactories.build())); return builder.build(); } + private static void setupBuiltinMetricsTracerFactory( + EnhancedBigtableStubSettings.Builder settings, + ImmutableList.Builder tracerFactories, + Attributes attributes) + throws IOException { + if (settings.getOpenTelemetry() != null) { + tracerFactories.add( + BuiltinMetricsTracerFactory.create(settings.getOpenTelemetry(), attributes)); + } else if (settings.isBuiltinMetricsEnabled()) { + MetricExporter metricExporter = + BigtableCloudMonitoringExporter.create( + settings.getProjectId(), settings.getCredentialsProvider().getCredentials()); + Resource resource = Resource.create(attributes); + SdkMeterProvider meterProvider = + SdkMeterProvider.builder() + .setResource(resource) + .registerMetricReader(PeriodicMetricReader.create(metricExporter)) + .registerView(OPERATION_LATENCIES_SELECTOR, OPERATION_LATENCIES_VIEW) + .registerView(ATTEMPT_LATENCIES_SELECTOR, ATTEMPT_LATENCIES_VIEW) + .registerView(SERVER_LATENCIES_SELECTOR, SERVER_LATENCIES_VIEW) + .registerView(FIRST_RESPONSE_LATENCIES_SELECTOR, FIRST_RESPONSE_LATENCIES_VIEW) + .registerView( + APPLICATION_BLOCKING_LATENCIES_SELECTOR, APPLICATION_BLOCKING_LATENCIES_VIEW) + .registerView(CLIENT_BLOCKING_LATENCIES_SELECTOR, CLIENT_BLOCKING_LATENCIES_VIEW) + .registerView(RETRY_COUNT_SELECTOR, RETRY_COUNT_VIEW) + .registerView(CONNECTIVITY_ERROR_COUNT_SELECTOR, CONNECTIVITY_ERROR_COUNT_VIEW) + .build(); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build(); + tracerFactories.add(BuiltinMetricsTracerFactory.create(openTelemetry, attributes)); + } + } + private static void patchCredentials(EnhancedBigtableStubSettings.Builder settings) throws IOException { int i = settings.getEndpoint().lastIndexOf(":"); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java index 7943748cf4..b24f92c057 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java @@ -52,6 +52,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import io.opentelemetry.api.OpenTelemetry; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -230,6 +231,8 @@ public class EnhancedBigtableStubSettings extends StubSettings getJwtAudienceMapping() { return jwtAudienceMapping; @@ -1051,6 +1073,7 @@ public String toString() { .add("readChangeStreamSettings", readChangeStreamSettings) .add("pingAndWarmSettings", pingAndWarmSettings) .add("isBuiltinMetricsEnabled", isBuiltinMetricsEnabled) + .add("openTelemetry", openTelemetry) .add("parent", super.toString()) .toString(); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java index 7e7912ed82..6a84e18c8c 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java @@ -16,6 +16,7 @@ package com.google.cloud.bigtable.data.v2.stub.metrics; import com.google.api.MonitoredResource; +import com.google.api.core.InternalApi; import com.google.api.gax.core.FixedCredentialsProvider; import com.google.auth.Credentials; import com.google.cloud.monitoring.v3.MetricServiceClient; @@ -38,7 +39,8 @@ import org.threeten.bp.Duration; /** Bigtable Cloud Monitoring OpenTelemetry Exporter. */ -final class BigtableCloudMonitoringExporter implements MetricExporter { +@InternalApi +public final class BigtableCloudMonitoringExporter implements MetricExporter { private static final Logger logger = Logger.getLogger(BigtableCloudMonitoringExporter.class.getName()); @@ -51,7 +53,7 @@ final class BigtableCloudMonitoringExporter implements MetricExporter { private static final String RESOURCE_TYPE = "bigtable_client_raw"; - static BigtableCloudMonitoringExporter create(String projectId, Credentials credentials) + public static BigtableCloudMonitoringExporter create(String projectId, Credentials credentials) throws IOException { MetricServiceSettings.Builder settingsBuilder = MetricServiceSettings.newBuilder(); settingsBuilder.setCredentialsProvider(FixedCredentialsProvider.create(credentials)); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java index f8a20d961e..f65916e9bb 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java @@ -15,6 +15,7 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; +import com.google.api.core.InternalApi; import com.google.common.collect.ImmutableList; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.sdk.metrics.Aggregation; @@ -22,6 +23,7 @@ import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.View; +@InternalApi public class BuiltinMetricsAttributes { public static final AttributeKey PROJECT_ID = AttributeKey.stringKey("project_id"); @@ -56,14 +58,14 @@ public class BuiltinMetricsAttributes { static final String SCOPE = "bigtable.googleapis.com"; - static final InstrumentSelector OPERATION_LATENCIES_SELECTOR = + public static final InstrumentSelector OPERATION_LATENCIES_SELECTOR = InstrumentSelector.builder() .setName(OPERATION_LATENCIES_NAME) .setMeterName(SCOPE) .setType(InstrumentType.HISTOGRAM) .setUnit("ms") .build(); - static final InstrumentSelector ATTEMPT_LATENCIES_SELECTOR = + public static final InstrumentSelector ATTEMPT_LATENCIES_SELECTOR = InstrumentSelector.builder() .setName(ATTEMPT_LATENCIES_NAME) .setMeterName(SCOPE) @@ -71,7 +73,7 @@ public class BuiltinMetricsAttributes { .setUnit("ms") .build(); - static final InstrumentSelector RETRY_COUNT_SELECTOR = + public static final InstrumentSelector RETRY_COUNT_SELECTOR = InstrumentSelector.builder() .setName(RETRY_COUNT_NAME) .setMeterName(SCOPE) @@ -79,35 +81,35 @@ public class BuiltinMetricsAttributes { .setUnit("1") .build(); - static final InstrumentSelector SERVER_LATENCIES_SELECTOR = + public static final InstrumentSelector SERVER_LATENCIES_SELECTOR = InstrumentSelector.builder() .setName(SERVER_LATENCIES_NAME) .setMeterName(SCOPE) .setType(InstrumentType.HISTOGRAM) .setUnit("ms") .build(); - static final InstrumentSelector FIRST_RESPONSE_LATENCIES_SELECTOR = + public static final InstrumentSelector FIRST_RESPONSE_LATENCIES_SELECTOR = InstrumentSelector.builder() .setName(FIRST_RESPONSE_LATENCIES_NAME) .setMeterName(SCOPE) .setType(InstrumentType.HISTOGRAM) .setUnit("ms") .build(); - static final InstrumentSelector CLIENT_BLOCKING_LATENCIES_SELECTOR = + public static final InstrumentSelector CLIENT_BLOCKING_LATENCIES_SELECTOR = InstrumentSelector.builder() .setName(CLIENT_BLOCKING_LATENCIES_NAME) .setMeterName(SCOPE) .setType(InstrumentType.HISTOGRAM) .setUnit("ms") .build(); - static final InstrumentSelector APPLICATION_BLOCKING_LATENCIES_SELECTOR = + public static final InstrumentSelector APPLICATION_BLOCKING_LATENCIES_SELECTOR = InstrumentSelector.builder() .setName(APPLICATION_BLOCKING_LATENCIES_NAME) .setMeterName(SCOPE) .setType(InstrumentType.HISTOGRAM) .setUnit("ms") .build(); - static final InstrumentSelector CONNECTIVITY_ERROR_COUNT_SELECTOR = + public static final InstrumentSelector CONNECTIVITY_ERROR_COUNT_SELECTOR = InstrumentSelector.builder() .setName(CONNECTIVITY_ERROR_COUNT_NAME) .setMeterName(SCOPE) @@ -115,42 +117,42 @@ public class BuiltinMetricsAttributes { .setUnit("1") .build(); - static final View OPERATION_LATENCIES_VIEW = + public static final View OPERATION_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/operation_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) .build(); - static final View ATTEMPT_LATENCIES_VIEW = + public static final View ATTEMPT_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/attempt_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) .build(); - static final View SERVER_LATENCIES_VIEW = + public static final View SERVER_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/server_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) .build(); - static final View FIRST_RESPONSE_LATENCIES_VIEW = + public static final View FIRST_RESPONSE_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/first_response_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) .build(); - static final View APPLICATION_BLOCKING_LATENCIES_VIEW = + public static final View APPLICATION_BLOCKING_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/application_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) .build(); - static final View CLIENT_BLOCKING_LATENCIES_VIEW = + public static final View CLIENT_BLOCKING_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/throttling_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) .build(); - static final View RETRY_COUNT_VIEW = + public static final View RETRY_COUNT_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/retry_count") .setAggregation(Aggregation.sum()) .build(); - static final View CONNECTIVITY_ERROR_COUNT_VIEW = + public static final View CONNECTIVITY_ERROR_COUNT_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/connectivity_error_count") .setAggregation(Aggregation.sum()) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java index aab9433bbd..97bfc88eca 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java @@ -15,40 +15,15 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APP_PROFILE; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.INSTANCE_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.PROJECT_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_VIEW; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SCOPE; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_VIEW; import com.google.api.core.InternalApi; import com.google.api.gax.tracing.ApiTracer; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.BaseApiTracerFactory; import com.google.api.gax.tracing.SpanName; -import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.metrics.SdkMeterProvider; -import io.opentelemetry.sdk.metrics.export.MetricExporter; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; -import io.opentelemetry.sdk.resources.Resource; import java.io.IOException; /** @@ -57,48 +32,18 @@ */ @InternalApi("For internal use only") public class BuiltinMetricsTracerFactory extends BaseApiTracerFactory { - private final Attributes attributes; + private final BigtableMetricsRecorder bigtableMetricsRecorder; + private final Attributes attributes; - public static BuiltinMetricsTracerFactory create(EnhancedBigtableStubSettings settings) - throws IOException { - return new BuiltinMetricsTracerFactory(settings); + public static BuiltinMetricsTracerFactory create( + OpenTelemetry openTelemetry, Attributes attributes) throws IOException { + return new BuiltinMetricsTracerFactory(openTelemetry, attributes); } - BuiltinMetricsTracerFactory(EnhancedBigtableStubSettings settings) throws IOException { - this.attributes = - Attributes.builder() - .put(PROJECT_ID, settings.getProjectId()) - .put(INSTANCE_ID, settings.getInstanceId()) - .put(APP_PROFILE, settings.getAppProfileId()) - .build(); - - if (settings.isBuiltinMetricsEnabled()) { - Resource resource = Resource.create(attributes); - MetricExporter metricExporter = - BigtableCloudMonitoringExporter.create( - settings.getProjectId(), settings.getCredentialsProvider().getCredentials()); - - SdkMeterProvider meterProvider = - SdkMeterProvider.builder() - .setResource(resource) - .registerMetricReader(PeriodicMetricReader.create(metricExporter)) - .registerView(OPERATION_LATENCIES_SELECTOR, OPERATION_LATENCIES_VIEW) - .registerView(ATTEMPT_LATENCIES_SELECTOR, ATTEMPT_LATENCIES_VIEW) - .registerView(SERVER_LATENCIES_SELECTOR, SERVER_LATENCIES_VIEW) - .registerView(FIRST_RESPONSE_LATENCIES_SELECTOR, FIRST_RESPONSE_LATENCIES_VIEW) - .registerView( - APPLICATION_BLOCKING_LATENCIES_SELECTOR, APPLICATION_BLOCKING_LATENCIES_VIEW) - .registerView(CLIENT_BLOCKING_LATENCIES_SELECTOR, CLIENT_BLOCKING_LATENCIES_VIEW) - .registerView(RETRY_COUNT_SELECTOR, RETRY_COUNT_VIEW) - .registerView(CONNECTIVITY_ERROR_COUNT_SELECTOR, CONNECTIVITY_ERROR_COUNT_VIEW) - .build(); - OpenTelemetry openTelemetry = - OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build(); - bigtableMetricsRecorder = new BuiltinInMetricsRecorder(openTelemetry.getMeter(SCOPE)); - } else { - bigtableMetricsRecorder = new BigtableMetricsRecorder(); - } + BuiltinMetricsTracerFactory(OpenTelemetry openTelemetry, Attributes attributes) { + this.attributes = attributes; + bigtableMetricsRecorder = new BuiltinInMetricsRecorder(openTelemetry.getMeter(SCOPE)); } @Override diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java new file mode 100644 index 0000000000..6ff4fc6336 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java @@ -0,0 +1,66 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub.metrics; + +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_VIEW; + +import com.google.auth.Credentials; +import com.google.auth.oauth2.GoogleCredentials; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import java.io.IOException; + +/** Register built-in metrics on a custom OpenTelemetry instance. */ +public class BuiltinMetricsView { + + public static void registerBuiltinMetrics(String projectId, SdkMeterProviderBuilder builder) + throws IOException { + BuiltinMetricsView.registerBuiltinMetrics( + projectId, GoogleCredentials.getApplicationDefault(), builder); + } + + public static void registerBuiltinMetrics( + String projectId, Credentials credentials, SdkMeterProviderBuilder builder) + throws IOException { + MetricExporter metricExporter = BigtableCloudMonitoringExporter.create(projectId, credentials); + builder + .registerMetricReader(PeriodicMetricReader.create(metricExporter)) + .registerView(OPERATION_LATENCIES_SELECTOR, OPERATION_LATENCIES_VIEW) + .registerView(ATTEMPT_LATENCIES_SELECTOR, ATTEMPT_LATENCIES_VIEW) + .registerView(SERVER_LATENCIES_SELECTOR, SERVER_LATENCIES_VIEW) + .registerView(FIRST_RESPONSE_LATENCIES_SELECTOR, FIRST_RESPONSE_LATENCIES_VIEW) + .registerView(APPLICATION_BLOCKING_LATENCIES_SELECTOR, APPLICATION_BLOCKING_LATENCIES_VIEW) + .registerView(CLIENT_BLOCKING_LATENCIES_SELECTOR, CLIENT_BLOCKING_LATENCIES_VIEW) + .registerView(RETRY_COUNT_SELECTOR, RETRY_COUNT_VIEW) + .registerView(CONNECTIVITY_ERROR_COUNT_SELECTOR, CONNECTIVITY_ERROR_COUNT_VIEW); + } +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java index 262c583d67..e5654361ed 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java @@ -800,6 +800,7 @@ public void isRefreshingChannelFalseValueTest() { "readChangeStreamSettings", "pingAndWarmSettings", "isBuiltinMetricsEnabled", + "openTelemetry", }; @Test diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java index f0d86a7aa8..0a5590fce1 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java @@ -682,18 +682,16 @@ public void testBatchBlockingLatencies() throws InterruptedException { .map(a -> a.get(TABLE_ID)) .collect(Collectors.toList())) .containsExactly(TABLE, TABLE, TABLE); - // TODO: the order of batched tracer unary callable seems to be incorrect, causing the first - // attempt returning global assertThat( attributes.getAllValues().stream() .map(a -> a.get(ZONE_ID)) .collect(Collectors.toList())) - .containsExactly("global", ZONE, ZONE); + .containsExactly(ZONE, ZONE, ZONE); assertThat( attributes.getAllValues().stream() .map(a -> a.get(CLUSTER_ID)) .collect(Collectors.toList())) - .containsExactly("unspecified", CLUSTER, CLUSTER); + .containsExactly(CLUSTER, CLUSTER, CLUSTER); assertThat( attributes.getAllValues().stream() .map(a -> a.get(CLIENT_NAME)) From 9fcd0f36d8589126aa1cbe29ed036ddcba142517 Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Thu, 24 Aug 2023 16:56:32 -0400 Subject: [PATCH 12/34] fix tests --- .../v2/stub/EnhancedBigtableStubSettings.java | 1 + .../data/v2/stub/RetryCookieCallable.java | 63 +++++++++++++++++++ .../stub/metrics/BigtableExporterUtils.java | 4 +- .../bigtable/data/v2/it/BuiltinMetricsIT.java | 4 -- 4 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RetryCookieCallable.java diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java index b24f92c057..d08806e16f 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java @@ -765,6 +765,7 @@ private Builder(EnhancedBigtableStubSettings settings) { isRefreshingChannel = settings.isRefreshingChannel; primedTableIds = settings.primedTableIds; jwtAudienceMapping = settings.jwtAudienceMapping; + isBuiltinMetricsEnabled = settings.isBuiltinMetricsEnabled; openTelemetry = settings.openTelemetry; // Per method settings. diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RetryCookieCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RetryCookieCallable.java new file mode 100644 index 0000000000..4a9d166624 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RetryCookieCallable.java @@ -0,0 +1,63 @@ +package com.google.cloud.bigtable.data.v2.stub; + +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutureCallback; +import com.google.api.core.ApiFutures; +import com.google.api.gax.grpc.GrpcCallContext; +import com.google.api.gax.rpc.ApiCallContext; +import com.google.api.gax.rpc.UnaryCallable; +import com.google.common.util.concurrent.MoreExecutors; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ForwardingClientCall; +import io.grpc.MethodDescriptor; + +public class RetryCookieCallable extends UnaryCallable { + + UnaryCallable innerCallable; + + RetryCookieCallable(UnaryCallable innerCallable) { + this.innerCallable = innerCallable; + } + + @Override + public ApiFuture futureCall(RequestT request, ApiCallContext apiCallContext) { + GrpcCallContext grpcCallContext = (GrpcCallContext) apiCallContext; + grpcCallContext = + grpcCallContext.withCallOptions( + grpcCallContext + .getCallOptions() + .withOption(CallOptions.Key.create("retry-cookie"), "")); + RetryCookieCallback callback = new RetryCookieCallback(grpcCallContext); + ApiFuture future = innerCallable.futureCall(request, grpcCallContext); + ApiFutures.addCallback(future, callback, MoreExecutors.directExecutor()); + return future; + } + + class RetryCookieCallback implements ApiFutureCallback { + final GrpcCallContext context; + + RetryCookieCallback(GrpcCallContext context) { + this.context = context; + } + + @Override + public void onFailure(Throwable throwable) {} + + @Override + public void onSuccess(ResponseT responseT) {} + } + + class RetryCookieInterceptor implements ClientInterceptor { + + @Override + public ClientCall interceptCall( + MethodDescriptor methodDescriptor, CallOptions callOptions, Channel channel) { + ClientCall call = channel.newCall(methodDescriptor, callOptions); + + return new ForwardingClientCall.SimpleForwardingClientCall(call) {}; + } + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java index 9f9d49fe19..7a6ade43ef 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java @@ -67,8 +67,6 @@ class BigtableExporterUtils { private static final Logger logger = Logger.getLogger(BigtableExporterUtils.class.getName()); - private static final String METRIC_PREFIX = "bigtable.googleapis.com/internal/client/"; - static String getDefaultTaskValue() { // Something like '@' final String jvmName = ManagementFactory.getRuntimeMXBean().getName(); @@ -119,7 +117,7 @@ static TimeSeries convertPointToTimeSeries( .setMetricKind(convertMetricKind(metricData)) .setMetric( Metric.newBuilder() - .setType(METRIC_PREFIX + metricData.getName()) + .setType(metricData.getName()) .putAllLabels(metricLabels.build()) .build()) .setValueType(convertValueType(metricData.getType())); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java index 4e75fb8631..d3d355e39a 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java @@ -22,7 +22,6 @@ import com.google.cloud.bigtable.admin.v2.BigtableTableAdminClient; import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest; import com.google.cloud.bigtable.admin.v2.models.Table; -import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.data.v2.models.Query; import com.google.cloud.bigtable.data.v2.models.Row; import com.google.cloud.bigtable.data.v2.models.RowMutation; @@ -76,9 +75,6 @@ public static void setUpClass() throws IOException { .that(testEnvRule.env()) .isNotInstanceOf(EmulatorEnv.class); - // Enable built in metrics - BigtableDataSettings.enableBuiltinMetrics(); - // Create a cloud monitoring client metricClient = MetricServiceClient.create(); From 0c80a4c026448d91181193f497c35b514be48cfe Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Fri, 25 Aug 2023 10:22:57 -0400 Subject: [PATCH 13/34] remove unrelated changes --- .../data/v2/stub/RetryCookieCallable.java | 63 ------------------- 1 file changed, 63 deletions(-) delete mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RetryCookieCallable.java diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RetryCookieCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RetryCookieCallable.java deleted file mode 100644 index 4a9d166624..0000000000 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RetryCookieCallable.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.google.cloud.bigtable.data.v2.stub; - -import com.google.api.core.ApiFuture; -import com.google.api.core.ApiFutureCallback; -import com.google.api.core.ApiFutures; -import com.google.api.gax.grpc.GrpcCallContext; -import com.google.api.gax.rpc.ApiCallContext; -import com.google.api.gax.rpc.UnaryCallable; -import com.google.common.util.concurrent.MoreExecutors; -import io.grpc.CallOptions; -import io.grpc.Channel; -import io.grpc.ClientCall; -import io.grpc.ClientInterceptor; -import io.grpc.ForwardingClientCall; -import io.grpc.MethodDescriptor; - -public class RetryCookieCallable extends UnaryCallable { - - UnaryCallable innerCallable; - - RetryCookieCallable(UnaryCallable innerCallable) { - this.innerCallable = innerCallable; - } - - @Override - public ApiFuture futureCall(RequestT request, ApiCallContext apiCallContext) { - GrpcCallContext grpcCallContext = (GrpcCallContext) apiCallContext; - grpcCallContext = - grpcCallContext.withCallOptions( - grpcCallContext - .getCallOptions() - .withOption(CallOptions.Key.create("retry-cookie"), "")); - RetryCookieCallback callback = new RetryCookieCallback(grpcCallContext); - ApiFuture future = innerCallable.futureCall(request, grpcCallContext); - ApiFutures.addCallback(future, callback, MoreExecutors.directExecutor()); - return future; - } - - class RetryCookieCallback implements ApiFutureCallback { - final GrpcCallContext context; - - RetryCookieCallback(GrpcCallContext context) { - this.context = context; - } - - @Override - public void onFailure(Throwable throwable) {} - - @Override - public void onSuccess(ResponseT responseT) {} - } - - class RetryCookieInterceptor implements ClientInterceptor { - - @Override - public ClientCall interceptCall( - MethodDescriptor methodDescriptor, CallOptions callOptions, Channel channel) { - ClientCall call = channel.newCall(methodDescriptor, callOptions); - - return new ForwardingClientCall.SimpleForwardingClientCall(call) {}; - } - } -} From 4b66a0edb93d3f8dd6cf956cb2bfef17df84a717 Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Fri, 25 Aug 2023 15:05:29 -0400 Subject: [PATCH 14/34] fix tests --- .../clirr-ignored-differences.xml | 6 ++ .../cloud/bigtable/stats/StatsWrapper.java | 13 ---- .../data/v2/stub/metrics/BuiltinViews.java | 63 ++++++++++++++++ .../v2/it/StreamingMetricsMetadataIT.java | 66 +++++++++++++---- .../data/v2/it/UnaryMetricsMetadataIT.java | 74 +++++++++++++------ .../test_helpers/env/AbstractTestEnv.java | 3 + .../bigtable/test_helpers/env/CloudEnv.java | 28 +++++++ .../test_helpers/env/EmulatorEnv.java | 6 ++ 8 files changed, 210 insertions(+), 49 deletions(-) create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinViews.java diff --git a/google-cloud-bigtable-stats/clirr-ignored-differences.xml b/google-cloud-bigtable-stats/clirr-ignored-differences.xml index a920751495..fcdaa2b51c 100644 --- a/google-cloud-bigtable-stats/clirr-ignored-differences.xml +++ b/google-cloud-bigtable-stats/clirr-ignored-differences.xml @@ -19,4 +19,10 @@ com/google/cloud/bigtable/stats/StatsRecorderWrapper void putBatchRequestThrottled(long) + + + 7002 + com/google/cloud/bigtable/stats/StatsWrapper + * + diff --git a/google-cloud-bigtable-stats/src/main/java/com/google/cloud/bigtable/stats/StatsWrapper.java b/google-cloud-bigtable-stats/src/main/java/com/google/cloud/bigtable/stats/StatsWrapper.java index 401a1cf975..92057b713e 100644 --- a/google-cloud-bigtable-stats/src/main/java/com/google/cloud/bigtable/stats/StatsWrapper.java +++ b/google-cloud-bigtable-stats/src/main/java/com/google/cloud/bigtable/stats/StatsWrapper.java @@ -22,7 +22,6 @@ import io.opencensus.stats.Stats; import io.opencensus.stats.View; import io.opencensus.tags.TagKey; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -40,18 +39,6 @@ public static StatsRecorderWrapper createRecorder( operationType, spanName, statsAttributes, Stats.getStatsRecorder()); } - // This is used in integration tests to get the tag value strings from view manager because Stats - // is relocated to com.google.bigtable.veneer.repackaged.io.opencensus. - @InternalApi("Visible for testing") - public static List getOperationLatencyViewTagValueStrings() { - return Stats.getViewManager().getView(BuiltinViewConstants.OPERATION_LATENCIES_VIEW.getName()) - .getAggregationMap().entrySet().stream() - .map(Map.Entry::getKey) - .flatMap(x -> x.stream()) - .map(x -> x.asString()) - .collect(Collectors.toCollection(ArrayList::new)); - } - // A workaround to run ITBuiltinViewConstantsTest as integration test. Integration test runs after // the packaging step. Opencensus classes will be relocated when they are packaged but the // integration test files will not be. So the integration tests can't reference any transitive diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinViews.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinViews.java new file mode 100644 index 0000000000..0264ef79f0 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinViews.java @@ -0,0 +1,63 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub.metrics; + +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_VIEW; + +import com.google.auth.Credentials; +import com.google.auth.oauth2.GoogleCredentials; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import java.io.IOException; + +public class BuiltinViews { + + public static void registerBuiltinViews(String project, SdkMeterProviderBuilder builder) + throws IOException { + BuiltinViews.registerBuiltinViews(project, GoogleCredentials.getApplicationDefault(), builder); + } + + public static void registerBuiltinViews( + String project, Credentials credentials, SdkMeterProviderBuilder builder) throws IOException { + builder + .registerMetricReader( + PeriodicMetricReader.create( + BigtableCloudMonitoringExporter.create(project, credentials))) + .registerView(OPERATION_LATENCIES_SELECTOR, OPERATION_LATENCIES_VIEW) + .registerView(ATTEMPT_LATENCIES_SELECTOR, ATTEMPT_LATENCIES_VIEW) + .registerView(SERVER_LATENCIES_SELECTOR, SERVER_LATENCIES_VIEW) + .registerView(FIRST_RESPONSE_LATENCIES_SELECTOR, FIRST_RESPONSE_LATENCIES_VIEW) + .registerView(APPLICATION_BLOCKING_LATENCIES_SELECTOR, APPLICATION_BLOCKING_LATENCIES_VIEW) + .registerView(CLIENT_BLOCKING_LATENCIES_SELECTOR, CLIENT_BLOCKING_LATENCIES_VIEW) + .registerView(RETRY_COUNT_SELECTOR, RETRY_COUNT_VIEW) + .registerView(CONNECTIVITY_ERROR_COUNT_SELECTOR, CONNECTIVITY_ERROR_COUNT_VIEW); + } +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java index b0e12d5ade..64c393926c 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java @@ -23,15 +23,18 @@ import com.google.cloud.bigtable.admin.v2.models.Cluster; import com.google.cloud.bigtable.data.v2.models.Query; import com.google.cloud.bigtable.data.v2.models.Row; -import com.google.cloud.bigtable.stats.BuiltinViews; -import com.google.cloud.bigtable.stats.StatsWrapper; +import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes; import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv; import com.google.cloud.bigtable.test_helpers.env.TestEnvRule; import com.google.common.collect.Lists; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.data.PointData; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; @@ -45,11 +48,12 @@ public static void setUpClass() { .withMessage("StreamingMetricsMetadataIT is not supported on Emulator") .that(testEnvRule.env()) .isNotInstanceOf(EmulatorEnv.class); - BuiltinViews.registerBigtableBuiltinViews(); } @Test public void testSuccess() throws Exception { + InMemoryMetricReader metricReader = testEnvRule.env().getMetricReader(); + String prefix = UUID.randomUUID().toString(); String uniqueKey = prefix + "-read"; @@ -64,27 +68,61 @@ public void testSuccess() throws Exception { List clusters = clustersFuture.get(1, TimeUnit.MINUTES); - // give opencensus some time to populate view data - Thread.sleep(100); + List metrics = + metricReader.collectAllMetrics().stream() + .filter( + m -> + m.getName().equals(BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW.getName())) + .collect(Collectors.toList()); + + assertThat(metrics.size()).isEqualTo(1); + + MetricData metricData = metrics.get(0); + List pointData = new ArrayList<>(metricData.getData().getPoints()); + List clusterAttributes = + pointData.stream() + .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.CLUSTER_ID)) + .collect(Collectors.toList()); + List zoneAttributes = + pointData.stream() + .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.ZONE_ID)) + .collect(Collectors.toList()); - List tagValueStrings = StatsWrapper.getOperationLatencyViewTagValueStrings(); - assertThat(tagValueStrings).contains(clusters.get(0).getZone()); - assertThat(tagValueStrings).contains(clusters.get(0).getId()); + assertThat(clusterAttributes).contains(clusters.get(0).getId()); + assertThat(zoneAttributes).contains(clusters.get(0).getZone()); } @Test - public void testFailure() throws InterruptedException { + public void testFailure() { + InMemoryMetricReader metricReader = testEnvRule.env().getMetricReader(); + Query query = Query.create("non-exist-table"); try { Lists.newArrayList(testEnvRule.env().getDataClient().readRows(query)); } catch (NotFoundException e) { } - // give opencensus some time to populate view data - Thread.sleep(100); + List metrics = + metricReader.collectAllMetrics().stream() + .filter( + m -> + m.getName().equals(BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW.getName())) + .collect(Collectors.toList()); + + assertThat(metrics.size()).isEqualTo(1); + + MetricData metricData = metrics.get(0); + List pointData = new ArrayList<>(metricData.getData().getPoints()); + List clusterAttributes = + pointData.stream() + .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.CLUSTER_ID)) + .collect(Collectors.toList()); + List zoneAttributes = + pointData.stream() + .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.ZONE_ID)) + .collect(Collectors.toList()); - List tagValueStrings = StatsWrapper.getOperationLatencyViewTagValueStrings(); - assertThat(tagValueStrings).contains("unspecified"); - assertThat(tagValueStrings).contains("global"); + assertThat(clusterAttributes).contains("unspecified"); + assertThat(zoneAttributes).contains("global"); } } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java index aa2a4317fc..afa4ce50f8 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java @@ -22,14 +22,19 @@ import com.google.api.gax.rpc.NotFoundException; import com.google.cloud.bigtable.admin.v2.models.Cluster; import com.google.cloud.bigtable.data.v2.models.RowMutation; +import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes; import com.google.cloud.bigtable.stats.BuiltinViews; -import com.google.cloud.bigtable.stats.StatsWrapper; import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv; import com.google.cloud.bigtable.test_helpers.env.TestEnvRule; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.data.PointData; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; +import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; @@ -48,6 +53,8 @@ public static void setUpClass() { @Test public void testSuccess() throws Exception { + InMemoryMetricReader metricReader = testEnvRule.env().getMetricReader(); + String rowKey = UUID.randomUUID().toString(); String familyId = testEnvRule.env().getFamilyId(); @@ -69,22 +76,34 @@ public void testSuccess() throws Exception { .listClustersAsync(testEnvRule.env().getInstanceId()); List clusters = clustersFuture.get(1, TimeUnit.MINUTES); - // give opencensus some time to populate view data - for (int i = 0; i < 10; i++) { - if (StatsWrapper.getOperationLatencyViewTagValueStrings() - .contains(clusters.get(0).getZone())) { - break; - } - Thread.sleep(100); - } + List metrics = + metricReader.collectAllMetrics().stream() + .filter( + m -> + m.getName().equals(BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW.getName())) + .collect(Collectors.toList()); + + assertThat(metrics.size()).isEqualTo(1); + + MetricData metricData = metrics.get(0); + List pointData = new ArrayList<>(metricData.getData().getPoints()); + List clusterAttributes = + pointData.stream() + .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.CLUSTER_ID)) + .collect(Collectors.toList()); + List zoneAttributes = + pointData.stream() + .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.ZONE_ID)) + .collect(Collectors.toList()); - List tagValueStrings = StatsWrapper.getOperationLatencyViewTagValueStrings(); - assertThat(tagValueStrings).contains(clusters.get(0).getZone()); - assertThat(tagValueStrings).contains(clusters.get(0).getId()); + assertThat(clusterAttributes).contains(clusters.get(0).getId()); + assertThat(zoneAttributes).contains(clusters.get(0).getZone()); } @Test public void testFailure() throws Exception { + InMemoryMetricReader metricReader = testEnvRule.env().getMetricReader(); + String rowKey = UUID.randomUUID().toString(); String familyId = testEnvRule.env().getFamilyId(); @@ -106,16 +125,27 @@ public void testFailure() throws Exception { } } - // give opencensus some time to populate view data - for (int i = 0; i < 10; i++) { - if (StatsWrapper.getOperationLatencyViewTagValueStrings().contains("unspecified")) { - break; - } - Thread.sleep(100); - } + List metrics = + metricReader.collectAllMetrics().stream() + .filter( + m -> + m.getName().equals(BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW.getName())) + .collect(Collectors.toList()); + + assertThat(metrics.size()).isEqualTo(1); + + MetricData metricData = metrics.get(0); + List pointData = new ArrayList<>(metricData.getData().getPoints()); + List clusterAttributes = + pointData.stream() + .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.CLUSTER_ID)) + .collect(Collectors.toList()); + List zoneAttributes = + pointData.stream() + .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.ZONE_ID)) + .collect(Collectors.toList()); - List tagValueStrings = StatsWrapper.getOperationLatencyViewTagValueStrings(); - assertThat(tagValueStrings).contains("unspecified"); - assertThat(tagValueStrings).contains("global"); + assertThat(clusterAttributes).contains("unspecified"); + assertThat(zoneAttributes).contains("global"); } } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/AbstractTestEnv.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/AbstractTestEnv.java index fd363099d9..069b1d979f 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/AbstractTestEnv.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/AbstractTestEnv.java @@ -25,6 +25,7 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.io.IOException; import java.util.HashMap; import java.util.List; @@ -70,6 +71,8 @@ public abstract BigtableTableAdminClient getTableAdminClientForInstance(String i public abstract String getInstanceId(); + public abstract InMemoryMetricReader getMetricReader(); + /** Try to guess the primary cluster id */ public synchronized String getPrimaryClusterId() { if (primaryClusterId != null) { diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java index ba0fda8b2c..dd5a667720 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java @@ -26,6 +26,7 @@ import com.google.cloud.bigtable.data.v2.BigtableDataClient; import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings; +import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinViews; import com.google.common.base.Joiner; import com.google.common.base.MoreObjects; import com.google.common.base.Predicate; @@ -41,6 +42,11 @@ import io.grpc.ManagedChannelBuilder; import io.grpc.Metadata; import io.grpc.MethodDescriptor; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -97,6 +103,8 @@ public boolean apply(InetSocketAddress input) { private BigtableTableAdminClient tableAdminClient; private BigtableInstanceAdminClient instanceAdminClient; + private final InMemoryMetricReader metricReader = InMemoryMetricReader.create(); + static CloudEnv fromSystemProperties() { return new CloudEnv( getOptionalProperty(DATA_ENDPOINT_PROPERTY_NAME, ""), @@ -127,6 +135,7 @@ private CloudEnv( setupRemoteAddrInterceptor(dataSettings.stubSettings()); configureUserAgent(dataSettings.stubSettings()); + configureMetricsReader(dataSettings.stubSettings()); this.tableAdminSettings = BigtableTableAdminSettings.newBuilder().setProjectId(projectId).setInstanceId(instanceId); @@ -265,6 +274,20 @@ private void configureUserAgent(EnhancedBigtableStubSettings.Builder stubSetting stubSettings.setHeaderProvider(FixedHeaderProvider.create(newHeaders)); } + private void configureMetricsReader(EnhancedBigtableStubSettings.Builder stubSettings) { + SdkMeterProviderBuilder meterProvider = + SdkMeterProvider.builder().registerMetricReader(metricReader); + try { + BuiltinViews.registerBuiltinViews(projectId, meterProvider); + } catch (IOException e) { + throw new RuntimeException("Failed to register views"); + } + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); + + stubSettings.setOpenTelemetry(openTelemetry); + } + @Override void start() throws IOException { dataClient = BigtableDataClient.create(dataSettings.build()); @@ -342,6 +365,11 @@ public BigtableTableAdminSettings getTableAdminSettings() { } } + @Override + public InMemoryMetricReader getMetricReader() { + return this.metricReader; + } + @Override public String getProjectId() { return projectId; diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/EmulatorEnv.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/EmulatorEnv.java index bec3e0eef2..c570811434 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/EmulatorEnv.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/EmulatorEnv.java @@ -24,6 +24,7 @@ import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.emulator.v2.Emulator; import com.google.common.base.Strings; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.io.IOException; import java.nio.file.Paths; @@ -140,6 +141,11 @@ public BigtableInstanceAdminClient getInstanceAdminClient() { throw new UnsupportedOperationException("InstanceAdminClient is not supported with emulator"); } + @Override + public InMemoryMetricReader getMetricReader() { + throw new UnsupportedOperationException("Metric reader is not supported with emulator"); + } + public String getKmsKeyName() { throw new UnsupportedOperationException("CMEK is not supported with emulator"); } From 9414ecef7018fc206e809f0e7e00d5a02865db61 Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Fri, 1 Sep 2023 14:20:24 -0400 Subject: [PATCH 15/34] add documentation --- .../data/v2/BigtableDataSettings.java | 2 +- .../data/v2/stub/EnhancedBigtableStub.java | 38 +++++------ .../v2/stub/EnhancedBigtableStubSettings.java | 30 ++++++++- .../stub/metrics/BigtableExporterUtils.java | 12 ++-- .../stub/metrics/BigtableMetricsRecorder.java | 3 +- .../metrics/BuiltinInMetricsRecorder.java | 17 ++--- ...utes.java => BuiltinMetricsConstants.java} | 3 +- .../v2/stub/metrics/BuiltinMetricsTracer.java | 14 ++--- .../metrics/BuiltinMetricsTracerFactory.java | 2 +- .../v2/stub/metrics/BuiltinMetricsView.java | 37 ++++++----- .../data/v2/stub/metrics/BuiltinViews.java | 63 ------------------- .../v2/it/StreamingMetricsMetadataIT.java | 16 +++-- .../data/v2/it/UnaryMetricsMetadataIT.java | 16 +++-- .../BigtableCloudMonitoringExporterTest.java | 14 ++--- .../metrics/BuiltinMetricsTracerTest.java | 38 +++++------ .../bigtable/test_helpers/env/CloudEnv.java | 4 +- 16 files changed, 138 insertions(+), 171 deletions(-) rename google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/{BuiltinMetricsAttributes.java => BuiltinMetricsConstants.java} (98%) delete mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinViews.java diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java index 4729c3fd19..177e200538 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java @@ -532,7 +532,7 @@ public boolean isBulkMutationFlowControlEnabled() { return stubSettings.bulkMutateRowsSettings().isServerInitiatedFlowControlEnabled(); } - /** Set if built-in metrics will be enabled. */ + /** Set enable to true to enable builtin metrics. */ public Builder setBuiltinMetricsEnabled(boolean enable) { stubSettings.setBuiltinMetricsEnabled(enable); return this; diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java index 7c2e64d3c2..c9a552c5bf 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java @@ -15,25 +15,25 @@ */ package com.google.cloud.bigtable.data.v2.stub; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APP_PROFILE; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.INSTANCE_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.PROJECT_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APP_PROFILE; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PROJECT_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_VIEW; import com.google.api.core.BetaApi; import com.google.api.core.InternalApi; diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java index d08806e16f..6931d98660 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java @@ -62,6 +62,7 @@ import java.util.Set; import java.util.logging.Logger; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.threeten.bp.Duration; /** @@ -915,18 +916,43 @@ public boolean isBuiltinMetricsEnabled() { return isBuiltinMetricsEnabled; } - /** Set if builtin metrics will be enabled. */ + /** Set enable to true to enable builtin metrics. */ public Builder setBuiltinMetricsEnabled(boolean enable) { this.isBuiltinMetricsEnabled = enable; return this; } + /** + * Set a custom OpenTelemetry instance. + * + *

Register builtin metrics on the custom OpenTelemetry: + * SdkMeterProviderBuilder sdkMeterProvider = SdkMeterProvider.builder(); + * + * // register Builtin metrics on your meter provider + * BuiltinMetricsViews.registerBuiltinMetrics("project-id", sdkMeterProvider); + * + * // register other metrics reader and views + * sdkMeterProvider.registerMetricReader(..); + * sdkMeterProvider.registerView(..); + * + * // create the OTEL instance + * OpenTelemetry openTelemetry = OpenTelemetrySdk.builder(). + * setMeterProvider(sdkMeterProvider().build(); + * + * stubSettings.setOpenTelemetry(openTelemetry); + * + */ public Builder setOpenTelemetry(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; return this; } - public OpenTelemetry getOpenTelemetry() { + /** + * Gets the custom OpenTelemetry instance. If no custom OTEL instance is set, the client uses a + * default instance with builtin metrics enabled. Use {@link #setBuiltinMetricsEnabled(boolean)} + * to disable the builtin metrics. + */ + public @Nullable OpenTelemetry getOpenTelemetry() { return this.openTelemetry; } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java index 7a6ade43ef..329d506ba4 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java @@ -25,12 +25,12 @@ import static com.google.api.MetricDescriptor.ValueType.DISTRIBUTION; import static com.google.api.MetricDescriptor.ValueType.DOUBLE; import static com.google.api.MetricDescriptor.ValueType.INT64; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_UID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLUSTER_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.INSTANCE_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.PROJECT_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.TABLE_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ZONE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_UID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PROJECT_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID; import com.google.api.Distribution; import com.google.api.Metric; diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableMetricsRecorder.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableMetricsRecorder.java index ae4d7d301c..914eb1c46b 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableMetricsRecorder.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableMetricsRecorder.java @@ -17,7 +17,8 @@ import io.opentelemetry.api.common.Attributes; -public class BigtableMetricsRecorder { +/** A helper class to record Bigtable metrics. */ +class BigtableMetricsRecorder { void recordOperationLatencies(long value, Attributes attributes) {} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinInMetricsRecorder.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinInMetricsRecorder.java index a4b48c6283..138a6f2004 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinInMetricsRecorder.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinInMetricsRecorder.java @@ -15,20 +15,21 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_NAME; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.LongCounter; import io.opentelemetry.api.metrics.LongHistogram; import io.opentelemetry.api.metrics.Meter; +/** Implementation of {@link BigtableMetricsRecorder} that creates Builtin metrics measurements. */ class BuiltinInMetricsRecorder extends BigtableMetricsRecorder { private static final String MILLISECOND = "ms"; diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java similarity index 98% rename from google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java rename to google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java index f65916e9bb..0eab6f5392 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java @@ -23,8 +23,9 @@ import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.View; +/** Defining Bigtable builit-in metrics scope, attributes, metric names and views. */ @InternalApi -public class BuiltinMetricsAttributes { +public class BuiltinMetricsConstants { public static final AttributeKey PROJECT_ID = AttributeKey.stringKey("project_id"); public static final AttributeKey INSTANCE_ID = AttributeKey.stringKey("instance"); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java index 41361d8d80..16d6a2dc1f 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java @@ -16,13 +16,13 @@ package com.google.cloud.bigtable.data.v2.stub.metrics; import static com.google.api.gax.tracing.ApiTracerFactory.OperationType; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLUSTER_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.METHOD; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.STATUS; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.STREAMING; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.TABLE_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ZONE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METHOD; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STATUS; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STREAMING; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID; import com.google.api.gax.retrying.ServerStreamingAttemptException; import com.google.api.gax.tracing.SpanName; diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java index 97bfc88eca..0cc67fa456 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java @@ -15,7 +15,7 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SCOPE; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SCOPE; import com.google.api.core.InternalApi; import com.google.api.gax.tracing.ApiTracer; diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java index 6ff4fc6336..7d00ade31f 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java @@ -15,22 +15,22 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_VIEW; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_SELECTOR; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_VIEW; import com.google.auth.Credentials; import com.google.auth.oauth2.GoogleCredentials; @@ -42,12 +42,17 @@ /** Register built-in metrics on a custom OpenTelemetry instance. */ public class BuiltinMetricsView { + /** + * Register built-in metrics on the {@link SdkMeterProviderBuilder} with application default + * credentials. + */ public static void registerBuiltinMetrics(String projectId, SdkMeterProviderBuilder builder) throws IOException { BuiltinMetricsView.registerBuiltinMetrics( projectId, GoogleCredentials.getApplicationDefault(), builder); } + /** Register built-in metrics on the {@link SdkMeterProviderBuilder} with credentials. */ public static void registerBuiltinMetrics( String projectId, Credentials credentials, SdkMeterProviderBuilder builder) throws IOException { diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinViews.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinViews.java deleted file mode 100644 index 0264ef79f0..0000000000 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinViews.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.cloud.bigtable.data.v2.stub.metrics; - -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APPLICATION_BLOCKING_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ATTEMPT_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_BLOCKING_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CONNECTIVITY_ERROR_COUNT_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.FIRST_RESPONSE_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.RETRY_COUNT_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.SERVER_LATENCIES_VIEW; - -import com.google.auth.Credentials; -import com.google.auth.oauth2.GoogleCredentials; -import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; -import java.io.IOException; - -public class BuiltinViews { - - public static void registerBuiltinViews(String project, SdkMeterProviderBuilder builder) - throws IOException { - BuiltinViews.registerBuiltinViews(project, GoogleCredentials.getApplicationDefault(), builder); - } - - public static void registerBuiltinViews( - String project, Credentials credentials, SdkMeterProviderBuilder builder) throws IOException { - builder - .registerMetricReader( - PeriodicMetricReader.create( - BigtableCloudMonitoringExporter.create(project, credentials))) - .registerView(OPERATION_LATENCIES_SELECTOR, OPERATION_LATENCIES_VIEW) - .registerView(ATTEMPT_LATENCIES_SELECTOR, ATTEMPT_LATENCIES_VIEW) - .registerView(SERVER_LATENCIES_SELECTOR, SERVER_LATENCIES_VIEW) - .registerView(FIRST_RESPONSE_LATENCIES_SELECTOR, FIRST_RESPONSE_LATENCIES_VIEW) - .registerView(APPLICATION_BLOCKING_LATENCIES_SELECTOR, APPLICATION_BLOCKING_LATENCIES_VIEW) - .registerView(CLIENT_BLOCKING_LATENCIES_SELECTOR, CLIENT_BLOCKING_LATENCIES_VIEW) - .registerView(RETRY_COUNT_SELECTOR, RETRY_COUNT_VIEW) - .registerView(CONNECTIVITY_ERROR_COUNT_SELECTOR, CONNECTIVITY_ERROR_COUNT_VIEW); - } -} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java index 64c393926c..bb5d8956e3 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java @@ -23,7 +23,7 @@ import com.google.cloud.bigtable.admin.v2.models.Cluster; import com.google.cloud.bigtable.data.v2.models.Query; import com.google.cloud.bigtable.data.v2.models.Row; -import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes; +import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv; import com.google.cloud.bigtable.test_helpers.env.TestEnvRule; import com.google.common.collect.Lists; @@ -71,8 +71,7 @@ public void testSuccess() throws Exception { List metrics = metricReader.collectAllMetrics().stream() .filter( - m -> - m.getName().equals(BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW.getName())) + m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_VIEW.getName())) .collect(Collectors.toList()); assertThat(metrics.size()).isEqualTo(1); @@ -81,11 +80,11 @@ public void testSuccess() throws Exception { List pointData = new ArrayList<>(metricData.getData().getPoints()); List clusterAttributes = pointData.stream() - .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.CLUSTER_ID)) + .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID)) .collect(Collectors.toList()); List zoneAttributes = pointData.stream() - .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.ZONE_ID)) + .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID)) .collect(Collectors.toList()); assertThat(clusterAttributes).contains(clusters.get(0).getId()); @@ -105,8 +104,7 @@ public void testFailure() { List metrics = metricReader.collectAllMetrics().stream() .filter( - m -> - m.getName().equals(BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW.getName())) + m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_VIEW.getName())) .collect(Collectors.toList()); assertThat(metrics.size()).isEqualTo(1); @@ -115,11 +113,11 @@ public void testFailure() { List pointData = new ArrayList<>(metricData.getData().getPoints()); List clusterAttributes = pointData.stream() - .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.CLUSTER_ID)) + .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID)) .collect(Collectors.toList()); List zoneAttributes = pointData.stream() - .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.ZONE_ID)) + .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID)) .collect(Collectors.toList()); assertThat(clusterAttributes).contains("unspecified"); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java index afa4ce50f8..d80267c402 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java @@ -22,7 +22,7 @@ import com.google.api.gax.rpc.NotFoundException; import com.google.cloud.bigtable.admin.v2.models.Cluster; import com.google.cloud.bigtable.data.v2.models.RowMutation; -import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes; +import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; import com.google.cloud.bigtable.stats.BuiltinViews; import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv; import com.google.cloud.bigtable.test_helpers.env.TestEnvRule; @@ -79,8 +79,7 @@ public void testSuccess() throws Exception { List metrics = metricReader.collectAllMetrics().stream() .filter( - m -> - m.getName().equals(BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW.getName())) + m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_VIEW.getName())) .collect(Collectors.toList()); assertThat(metrics.size()).isEqualTo(1); @@ -89,11 +88,11 @@ public void testSuccess() throws Exception { List pointData = new ArrayList<>(metricData.getData().getPoints()); List clusterAttributes = pointData.stream() - .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.CLUSTER_ID)) + .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID)) .collect(Collectors.toList()); List zoneAttributes = pointData.stream() - .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.ZONE_ID)) + .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID)) .collect(Collectors.toList()); assertThat(clusterAttributes).contains(clusters.get(0).getId()); @@ -128,8 +127,7 @@ public void testFailure() throws Exception { List metrics = metricReader.collectAllMetrics().stream() .filter( - m -> - m.getName().equals(BuiltinMetricsAttributes.OPERATION_LATENCIES_VIEW.getName())) + m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_VIEW.getName())) .collect(Collectors.toList()); assertThat(metrics.size()).isEqualTo(1); @@ -138,11 +136,11 @@ public void testFailure() throws Exception { List pointData = new ArrayList<>(metricData.getData().getPoints()); List clusterAttributes = pointData.stream() - .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.CLUSTER_ID)) + .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID)) .collect(Collectors.toList()); List zoneAttributes = pointData.stream() - .map(pd -> pd.getAttributes().get(BuiltinMetricsAttributes.ZONE_ID)) + .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID)) .collect(Collectors.toList()); assertThat(clusterAttributes).contains("unspecified"); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java index 26b8dc8cd7..2f0a566147 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java @@ -15,13 +15,13 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APP_PROFILE; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_UID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLUSTER_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.INSTANCE_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.PROJECT_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.TABLE_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ZONE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APP_PROFILE; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_UID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PROJECT_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java index 0a5590fce1..d29d6c9665 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java @@ -15,13 +15,13 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLUSTER_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.METHOD; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.STATUS; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.STREAMING; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.TABLE_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ZONE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METHOD; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STATUS; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STREAMING; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.timeout; @@ -241,9 +241,9 @@ public void sendMessage(ReqT message) { baseAttributes = Attributes.builder() - .put(BuiltinMetricsAttributes.PROJECT_ID, PROJECT_ID) - .put(BuiltinMetricsAttributes.INSTANCE_ID, INSTANCE_ID) - .put(BuiltinMetricsAttributes.APP_PROFILE, APP_PROFILE_ID) + .put(BuiltinMetricsConstants.PROJECT_ID, PROJECT_ID) + .put(BuiltinMetricsConstants.INSTANCE_ID, INSTANCE_ID) + .put(BuiltinMetricsConstants.APP_PROFILE, APP_PROFILE_ID) .build(); } @@ -516,17 +516,17 @@ public void testMutateRowAttemptsTagValues() { .recordAttemptLatencies(value.capture(), attributes.capture()); assertThat( attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsAttributes.PROJECT_ID)) + .map(a -> a.get(BuiltinMetricsConstants.PROJECT_ID)) .collect(Collectors.toList())) .containsExactly(PROJECT_ID, PROJECT_ID, PROJECT_ID); assertThat( attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsAttributes.INSTANCE_ID)) + .map(a -> a.get(BuiltinMetricsConstants.INSTANCE_ID)) .collect(Collectors.toList())) .containsExactly(INSTANCE_ID, INSTANCE_ID, INSTANCE_ID); assertThat( attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsAttributes.APP_PROFILE)) + .map(a -> a.get(BuiltinMetricsConstants.APP_PROFILE)) .collect(Collectors.toList())) .containsExactly(APP_PROFILE_ID, APP_PROFILE_ID, APP_PROFILE_ID); assertThat( @@ -586,17 +586,17 @@ public void testReadRowsAttemptsTagValues() { .recordAttemptLatencies(value.capture(), attributes.capture()); assertThat( attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsAttributes.PROJECT_ID)) + .map(a -> a.get(BuiltinMetricsConstants.PROJECT_ID)) .collect(Collectors.toList())) .containsExactly(PROJECT_ID, PROJECT_ID); assertThat( attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsAttributes.INSTANCE_ID)) + .map(a -> a.get(BuiltinMetricsConstants.INSTANCE_ID)) .collect(Collectors.toList())) .containsExactly(INSTANCE_ID, INSTANCE_ID); assertThat( attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsAttributes.APP_PROFILE)) + .map(a -> a.get(BuiltinMetricsConstants.APP_PROFILE)) .collect(Collectors.toList())) .containsExactly(APP_PROFILE_ID, APP_PROFILE_ID); assertThat( @@ -664,17 +664,17 @@ public void testBatchBlockingLatencies() throws InterruptedException { assertThat( attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsAttributes.PROJECT_ID)) + .map(a -> a.get(BuiltinMetricsConstants.PROJECT_ID)) .collect(Collectors.toList())) .containsExactly(PROJECT_ID, PROJECT_ID, PROJECT_ID); assertThat( attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsAttributes.INSTANCE_ID)) + .map(a -> a.get(BuiltinMetricsConstants.INSTANCE_ID)) .collect(Collectors.toList())) .containsExactly(INSTANCE_ID, INSTANCE_ID, INSTANCE_ID); assertThat( attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsAttributes.APP_PROFILE)) + .map(a -> a.get(BuiltinMetricsConstants.APP_PROFILE)) .collect(Collectors.toList())) .containsExactly(APP_PROFILE_ID, APP_PROFILE_ID, APP_PROFILE_ID); assertThat( diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java index dd5a667720..c2a2ed300c 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java @@ -26,7 +26,7 @@ import com.google.cloud.bigtable.data.v2.BigtableDataClient; import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings; -import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinViews; +import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsView; import com.google.common.base.Joiner; import com.google.common.base.MoreObjects; import com.google.common.base.Predicate; @@ -278,7 +278,7 @@ private void configureMetricsReader(EnhancedBigtableStubSettings.Builder stubSet SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder().registerMetricReader(metricReader); try { - BuiltinViews.registerBuiltinViews(projectId, meterProvider); + BuiltinMetricsView.registerBuiltinMetrics(projectId, meterProvider); } catch (IOException e) { throw new RuntimeException("Failed to register views"); } From a07f817a98e50a0f66a56e0f3803f1fcef4bb38e Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Fri, 1 Sep 2023 19:46:16 -0400 Subject: [PATCH 16/34] fix test --- google-cloud-bigtable/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/google-cloud-bigtable/pom.xml b/google-cloud-bigtable/pom.xml index bf8d51cf10..26ee617e64 100644 --- a/google-cloud-bigtable/pom.xml +++ b/google-cloud-bigtable/pom.xml @@ -67,6 +67,7 @@ com.google.cloud google-cloud-bigtable-stats + test From ef4f2be05eb7c98a2634a59f920a65c6684140ab Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Fri, 1 Sep 2023 19:56:52 -0400 Subject: [PATCH 17/34] merge exporter changes --- .../v2/stub/metrics/BigtableCloudMonitoringExporter.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java index bd32f51d3b..40526e1b2f 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java @@ -19,6 +19,7 @@ import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; +import com.google.api.core.InternalApi; import com.google.api.gax.core.FixedCredentialsProvider; import com.google.auth.Credentials; import com.google.cloud.monitoring.v3.MetricServiceClient; @@ -45,7 +46,8 @@ import org.threeten.bp.Duration; /** Bigtable Cloud Monitoring OpenTelemetry Exporter. */ -final class BigtableCloudMonitoringExporter implements MetricExporter { +@InternalApi("For internal use only") +public final class BigtableCloudMonitoringExporter implements MetricExporter { private static final Logger logger = Logger.getLogger(BigtableCloudMonitoringExporter.class.getName()); @@ -60,7 +62,7 @@ final class BigtableCloudMonitoringExporter implements MetricExporter { private CompletableResultCode lastCode; - static BigtableCloudMonitoringExporter create(String projectId, Credentials credentials) + public static BigtableCloudMonitoringExporter create(String projectId, Credentials credentials) throws IOException { MetricServiceSettings.Builder settingsBuilder = MetricServiceSettings.newBuilder(); settingsBuilder.setCredentialsProvider(FixedCredentialsProvider.create(credentials)); From e9c535f1013e25a5d2ed7c3e013a13fd172e15db Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Fri, 19 Jan 2024 18:26:48 -0500 Subject: [PATCH 18/34] address comments --- .../clirr-ignored-differences.xml | 1 + .../data/v2/BigtableDataSettings.java | 62 +- .../data/v2/stub/EnhancedBigtableStub.java | 84 +- .../v2/stub/EnhancedBigtableStubSettings.java | 23 +- .../stub/metrics/BigtableMetricsRecorder.java | 38 - .../metrics/BuiltinInMetricsRecorder.java | 149 ---- .../stub/metrics/BuiltinMetricsConstants.java | 15 + .../v2/stub/metrics/BuiltinMetricsTracer.java | 56 +- .../metrics/BuiltinMetricsTracerFactory.java | 100 ++- .../v2/stub/metrics/BuiltinMetricsView.java | 44 +- .../v2/BigtableDataClientFactoryTest.java | 12 +- .../v2/it/StreamingMetricsMetadataIT.java | 47 +- .../data/v2/it/UnaryMetricsMetadataIT.java | 53 +- .../BigtableCloudMonitoringExporterTest.java | 3 + .../metrics/BuiltinMetricsTracerTest.java | 798 ++++++++---------- .../test_helpers/env/AbstractTestEnv.java | 3 - .../bigtable/test_helpers/env/CloudEnv.java | 28 - .../test_helpers/env/EmulatorEnv.java | 6 - 18 files changed, 693 insertions(+), 829 deletions(-) delete mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableMetricsRecorder.java delete mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinInMetricsRecorder.java diff --git a/google-cloud-bigtable/clirr-ignored-differences.xml b/google-cloud-bigtable/clirr-ignored-differences.xml index e82ba831cb..e8e7f3ab0a 100644 --- a/google-cloud-bigtable/clirr-ignored-differences.xml +++ b/google-cloud-bigtable/clirr-ignored-differences.xml @@ -160,6 +160,7 @@ 7004 com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory + * diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java index 177e200538..21b4743803 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java @@ -32,6 +32,7 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import io.grpc.ManagedChannelBuilder; +import io.opentelemetry.api.OpenTelemetry; import java.io.IOException; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -197,24 +198,22 @@ public static void enableGfeOpenCensusStats() { /** * Register built in metrics. * - * @deprecated Please use {@link BigtableDataSettings.Builder#setBuiltinMetricsEnabled(boolean)} - * to enable or disable built-in metrics. + * @deprecated This is a no-op that doesn't do anything. Builtin metrics are enabled by default + * now. Please use {@link BigtableDataSettings.Builder#setBuiltinMetricsEnabled(boolean)} to + * enable or disable built-in metrics. */ @Deprecated - public static void enableBuiltinMetrics() throws IOException { - BUILTIN_METRICS_REGISTERED.compareAndSet(false, true); - } + public static void enableBuiltinMetrics() throws IOException {} /** * Register built in metrics with credentials. The credentials need to have metric write access * for all the projects you're publishing to. * - * @deprecated Please use {@link BigtableDataSettings.Builder#setBuiltinMetricsEnabled(boolean)} - * to enable or disable built-in metrics. + * @deprecated This is a no-op that doesn't do anything. Builtin metrics are enabled by default + * now. Please use {@link BigtableDataSettings.Builder#setBuiltinMetricsEnabled(boolean)} to + * enable or disable built-in metrics. */ - public static void enableBuiltinMetrics(Credentials credentials) throws IOException { - BUILTIN_METRICS_REGISTERED.compareAndSet(false, true); - } + public static void enableBuiltinMetrics(Credentials credentials) throws IOException {} /** Returns the target project id. */ public String getProjectId() { @@ -283,6 +282,15 @@ public boolean isBuiltinMetricsEnabled() { return stubSettings.isBuiltinMetricsEnabled(); } + /** + * Gets the custom OpenTelemetry instance. If no custom OTEL instance is set, the client uses a + * default instance with builtin metrics enabled. Use {@link + * Builder#setBuiltinMetricsEnabled(boolean)} to disable the builtin metrics. + */ + public OpenTelemetry getOpenTelemetry() { + return stubSettings.getOpenTelemetry(); + } + /** Returns the underlying RPC settings. */ public EnhancedBigtableStubSettings getStubSettings() { return stubSettings; @@ -543,6 +551,40 @@ public boolean isBuiltinMetricsEnabled() { return stubSettings.isBuiltinMetricsEnabled(); } + /** + * Set a custom OpenTelemetry instance. + * + *

To register builtin metrics on the custom OpenTelemetry: + * SdkMeterProviderBuilder sdkMeterProvider = SdkMeterProvider.builder(); + * + * // register Builtin metrics on your meter provider + * BuiltinMetricsViews.registerBuiltinMetrics("project-id", sdkMeterProvider); + * + * // register other metrics reader and views + * sdkMeterProvider.registerMetricReader(..); + * sdkMeterProvider.registerView(..); + * + * // create the OTEL instance + * OpenTelemetry openTelemetry = OpenTelemetrySdk.builder(). + * setMeterProvider(sdkMeterProvider().build(); + * + * settings.setOpenTelemetry(openTelemetry); + * + */ + public Builder setOpenTelemetry(OpenTelemetry openTelemetry) { + stubSettings.setOpenTelemetry(openTelemetry); + return this; + } + + /** + * Gets the custom OpenTelemetry instance. If no custom OTEL instance is set, the client uses a + * default instance with builtin metrics enabled. Use {@link #setBuiltinMetricsEnabled(boolean)} + * to disable the builtin metrics. + */ + public OpenTelemetry getOpenTelemetry() { + return stubSettings.getOpenTelemetry(); + } + /** * Returns the underlying settings for making RPC calls. The settings should be changed with * care. diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java index a7e611d78a..116cdb5b32 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java @@ -264,14 +264,15 @@ public static ClientContext createClientContext(EnhancedBigtableStubSettings set return ClientContext.create(builder.build()); } - public static ApiTracerFactory createBigtableTracerFactory( - EnhancedBigtableStubSettings settings) throws IOException { + public static ApiTracerFactory createBigtableTracerFactory(EnhancedBigtableStubSettings settings) + throws IOException { return createBigtableTracerFactory(settings, Tags.getTagger(), Stats.getStatsRecorder()); } @VisibleForTesting public static ApiTracerFactory createBigtableTracerFactory( - EnhancedBigtableStubSettings settings, Tagger tagger, StatsRecorder stats) throws IOException { + EnhancedBigtableStubSettings settings, Tagger tagger, StatsRecorder stats) + throws IOException { String projectId = settings.getProjectId(); String instanceId = settings.getInstanceId(); String appProfileId = settings.getAppProfileId(); @@ -285,49 +286,47 @@ public static ApiTracerFactory createBigtableTracerFactory( ImmutableList.Builder tracerFactories = ImmutableList.builder(); tracerFactories - .add( - // Add OpenCensus Tracing - new OpencensusTracerFactory( - ImmutableMap.builder() - // Annotate traces with the same tags as metrics - .put(RpcMeasureConstants.BIGTABLE_PROJECT_ID.getName(), settings.getProjectId()) - .put( - RpcMeasureConstants.BIGTABLE_INSTANCE_ID.getName(), - settings.getInstanceId()) - .put( - RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID.getName(), - settings.getAppProfileId()) - // Also annotate traces with library versions - .put("gax", GaxGrpcProperties.getGaxGrpcVersion()) - .put("grpc", GaxGrpcProperties.getGrpcVersion()) - .put("gapic", Version.VERSION) - .build())) - // Add OpenCensus Metrics - .add(MetricsTracerFactory.create(tagger, stats, attributes)) - // Add user configured tracer - .add(settings.getTracerFactory()); + .add( + // Add OpenCensus Tracing + new OpencensusTracerFactory( + ImmutableMap.builder() + // Annotate traces with the same tags as metrics + .put(RpcMeasureConstants.BIGTABLE_PROJECT_ID.getName(), settings.getProjectId()) + .put( + RpcMeasureConstants.BIGTABLE_INSTANCE_ID.getName(), + settings.getInstanceId()) + .put( + RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID.getName(), + settings.getAppProfileId()) + // Also annotate traces with library versions + .put("gax", GaxGrpcProperties.getGaxGrpcVersion()) + .put("grpc", GaxGrpcProperties.getGrpcVersion()) + .put("gapic", Version.VERSION) + .build())) + // Add OpenCensus Metrics + .add(MetricsTracerFactory.create(tagger, stats, attributes)) + // Add user configured tracer + .add(settings.getTracerFactory()); Attributes otelAttributes = - Attributes.of( - PROJECT_ID, - settings.getProjectId(), - INSTANCE_ID, - settings.getInstanceId(), - APP_PROFILE, - settings.getAppProfileId()); - setupBuiltinMetricsTracerFactory(settings.toBuilder(), tracerFactories, otelAttributes); - // Inject Opencensus instrumentation - settings.toBuilder().setTracerFactory(new CompositeTracerFactory(tracerFactories.build())); + Attributes.of( + PROJECT_ID, + settings.getProjectId(), + INSTANCE_ID, + settings.getInstanceId(), + APP_PROFILE, + settings.getAppProfileId()); + BuiltinMetricsTracerFactory builtinMetricsTracerFactory = + setupBuiltinMetricsTracerFactory(settings.toBuilder(), otelAttributes); + if (builtinMetricsTracerFactory != null) { + tracerFactories.add(builtinMetricsTracerFactory); + } return new CompositeTracerFactory(tracerFactories.build()); } - private static void setupBuiltinMetricsTracerFactory( - EnhancedBigtableStubSettings.Builder settings, - ImmutableList.Builder tracerFactories, - Attributes attributes) - throws IOException { + private static BuiltinMetricsTracerFactory setupBuiltinMetricsTracerFactory( + EnhancedBigtableStubSettings.Builder settings, Attributes attributes) throws IOException { if (settings.getOpenTelemetry() != null) { - tracerFactories.add( - BuiltinMetricsTracerFactory.create(settings.getOpenTelemetry(), attributes)); + return BuiltinMetricsTracerFactory.create(settings.getOpenTelemetry(), attributes); } else if (settings.isBuiltinMetricsEnabled()) { MetricExporter metricExporter = BigtableCloudMonitoringExporter.create( @@ -349,8 +348,9 @@ private static void setupBuiltinMetricsTracerFactory( .build(); OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build(); - tracerFactories.add(BuiltinMetricsTracerFactory.create(openTelemetry, attributes)); + return BuiltinMetricsTracerFactory.create(openTelemetry, attributes); } + return null; } private static void patchCredentials(EnhancedBigtableStubSettings.Builder settings) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java index ec7d735ace..72d30497ba 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java @@ -265,7 +265,6 @@ private EnhancedBigtableStubSettings(Builder builder) { isBuiltinMetricsEnabled = builder.isBuiltinMetricsEnabled; openTelemetry = builder.openTelemetry; - // Per method settings. readRowsSettings = builder.readRowsSettings.build(); readRowSettings = builder.readRowSettings.build(); @@ -801,7 +800,6 @@ private Builder(EnhancedBigtableStubSettings settings) { isBuiltinMetricsEnabled = settings.isBuiltinMetricsEnabled; openTelemetry = settings.openTelemetry; - // Per method settings. readRowsSettings = settings.readRowsSettings.toBuilder(); readRowSettings = settings.readRowSettings.toBuilder(); @@ -956,24 +954,9 @@ public Builder setBuiltinMetricsEnabled(boolean enable) { } /** - * Set a custom OpenTelemetry instance. - * - *

Register builtin metrics on the custom OpenTelemetry: - * SdkMeterProviderBuilder sdkMeterProvider = SdkMeterProvider.builder(); - * - * // register Builtin metrics on your meter provider - * BuiltinMetricsViews.registerBuiltinMetrics("project-id", sdkMeterProvider); - * - * // register other metrics reader and views - * sdkMeterProvider.registerMetricReader(..); - * sdkMeterProvider.registerView(..); - * - * // create the OTEL instance - * OpenTelemetry openTelemetry = OpenTelemetrySdk.builder(). - * setMeterProvider(sdkMeterProvider().build(); - * - * stubSettings.setOpenTelemetry(openTelemetry); - * + * Set a custom OpenTelemetry instance. See {@link + * com.google.cloud.bigtable.data.v2.BigtableDataSettings.Builder#setOpenTelemetry(OpenTelemetry)} + * on how to register built-in metrics on your custom OTEL instance. */ public Builder setOpenTelemetry(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableMetricsRecorder.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableMetricsRecorder.java deleted file mode 100644 index 914eb1c46b..0000000000 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableMetricsRecorder.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.cloud.bigtable.data.v2.stub.metrics; - -import io.opentelemetry.api.common.Attributes; - -/** A helper class to record Bigtable metrics. */ -class BigtableMetricsRecorder { - - void recordOperationLatencies(long value, Attributes attributes) {} - - void recordAttemptLatencies(long value, Attributes attributes) {} - - void recordFirstResponseLatencies(long value, Attributes attributes) {} - - void recordRetryCount(long value, Attributes attributes) {} - - void recordServerLatencies(long value, Attributes attributes) {} - - void recordConnectivityErrorCount(long value, Attributes attributes) {} - - void recordApplicationBlockingLatencies(long value, Attributes attributes) {} - - void recordClientBlockingLatencies(long value, Attributes attributes) {} -} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinInMetricsRecorder.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinInMetricsRecorder.java deleted file mode 100644 index 138a6f2004..0000000000 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinInMetricsRecorder.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.cloud.bigtable.data.v2.stub.metrics; - -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_NAME; - -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.LongCounter; -import io.opentelemetry.api.metrics.LongHistogram; -import io.opentelemetry.api.metrics.Meter; - -/** Implementation of {@link BigtableMetricsRecorder} that creates Builtin metrics measurements. */ -class BuiltinInMetricsRecorder extends BigtableMetricsRecorder { - - private static final String MILLISECOND = "ms"; - private static final String COUNT = "1"; - - private final LongHistogram operationLatencies; - private final LongHistogram attemptLatencies; - private final LongHistogram serverLatencies; - private final LongHistogram firstResponseLatencies; - private final LongHistogram clientBlockingLatencies; - private final LongHistogram applicationBlockingLatencies; - private final LongCounter connectivityErrorCount; - private final LongCounter retryCount; - - BuiltinInMetricsRecorder(Meter meter) { - operationLatencies = - meter - .histogramBuilder(OPERATION_LATENCIES_NAME) - .ofLongs() - .setDescription( - "Total time until final operation success or failure, including retries and backoff.") - .setUnit(MILLISECOND) - .build(); - attemptLatencies = - meter - .histogramBuilder(ATTEMPT_LATENCIES_NAME) - .ofLongs() - .setDescription("Client observed latency per RPC attempt.") - .setUnit(MILLISECOND) - .build(); - serverLatencies = - meter - .histogramBuilder(SERVER_LATENCIES_NAME) - .ofLongs() - .setDescription( - "The latency measured from the moment that the RPC entered the Google data center until the RPC was completed.") - .setUnit(MILLISECOND) - .build(); - firstResponseLatencies = - meter - .histogramBuilder(FIRST_RESPONSE_LATENCIES_NAME) - .ofLongs() - .setDescription( - "Latency from operation start until the response headers were received. The publishing of the measurement will be delayed until the attempt response has been received.") - .setUnit(MILLISECOND) - .build(); - clientBlockingLatencies = - meter - .histogramBuilder(CLIENT_BLOCKING_LATENCIES_NAME) - .ofLongs() - .setDescription( - "The artificial latency introduced by the client to limit the number of outstanding requests. The publishing of the measurement will be delayed until the attempt trailers have been received.") - .setUnit(MILLISECOND) - .build(); - applicationBlockingLatencies = - meter - .histogramBuilder(APPLICATION_BLOCKING_LATENCIES_NAME) - .ofLongs() - .setDescription( - "The latency of the client application consuming available response data.") - .setUnit(MILLISECOND) - .build(); - connectivityErrorCount = - meter - .counterBuilder(CONNECTIVITY_ERROR_COUNT_NAME) - .setDescription( - "Number of requests that failed to reach the Google datacenter. (Requests without google response headers") - .setUnit(COUNT) - .build(); - retryCount = - meter - .counterBuilder(RETRY_COUNT_NAME) - .setDescription("The number of additional RPCs sent after the initial attempt.") - .setUnit(COUNT) - .build(); - } - - @Override - void recordOperationLatencies(long value, Attributes attributes) { - operationLatencies.record(value, attributes); - } - - @Override - void recordAttemptLatencies(long value, Attributes attributes) { - attemptLatencies.record(value, attributes); - } - - @Override - void recordFirstResponseLatencies(long value, Attributes attributes) { - firstResponseLatencies.record(value, attributes); - } - - @Override - void recordRetryCount(long value, Attributes attributes) { - retryCount.add(value, attributes); - } - - @Override - void recordServerLatencies(long value, Attributes attributes) { - serverLatencies.record(value, attributes); - } - - @Override - void recordConnectivityErrorCount(long value, Attributes attributes) { - connectivityErrorCount.add(value, attributes); - } - - @Override - void recordApplicationBlockingLatencies(long value, Attributes attributes) { - applicationBlockingLatencies.record(value, attributes); - } - - @Override - void recordClientBlockingLatencies(long value, Attributes attributes) { - clientBlockingLatencies.record(value, attributes); - } -} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java index 0eab6f5392..d10829a5d4 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java @@ -17,11 +17,13 @@ import com.google.api.core.InternalApi; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.sdk.metrics.Aggregation; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.View; +import java.util.Map; /** Defining Bigtable builit-in metrics scope, attributes, metric names and views. */ @InternalApi @@ -158,4 +160,17 @@ public class BuiltinMetricsConstants { .setName("bigtable.googleapis.com/internal/client/connectivity_error_count") .setAggregation(Aggregation.sum()) .build(); + + public static Map getAllViews() { + return new ImmutableMap.Builder() + .put(OPERATION_LATENCIES_SELECTOR, OPERATION_LATENCIES_VIEW) + .put(ATTEMPT_LATENCIES_SELECTOR, ATTEMPT_LATENCIES_VIEW) + .put(SERVER_LATENCIES_SELECTOR, SERVER_LATENCIES_VIEW) + .put(FIRST_RESPONSE_LATENCIES_SELECTOR, FIRST_RESPONSE_LATENCIES_VIEW) + .put(APPLICATION_BLOCKING_LATENCIES_SELECTOR, APPLICATION_BLOCKING_LATENCIES_VIEW) + .put(CLIENT_BLOCKING_LATENCIES_SELECTOR, CLIENT_BLOCKING_LATENCIES_VIEW) + .put(RETRY_COUNT_SELECTOR, RETRY_COUNT_VIEW) + .put(CONNECTIVITY_ERROR_COUNT_SELECTOR, CONNECTIVITY_ERROR_COUNT_VIEW) + .build(); + } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java index 16d6a2dc1f..e47e9c8fab 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java @@ -29,6 +29,8 @@ import com.google.common.base.Stopwatch; import com.google.common.math.IntMath; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.LongHistogram; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -47,8 +49,6 @@ class BuiltinMetricsTracer extends BigtableTracer { private final OperationType operationType; private final SpanName spanName; - private final BigtableMetricsRecorder instruments; - // Operation level metrics private final AtomicBoolean opFinished = new AtomicBoolean(); private final Stopwatch operationTimer = Stopwatch.createStarted(); @@ -84,16 +84,39 @@ class BuiltinMetricsTracer extends BigtableTracer { private Long serverLatencies = null; + private final LongHistogram operationLatenciesHistogram; + private final LongHistogram attemptLatenciesHistogram; + private final LongHistogram serverLatenciesHistogram; + private final LongHistogram firstResponseLatenciesHistogram; + private final LongHistogram clientBlockingLatenciesHistogram; + private final LongHistogram applicationBlockingLatenciesHistogram; + private final LongCounter connectivityErrorCounter; + private final LongCounter retryCounter; + BuiltinMetricsTracer( OperationType operationType, SpanName spanName, - BigtableMetricsRecorder instruments, - Attributes attributes) { + Attributes attributes, + LongHistogram operationLatenciesHistogram, + LongHistogram attemptLatenciesHistogram, + LongHistogram serverLatenciesHistogram, + LongHistogram firstResponseLatenciesHistogram, + LongHistogram clientBlockingLatenciesHistogram, + LongHistogram applicationBlockingLatenciesHistogram, + LongCounter connectivityErrorCounter, + LongCounter retryCounter) { this.operationType = operationType; this.spanName = spanName; - - this.instruments = instruments; this.baseAttributes = attributes; + + this.operationLatenciesHistogram = operationLatenciesHistogram; + this.attemptLatenciesHistogram = attemptLatenciesHistogram; + this.serverLatenciesHistogram = serverLatenciesHistogram; + this.firstResponseLatenciesHistogram = firstResponseLatenciesHistogram; + this.clientBlockingLatenciesHistogram = clientBlockingLatenciesHistogram; + this.applicationBlockingLatenciesHistogram = applicationBlockingLatenciesHistogram; + this.connectivityErrorCounter = connectivityErrorCounter; + this.retryCounter = retryCounter; } @Override @@ -269,21 +292,20 @@ private void recordOperationCompletion(@Nullable Throwable status) { // Only record when retry count is greater than 0 so the retry // graph will be less confusing if (attemptCount > 1) { - instruments.recordRetryCount( - attemptCount - 1, attributes.toBuilder().put(STATUS, statusStr).build()); + retryCounter.add(attemptCount - 1, attributes.toBuilder().put(STATUS, statusStr).build()); } // serverLatencyTimer should already be stopped in recordAttemptCompletion - instruments.recordOperationLatencies( + operationLatenciesHistogram.record( operationLatency, attributes.toBuilder().put(STREAMING, isStreaming).put(STATUS, statusStr).build()); - instruments.recordApplicationBlockingLatencies( + applicationBlockingLatenciesHistogram.record( Duration.ofNanos(operationLatencyNano - totalServerLatencyNano.get()).toMillis(), attributes); if (operationType == OperationType.ServerStreaming && spanName.getMethodName().equals("ReadRows")) { - instruments.recordFirstResponseLatencies( + firstResponseLatenciesHistogram.record( firstResponsePerOpTimer.elapsed(TimeUnit.MILLISECONDS), attributes.toBuilder().put(STATUS, Util.extractStatus(status)).build()); } @@ -313,7 +335,7 @@ private void recordAttemptCompletion(@Nullable Throwable status) { .put(CLIENT_NAME, NAME) .build(); - instruments.recordClientBlockingLatencies(totalClientBlockingTime.get(), attributes); + clientBlockingLatenciesHistogram.record(totalClientBlockingTime.get(), attributes); // Patch the status until it's fixed in gax. When an attempt failed, // it'll throw a ServerStreamingAttemptException. Unwrap the exception @@ -324,18 +346,16 @@ private void recordAttemptCompletion(@Nullable Throwable status) { String statusStr = Util.extractStatus(status); - instruments.recordAttemptLatencies( + attemptLatenciesHistogram.record( attemptTimer.elapsed(TimeUnit.MILLISECONDS), attributes.toBuilder().put(STREAMING, isStreaming).put(STATUS, statusStr).build()); if (serverLatencies != null) { - instruments.recordServerLatencies( + serverLatenciesHistogram.record( serverLatencies, attributes.toBuilder().put(STATUS, statusStr).build()); - instruments.recordConnectivityErrorCount( - 0, attributes.toBuilder().put(STATUS, statusStr).build()); + connectivityErrorCounter.add(0, attributes.toBuilder().put(STATUS, statusStr).build()); } else { - instruments.recordConnectivityErrorCount( - 1, attributes.toBuilder().put(STATUS, statusStr).build()); + connectivityErrorCounter.add(1, attributes.toBuilder().put(STATUS, statusStr).build()); } } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java index 0cc67fa456..aed3f06acb 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java @@ -15,7 +15,15 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SCOPE; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_NAME; import com.google.api.core.InternalApi; import com.google.api.gax.tracing.ApiTracer; @@ -24,6 +32,9 @@ import com.google.api.gax.tracing.SpanName; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.Meter; import java.io.IOException; /** @@ -33,9 +44,20 @@ @InternalApi("For internal use only") public class BuiltinMetricsTracerFactory extends BaseApiTracerFactory { - private final BigtableMetricsRecorder bigtableMetricsRecorder; private final Attributes attributes; + private static final String MILLISECOND = "ms"; + private static final String COUNT = "1"; + + private final LongHistogram operationLatenciesHistogram; + private final LongHistogram attemptLatenciesHistogram; + private final LongHistogram serverLatenciesHistogram; + private final LongHistogram firstResponseLatenciesHistogram; + private final LongHistogram clientBlockingLatenciesHistogram; + private final LongHistogram applicationBlockingLatenciesHistogram; + private final LongCounter connectivityErrorCounter; + private final LongCounter retryCounter; + public static BuiltinMetricsTracerFactory create( OpenTelemetry openTelemetry, Attributes attributes) throws IOException { return new BuiltinMetricsTracerFactory(openTelemetry, attributes); @@ -43,11 +65,83 @@ public static BuiltinMetricsTracerFactory create( BuiltinMetricsTracerFactory(OpenTelemetry openTelemetry, Attributes attributes) { this.attributes = attributes; - bigtableMetricsRecorder = new BuiltinInMetricsRecorder(openTelemetry.getMeter(SCOPE)); + Meter meter = openTelemetry.getMeter(SCOPE); + + operationLatenciesHistogram = + meter + .histogramBuilder(OPERATION_LATENCIES_NAME) + .ofLongs() + .setDescription( + "Total time until final operation success or failure, including retries and backoff.") + .setUnit(MILLISECOND) + .build(); + attemptLatenciesHistogram = + meter + .histogramBuilder(ATTEMPT_LATENCIES_NAME) + .ofLongs() + .setDescription("Client observed latency per RPC attempt.") + .setUnit(MILLISECOND) + .build(); + serverLatenciesHistogram = + meter + .histogramBuilder(SERVER_LATENCIES_NAME) + .ofLongs() + .setDescription( + "The latency measured from the moment that the RPC entered the Google data center until the RPC was completed.") + .setUnit(MILLISECOND) + .build(); + firstResponseLatenciesHistogram = + meter + .histogramBuilder(FIRST_RESPONSE_LATENCIES_NAME) + .ofLongs() + .setDescription( + "Latency from operation start until the response headers were received. The publishing of the measurement will be delayed until the attempt response has been received.") + .setUnit(MILLISECOND) + .build(); + clientBlockingLatenciesHistogram = + meter + .histogramBuilder(CLIENT_BLOCKING_LATENCIES_NAME) + .ofLongs() + .setDescription( + "The artificial latency introduced by the client to limit the number of outstanding requests. The publishing of the measurement will be delayed until the attempt trailers have been received.") + .setUnit(MILLISECOND) + .build(); + applicationBlockingLatenciesHistogram = + meter + .histogramBuilder(APPLICATION_BLOCKING_LATENCIES_NAME) + .ofLongs() + .setDescription( + "The latency of the client application consuming available response data.") + .setUnit(MILLISECOND) + .build(); + connectivityErrorCounter = + meter + .counterBuilder(CONNECTIVITY_ERROR_COUNT_NAME) + .setDescription( + "Number of requests that failed to reach the Google datacenter. (Requests without google response headers") + .setUnit(COUNT) + .build(); + retryCounter = + meter + .counterBuilder(RETRY_COUNT_NAME) + .setDescription("The number of additional RPCs sent after the initial attempt.") + .setUnit(COUNT) + .build(); } @Override public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType operationType) { - return new BuiltinMetricsTracer(operationType, spanName, bigtableMetricsRecorder, attributes); + return new BuiltinMetricsTracer( + operationType, + spanName, + attributes, + operationLatenciesHistogram, + attemptLatenciesHistogram, + serverLatenciesHistogram, + firstResponseLatenciesHistogram, + clientBlockingLatenciesHistogram, + applicationBlockingLatenciesHistogram, + connectivityErrorCounter, + retryCounter); } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java index 7d00ade31f..06581d5844 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java @@ -15,31 +15,24 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_VIEW; - import com.google.auth.Credentials; import com.google.auth.oauth2.GoogleCredentials; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.metrics.View; import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import java.io.IOException; +import java.util.Map; -/** Register built-in metrics on a custom OpenTelemetry instance. */ +/** + * Register built-in metrics on a custom OpenTelemetry instance. This is for advanced usage, and is + * only necessary when wanting to write built-in metrics to cloud monitoring and custom sinks. + * Please refer to {@link + * com.google.cloud.bigtable.data.v2.BigtableDataSettings.Builder#setOpenTelemetry(OpenTelemetry)} + * for example usage. + */ public class BuiltinMetricsView { /** @@ -57,15 +50,10 @@ public static void registerBuiltinMetrics( String projectId, Credentials credentials, SdkMeterProviderBuilder builder) throws IOException { MetricExporter metricExporter = BigtableCloudMonitoringExporter.create(projectId, credentials); - builder - .registerMetricReader(PeriodicMetricReader.create(metricExporter)) - .registerView(OPERATION_LATENCIES_SELECTOR, OPERATION_LATENCIES_VIEW) - .registerView(ATTEMPT_LATENCIES_SELECTOR, ATTEMPT_LATENCIES_VIEW) - .registerView(SERVER_LATENCIES_SELECTOR, SERVER_LATENCIES_VIEW) - .registerView(FIRST_RESPONSE_LATENCIES_SELECTOR, FIRST_RESPONSE_LATENCIES_VIEW) - .registerView(APPLICATION_BLOCKING_LATENCIES_SELECTOR, APPLICATION_BLOCKING_LATENCIES_VIEW) - .registerView(CLIENT_BLOCKING_LATENCIES_SELECTOR, CLIENT_BLOCKING_LATENCIES_VIEW) - .registerView(RETRY_COUNT_SELECTOR, RETRY_COUNT_VIEW) - .registerView(CONNECTIVITY_ERROR_COUNT_SELECTOR, CONNECTIVITY_ERROR_COUNT_VIEW); + for (Map.Entry entry : + BuiltinMetricsConstants.getAllViews().entrySet()) { + builder.registerView(entry.getKey(), entry.getValue()); + } + builder.registerMetricReader(PeriodicMetricReader.create(metricExporter)); } } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java index edcda45938..926feda604 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java @@ -169,10 +169,14 @@ public void tearDown() { @Test public void testNewClientsShareTransportChannel() throws Exception { - // Create 3 lightweight clients - try (BigtableDataClientFactory factory = BigtableDataClientFactory.create(defaultSettings); + // Builtin metrics will call getCredentialsProvider at which point it'll be a + // FixedCredentialProvider. + // So disabling in the test code it's fine. + try (BigtableDataClientFactory factory = + BigtableDataClientFactory.create( + defaultSettings.toBuilder().setBuiltinMetricsEnabled(false).build()); BigtableDataClient ignored1 = factory.createForInstance("project1", "instance1"); BigtableDataClient ignored2 = factory.createForInstance("project2", "instance2"); BigtableDataClient ignored3 = factory.createForInstance("project3", "instance3")) { @@ -249,6 +253,10 @@ public void testCreateWithRefreshingChannel() throws Exception { .setProjectId(DEFAULT_PROJECT_ID) .setInstanceId(DEFAULT_INSTANCE_ID) .setAppProfileId(DEFAULT_APP_PROFILE_ID) + // Builtin metrics will call getCredentialsProvider at which point it'll be a + // FixedCredentialProvider. + // So disabling in the test code it's fine. + .setBuiltinMetricsEnabled(false) .setRefreshingChannel(true); builder .stubSettings() diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java index bb5d8956e3..2aac584319 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java @@ -21,44 +21,75 @@ import com.google.api.core.ApiFuture; import com.google.api.gax.rpc.NotFoundException; import com.google.cloud.bigtable.admin.v2.models.Cluster; +import com.google.cloud.bigtable.data.v2.BigtableDataClient; +import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.data.v2.models.Query; import com.google.cloud.bigtable.data.v2.models.Row; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; +import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsView; import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv; import com.google.cloud.bigtable.test_helpers.env.TestEnvRule; import com.google.common.collect.Lists; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.metrics.data.PointData; import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import org.junit.BeforeClass; +import org.junit.After; +import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; public class StreamingMetricsMetadataIT { @ClassRule public static TestEnvRule testEnvRule = new TestEnvRule(); - @BeforeClass - public static void setUpClass() { + private BigtableDataClient client; + private InMemoryMetricReader metricReader; + + @Before + public void setup() throws IOException { assume() .withMessage("StreamingMetricsMetadataIT is not supported on Emulator") .that(testEnvRule.env()) .isNotInstanceOf(EmulatorEnv.class); + + BigtableDataSettings.Builder settings = testEnvRule.env().getDataClientSettings().toBuilder(); + + metricReader = InMemoryMetricReader.create(); + + SdkMeterProviderBuilder meterProvider = + SdkMeterProvider.builder().registerMetricReader(metricReader); + BuiltinMetricsView.registerBuiltinMetrics(testEnvRule.env().getProjectId(), meterProvider); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); + + settings.setOpenTelemetry(openTelemetry); + + client = BigtableDataClient.create(settings.build()); + } + + @After + public void tearDown() throws IOException { + if (client != null) { + client.close(); + } } @Test public void testSuccess() throws Exception { - InMemoryMetricReader metricReader = testEnvRule.env().getMetricReader(); - String prefix = UUID.randomUUID().toString(); String uniqueKey = prefix + "-read"; Query query = Query.create(testEnvRule.env().getTableId()).rowKey(uniqueKey); - ArrayList rows = Lists.newArrayList(testEnvRule.env().getDataClient().readRows(query)); + ArrayList rows = Lists.newArrayList(client.readRows(query)); ApiFuture> clustersFuture = testEnvRule @@ -93,11 +124,9 @@ public void testSuccess() throws Exception { @Test public void testFailure() { - InMemoryMetricReader metricReader = testEnvRule.env().getMetricReader(); - Query query = Query.create("non-exist-table"); try { - Lists.newArrayList(testEnvRule.env().getDataClient().readRows(query)); + Lists.newArrayList(client.readRows(query)); } catch (NotFoundException e) { } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java index d80267c402..28b4fd8758 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java @@ -21,47 +21,74 @@ import com.google.api.core.ApiFuture; import com.google.api.gax.rpc.NotFoundException; import com.google.cloud.bigtable.admin.v2.models.Cluster; +import com.google.cloud.bigtable.data.v2.BigtableDataClient; +import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.data.v2.models.RowMutation; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; -import com.google.cloud.bigtable.stats.BuiltinViews; +import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsView; import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv; import com.google.cloud.bigtable.test_helpers.env.TestEnvRule; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.metrics.data.PointData; import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import org.junit.BeforeClass; +import org.junit.After; +import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; public class UnaryMetricsMetadataIT { @ClassRule public static TestEnvRule testEnvRule = new TestEnvRule(); - @BeforeClass - public static void setUpClass() { + private BigtableDataClient client; + private InMemoryMetricReader metricReader; + + @Before + public void setup() throws IOException { assume() .withMessage("UnaryMetricsMetadataIT is not supported on Emulator") .that(testEnvRule.env()) .isNotInstanceOf(EmulatorEnv.class); - BuiltinViews.registerBigtableBuiltinViews(); + + BigtableDataSettings.Builder settings = testEnvRule.env().getDataClientSettings().toBuilder(); + + metricReader = InMemoryMetricReader.create(); + + SdkMeterProviderBuilder meterProvider = + SdkMeterProvider.builder().registerMetricReader(metricReader); + BuiltinMetricsView.registerBuiltinMetrics(testEnvRule.env().getProjectId(), meterProvider); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); + + settings.setOpenTelemetry(openTelemetry); + + client = BigtableDataClient.create(settings.build()); + } + + @After + public void tearDown() throws IOException { + if (client != null) { + client.close(); + } } @Test public void testSuccess() throws Exception { - InMemoryMetricReader metricReader = testEnvRule.env().getMetricReader(); - String rowKey = UUID.randomUUID().toString(); String familyId = testEnvRule.env().getFamilyId(); ApiFuture future = - testEnvRule - .env() - .getDataClient() + client .mutateRowCallable() .futureCall( RowMutation.create(testEnvRule.env().getTableId(), rowKey) @@ -101,15 +128,11 @@ public void testSuccess() throws Exception { @Test public void testFailure() throws Exception { - InMemoryMetricReader metricReader = testEnvRule.env().getMetricReader(); - String rowKey = UUID.randomUUID().toString(); String familyId = testEnvRule.env().getFamilyId(); ApiFuture future = - testEnvRule - .env() - .getDataClient() + client .mutateRowCallable() .futureCall( RowMutation.create("non-exist-table", rowKey).setCell(familyId, "q", "myVal")); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java index 2f0a566147..de43da4b44 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java @@ -54,11 +54,14 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +@RunWith(JUnit4.class) public class BigtableCloudMonitoringExporterTest { private static final String projectId = "fake-project"; private static final String instanceId = "fake-instance"; diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java index 5ea6b99ee0..8aaa26116d 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java @@ -15,19 +15,22 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METHOD; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SCOPE; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STATUS; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STREAMING; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import com.google.api.client.util.Lists; import com.google.api.core.ApiFunction; @@ -40,8 +43,6 @@ import com.google.api.gax.rpc.NotFoundException; import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.StreamController; -import com.google.api.gax.tracing.ApiTracerFactory; -import com.google.api.gax.tracing.SpanName; import com.google.bigtable.v2.BigtableGrpc; import com.google.bigtable.v2.MutateRowRequest; import com.google.bigtable.v2.MutateRowResponse; @@ -81,7 +82,16 @@ import io.grpc.stub.ServerCallStreamObserver; import io.grpc.stub.StreamObserver; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.data.HistogramPointData; +import io.opentelemetry.sdk.metrics.data.LongPointData; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -96,8 +106,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.threeten.bp.Duration; @@ -126,16 +134,36 @@ public class BuiltinMetricsTracerTest { private EnhancedBigtableStub stub; - @Mock private BigtableMetricsRecorder mockInstruments; - - @Mock private BuiltinMetricsTracerFactory mockFactory; + private BuiltinMetricsTracerFactory facotry; private int batchElementCount = 2; private Attributes baseAttributes; + private InMemoryMetricReader metricReader; + @Before public void setUp() throws Exception { + metricReader = InMemoryMetricReader.create(); + + baseAttributes = + Attributes.builder() + .put(BuiltinMetricsConstants.PROJECT_ID, PROJECT_ID) + .put(BuiltinMetricsConstants.INSTANCE_ID, INSTANCE_ID) + .put(BuiltinMetricsConstants.APP_PROFILE, APP_PROFILE_ID) + .build(); + + SdkMeterProvider meterProvider = + SdkMeterProvider.builder() + .registerMetricReader(metricReader) + .setResource(Resource.create(baseAttributes)) + .build(); + + Meter meter = meterProvider.get(SCOPE); + + OpenTelemetrySdk otel = OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build(); + facotry = BuiltinMetricsTracerFactory.create(otel, baseAttributes); + // Add an interceptor to add server-timing in headers ServerInterceptor trailersInterceptor = new ServerInterceptor() { @@ -217,7 +245,7 @@ public void sendMessage(ReqT message) { .build()) .build()); - stubSettingsBuilder.setTracerFactory(mockFactory); + stubSettingsBuilder.setTracerFactory(facotry); InstantiatingGrpcChannelProvider.Builder channelProvider = ((InstantiatingGrpcChannelProvider) stubSettingsBuilder.getTransportChannelProvider()) @@ -238,13 +266,6 @@ public void sendMessage(ReqT message) { EnhancedBigtableStubSettings stubSettings = stubSettingsBuilder.build(); stub = new EnhancedBigtableStub(stubSettings, ClientContext.create(stubSettings)); - - baseAttributes = - Attributes.builder() - .put(BuiltinMetricsConstants.PROJECT_ID, PROJECT_ID) - .put(BuiltinMetricsConstants.INSTANCE_ID, INSTANCE_ID) - .put(BuiltinMetricsConstants.APP_PROFILE, APP_PROFILE_ID) - .build(); } @After @@ -255,127 +276,84 @@ public void tearDown() { @Test public void testReadRowsOperationLatencies() { - when(mockFactory.newTracer(any(), any(), any())) - .thenReturn( - new BuiltinMetricsTracer( - ApiTracerFactory.OperationType.ServerStreaming, - SpanName.of("Bigtable", "ReadRows"), - mockInstruments, - baseAttributes)); - Stopwatch stopwatch = Stopwatch.createStarted(); Lists.newArrayList(stub.readRowsCallable().call(Query.create(TABLE)).iterator()); long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS); - ArgumentCaptor value = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor attributes = ArgumentCaptor.forClass(Attributes.class); - verify(mockInstruments).recordOperationLatencies(value.capture(), attributes.capture()); - - // TODO why is it operation latency always elapsed + 1? - assertThat(value.getValue()).isIn(Range.closed(SERVER_LATENCY, elapsed + 1)); - assertThat(attributes.getValue().get(STATUS)).isEqualTo("OK"); - assertThat(attributes.getValue().get(TABLE_ID)).isEqualTo(TABLE); - assertThat(attributes.getValue().get(ZONE_ID)).isEqualTo(ZONE); - assertThat(attributes.getValue().get(CLUSTER_ID)).isEqualTo(CLUSTER); - assertThat(attributes.getValue().get(METHOD)).isEqualTo("Bigtable.ReadRows"); - assertThat(attributes.getValue().get(STREAMING)).isTrue(); + Attributes expectedAttributes = + baseAttributes + .toBuilder() + .put(STATUS, "OK") + .put(TABLE_ID, TABLE) + .put(ZONE_ID, ZONE) + .put(CLUSTER_ID, CLUSTER) + .put(METHOD, "Bigtable.ReadRows") + .put(STREAMING, true) + .put(CLIENT_NAME, "java-bigtable") + .build(); + + Collection allMetricData = metricReader.collectAllMetrics(); + + MetricData metricData = getMetricData(allMetricData, OPERATION_LATENCIES_NAME); + + long value = getAggregatedValue(metricData, expectedAttributes); + assertThat(value).isIn(Range.closed(SERVER_LATENCY, elapsed)); } @Test public void testGfeMetrics() { - when(mockFactory.newTracer(any(), any(), any())) - .thenReturn( - new BuiltinMetricsTracer( - ApiTracerFactory.OperationType.ServerStreaming, - SpanName.of("Bigtable", "ReadRows"), - mockInstruments, - baseAttributes)); - Lists.newArrayList(stub.readRowsCallable().call(Query.create(TABLE))); - // Verify record attempt are called multiple times - ArgumentCaptor value = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor attributes = ArgumentCaptor.forClass(Attributes.class); - - verify(mockInstruments, times(fakeService.getAttemptCounter().get())) - .recordAttemptLatencies(value.capture(), attributes.capture()); - - // The request was retried and gfe latency is only recorded in the retry attempt - ArgumentCaptor serverLatencies = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor serverLatenciesAttributes = - ArgumentCaptor.forClass(Attributes.class); - - verify(mockInstruments) - .recordServerLatencies(serverLatencies.capture(), serverLatenciesAttributes.capture()); - assertThat(serverLatencies.getValue()).isEqualTo(FAKE_SERVER_TIMING); - assertThat( - serverLatenciesAttributes.getAllValues().stream() - .map(a -> a.get(STATUS)) - .collect(Collectors.toList())) - .containsExactly("OK"); - assertThat( - serverLatenciesAttributes.getAllValues().stream() - .map(a -> a.get(TABLE_ID)) - .collect(Collectors.toList())) - .containsExactly(TABLE); - assertThat( - serverLatenciesAttributes.getAllValues().stream() - .map(a -> a.get(ZONE_ID)) - .collect(Collectors.toList())) - .containsExactly(ZONE); - assertThat( - serverLatenciesAttributes.getAllValues().stream() - .map(a -> a.get(CLUSTER_ID)) - .collect(Collectors.toList())) - .containsExactly(CLUSTER); - - // The first time the request was retried, it'll increment missing header counter - ArgumentCaptor connectivityErrorCount = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor errorCountAttributes = ArgumentCaptor.forClass(Attributes.class); - - verify(mockInstruments, times(fakeService.getAttemptCounter().get())) - .recordConnectivityErrorCount( - connectivityErrorCount.capture(), errorCountAttributes.capture()); - assertThat(connectivityErrorCount.getAllValues()).containsExactly(1L, 0L); - - assertThat( - errorCountAttributes.getAllValues().stream() - .map(a -> a.get(STATUS)) - .collect(Collectors.toList())) - .containsExactly("UNAVAILABLE", "OK"); - assertThat( - errorCountAttributes.getAllValues().stream() - .map(a -> a.get(TABLE_ID)) - .collect(Collectors.toList())) - .containsExactly(TABLE, TABLE); - assertThat( - errorCountAttributes.getAllValues().stream() - .map(a -> a.get(ZONE_ID)) - .collect(Collectors.toList())) - .containsExactly("global", ZONE); - assertThat( - errorCountAttributes.getAllValues().stream() - .map(a -> a.get(CLUSTER_ID)) - .collect(Collectors.toList())) - .containsExactly("unspecified", CLUSTER); + Attributes expectedAttributes = + baseAttributes + .toBuilder() + .put(STATUS, "OK") + .put(TABLE_ID, TABLE) + .put(ZONE_ID, ZONE) + .put(CLUSTER_ID, CLUSTER) + .put(CLIENT_NAME, "java-bigtable") + .put(METHOD, "Bigtable.ReadRows") + .build(); + + Collection allMetricData = metricReader.collectAllMetrics(); + + MetricData serverLatenciesMetricData = getMetricData(allMetricData, SERVER_LATENCIES_NAME); + + long serverLatencies = getAggregatedValue(serverLatenciesMetricData, expectedAttributes); + assertThat(serverLatencies).isEqualTo(FAKE_SERVER_TIMING); + + MetricData connectivityErrorCountMetricData = + getMetricData(allMetricData, CONNECTIVITY_ERROR_COUNT_NAME); + Attributes expected1 = + baseAttributes + .toBuilder() + .put(STATUS, "UNAVAILABLE") + .put(TABLE_ID, TABLE) + .put(ZONE_ID, "global") + .put(CLUSTER_ID, "unspecified") + .put(METHOD, "Bigtable.ReadRows") + .put(CLIENT_NAME, "java-bigtable") + .build(); + Attributes expected2 = + baseAttributes + .toBuilder() + .put(STATUS, "OK") + .put(TABLE_ID, TABLE) + .put(ZONE_ID, ZONE) + .put(CLUSTER_ID, CLUSTER) + .put(METHOD, "Bigtable.ReadRows") + .put(CLIENT_NAME, "java-bigtable") + .build(); + + verifyAttributes(connectivityErrorCountMetricData, expected1); + verifyAttributes(connectivityErrorCountMetricData, expected2); + + assertThat(getAggregatedValue(connectivityErrorCountMetricData, expected1)).isEqualTo(1); + assertThat(getAggregatedValue(connectivityErrorCountMetricData, expected2)).isEqualTo(0); } @Test public void testReadRowsApplicationLatencyWithAutoFlowControl() throws Exception { - when(mockFactory.newTracer(any(), any(), any())) - .thenReturn( - new BuiltinMetricsTracer( - ApiTracerFactory.OperationType.ServerStreaming, - SpanName.of("Bigtable", "ReadRows"), - mockInstruments, - baseAttributes)); - - ArgumentCaptor applicationLatency = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor operationLatency = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor opLatencyAttributes = ArgumentCaptor.forClass(Attributes.class); - ArgumentCaptor applicationLatencyAttributes = - ArgumentCaptor.forClass(Attributes.class); - final SettableApiFuture future = SettableApiFuture.create(); final AtomicInteger counter = new AtomicInteger(0); // For auto flow control, application latency is the time application spent in onResponse. @@ -407,38 +385,35 @@ public void onComplete() { }); future.get(); - verify(mockInstruments) - .recordApplicationBlockingLatencies( - applicationLatency.capture(), applicationLatencyAttributes.capture()); - verify(mockInstruments) - .recordOperationLatencies(operationLatency.capture(), opLatencyAttributes.capture()); - assertThat(counter.get()).isEqualTo(fakeService.getResponseCounter().get()); - // Thread.sleep might not sleep for the requested amount depending on the interrupt period - // defined by the OS. - // On linux this is ~1ms but on windows may be as high as 15-20ms. - assertThat(applicationLatency.getValue()) - .isAtLeast((APPLICATION_LATENCY - SLEEP_VARIABILITY) * counter.get()); - assertThat(applicationLatency.getValue()) - .isAtMost(operationLatency.getValue() - SERVER_LATENCY); + + Collection allMetricData = metricReader.collectAllMetrics(); + MetricData applicationLatency = + getMetricData(allMetricData, APPLICATION_BLOCKING_LATENCIES_NAME); + + Attributes expectedAttributes = + baseAttributes + .toBuilder() + .put(TABLE_ID, TABLE) + .put(ZONE_ID, ZONE) + .put(CLUSTER_ID, CLUSTER) + .put(CLIENT_NAME, "java-bigtable") + .put(METHOD, "Bigtable.ReadRows") + .build(); + long value = getAggregatedValue(applicationLatency, expectedAttributes); + + assertThat(value).isAtLeast((APPLICATION_LATENCY - SLEEP_VARIABILITY) * counter.get()); + + MetricData operationLatency = getMetricData(allMetricData, OPERATION_LATENCIES_NAME); + long operationLatencyValue = + getAggregatedValue( + operationLatency, + expectedAttributes.toBuilder().put(STATUS, "OK").put(STREAMING, true).build()); + assertThat(value).isAtMost(operationLatencyValue - SERVER_LATENCY); } @Test public void testReadRowsApplicationLatencyWithManualFlowControl() throws Exception { - when(mockFactory.newTracer(any(), any(), any())) - .thenReturn( - new BuiltinMetricsTracer( - ApiTracerFactory.OperationType.ServerStreaming, - SpanName.of("Bigtable", "ReadRows"), - mockInstruments, - baseAttributes)); - - ArgumentCaptor applicationLatency = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor operationLatency = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor opLatencyAttributes = ArgumentCaptor.forClass(Attributes.class); - ArgumentCaptor applicationLatencyAttributes = - ArgumentCaptor.forClass(Attributes.class); - int counter = 0; Iterator rows = stub.readRowsCallable().call(Query.create(TABLE)).iterator(); @@ -449,199 +424,129 @@ public void testReadRowsApplicationLatencyWithManualFlowControl() throws Excepti rows.next(); } - verify(mockInstruments) - .recordApplicationBlockingLatencies( - applicationLatency.capture(), applicationLatencyAttributes.capture()); - verify(mockInstruments) - .recordOperationLatencies(operationLatency.capture(), opLatencyAttributes.capture()); + Collection allMetricData = metricReader.collectAllMetrics(); + MetricData applicationLatency = + getMetricData(allMetricData, APPLICATION_BLOCKING_LATENCIES_NAME); + + Attributes expectedAttributes = + baseAttributes + .toBuilder() + .put(TABLE_ID, TABLE) + .put(ZONE_ID, ZONE) + .put(CLUSTER_ID, CLUSTER) + .put(CLIENT_NAME, "java-bigtable") + .put(METHOD, "Bigtable.ReadRows") + .build(); + long value = getAggregatedValue(applicationLatency, expectedAttributes); // For manual flow control, the last application latency shouldn't count, because at that // point the server already sent back all the responses. assertThat(counter).isEqualTo(fakeService.getResponseCounter().get()); - assertThat(applicationLatency.getValue()) - .isAtLeast(APPLICATION_LATENCY * (counter - 1) - SERVER_LATENCY); - assertThat(applicationLatency.getValue()) - .isAtMost(operationLatency.getValue() - SERVER_LATENCY); + assertThat(value).isAtLeast(APPLICATION_LATENCY * (counter - 1) - SERVER_LATENCY); + + MetricData operationLatency = getMetricData(allMetricData, OPERATION_LATENCIES_NAME); + long operationLatencyValue = + getAggregatedValue( + operationLatency, + expectedAttributes.toBuilder().put(STATUS, "OK").put(STREAMING, true).build()); + assertThat(value).isAtMost(operationLatencyValue - SERVER_LATENCY); } @Test - public void testRetryCount() { - when(mockFactory.newTracer(any(), any(), any())) - .thenReturn( - new BuiltinMetricsTracer( - ApiTracerFactory.OperationType.ServerStreaming, - SpanName.of("Bigtable", "ReadRows"), - mockInstruments, - baseAttributes)); - - ArgumentCaptor retryCount = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor retryCountAttribute = ArgumentCaptor.forClass(Attributes.class); - + public void testRetryCount() throws InterruptedException { stub.mutateRowCallable() .call(RowMutation.create(TABLE, "random-row").setCell("cf", "q", "value")); - // In TracedUnaryCallable, we create a future and add a TraceFinisher to the callback. Main - // thread is blocked on waiting for the future to be completed. When onComplete is called on - // the grpc thread, the future is completed, however we might not have enough time for - // TraceFinisher to run. Add a 1 second time out to wait for the callback. This shouldn't - // have any impact on production code. - verify(mockInstruments, timeout(1000)) - .recordRetryCount(retryCount.capture(), retryCountAttribute.capture()); + Collection allMetricData = metricReader.collectAllMetrics(); + MetricData metricData = getMetricData(allMetricData, RETRY_COUNT_NAME); + Attributes expectedAttributes = + baseAttributes + .toBuilder() + .put(TABLE_ID, TABLE) + .put(ZONE_ID, ZONE) + .put(CLUSTER_ID, CLUSTER) + .put(CLIENT_NAME, "java-bigtable") + .put(METHOD, "Bigtable.MutateRow") + .put(STATUS, "OK") + .build(); - assertThat(retryCount.getValue()).isEqualTo(fakeService.getAttemptCounter().get() - 1); + long value = getAggregatedValue(metricData, expectedAttributes); + assertThat(value).isEqualTo(fakeService.getAttemptCounter().get() - 1); } @Test public void testMutateRowAttemptsTagValues() { - when(mockFactory.newTracer(any(), any(), any())) - .thenReturn( - new BuiltinMetricsTracer( - ApiTracerFactory.OperationType.Unary, - SpanName.of("Bigtable", "MutateRow"), - mockInstruments, - baseAttributes)); - stub.mutateRowCallable() .call(RowMutation.create(TABLE, "random-row").setCell("cf", "q", "value")); - ArgumentCaptor value = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor attributes = ArgumentCaptor.forClass(Attributes.class); - - // Set a timeout to reduce flakiness of this test. BasicRetryingFuture will set - // attempt succeeded and set the response which will call complete() in AbstractFuture which - // calls releaseWaiters(). onOperationComplete() is called in TracerFinisher which will be - // called after the mutateRow call is returned. So there's a race between when the call - // returns and when the record() is called in onOperationCompletion(). - verify(mockInstruments, timeout(50).times(fakeService.getAttemptCounter().get())) - .recordAttemptLatencies(value.capture(), attributes.capture()); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsConstants.PROJECT_ID)) - .collect(Collectors.toList())) - .containsExactly(PROJECT_ID, PROJECT_ID, PROJECT_ID); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsConstants.INSTANCE_ID)) - .collect(Collectors.toList())) - .containsExactly(INSTANCE_ID, INSTANCE_ID, INSTANCE_ID); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsConstants.APP_PROFILE)) - .collect(Collectors.toList())) - .containsExactly(APP_PROFILE_ID, APP_PROFILE_ID, APP_PROFILE_ID); - assertThat( - attributes.getAllValues().stream().map(a -> a.get(STATUS)).collect(Collectors.toList())) - .containsExactly("UNAVAILABLE", "UNAVAILABLE", "OK"); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(TABLE_ID)) - .collect(Collectors.toList())) - .containsExactly(TABLE, TABLE, TABLE); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(ZONE_ID)) - .collect(Collectors.toList())) - .containsExactly("global", "global", ZONE); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(CLUSTER_ID)) - .collect(Collectors.toList())) - .containsExactly("unspecified", "unspecified", CLUSTER); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(CLIENT_NAME)) - .collect(Collectors.toList())) - .containsExactly("java-bigtable", "java-bigtable", "java-bigtable"); - assertThat( - attributes.getAllValues().stream().map(a -> a.get(METHOD)).collect(Collectors.toList())) - .containsExactly("Bigtable.MutateRow", "Bigtable.MutateRow", "Bigtable.MutateRow"); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(STREAMING)) - .collect(Collectors.toList())) - .containsExactly(false, false, false); + Collection allMetricData = metricReader.collectAllMetrics(); + MetricData metricData = getMetricData(allMetricData, ATTEMPT_LATENCIES_NAME); + + Attributes expected1 = + baseAttributes + .toBuilder() + .put(STATUS, "UNAVAILABLE") + .put(TABLE_ID, TABLE) + .put(ZONE_ID, "global") + .put(CLUSTER_ID, "unspecified") + .put(METHOD, "Bigtable.MutateRow") + .put(CLIENT_NAME, "java-bigtable") + .put(STREAMING, false) + .build(); + + Attributes expected2 = + baseAttributes + .toBuilder() + .put(STATUS, "OK") + .put(TABLE_ID, TABLE) + .put(ZONE_ID, ZONE) + .put(CLUSTER_ID, CLUSTER) + .put(METHOD, "Bigtable.MutateRow") + .put(CLIENT_NAME, "java-bigtable") + .put(STREAMING, false) + .build(); + + verifyAttributes(metricData, expected1); + verifyAttributes(metricData, expected2); } @Test public void testReadRowsAttemptsTagValues() { - when(mockFactory.newTracer(any(), any(), any())) - .thenReturn( - new BuiltinMetricsTracer( - ApiTracerFactory.OperationType.ServerStreaming, - SpanName.of("Bigtable", "ReadRows"), - mockInstruments, - baseAttributes)); - Lists.newArrayList(stub.readRowsCallable().call(Query.create("fake-table")).iterator()); - ArgumentCaptor value = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor attributes = ArgumentCaptor.forClass(Attributes.class); - - // Set a timeout to reduce flakiness of this test. BasicRetryingFuture will set - // attempt succeeded and set the response which will call complete() in AbstractFuture which - // calls releaseWaiters(). onOperationComplete() is called in TracerFinisher which will be - // called after the mutateRow call is returned. So there's a race between when the call - // returns and when the record() is called in onOperationCompletion(). - verify(mockInstruments, timeout(50).times(fakeService.getAttemptCounter().get())) - .recordAttemptLatencies(value.capture(), attributes.capture()); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsConstants.PROJECT_ID)) - .collect(Collectors.toList())) - .containsExactly(PROJECT_ID, PROJECT_ID); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsConstants.INSTANCE_ID)) - .collect(Collectors.toList())) - .containsExactly(INSTANCE_ID, INSTANCE_ID); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsConstants.APP_PROFILE)) - .collect(Collectors.toList())) - .containsExactly(APP_PROFILE_ID, APP_PROFILE_ID); - assertThat( - attributes.getAllValues().stream().map(a -> a.get(STATUS)).collect(Collectors.toList())) - .containsExactly("UNAVAILABLE", "OK"); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(TABLE_ID)) - .collect(Collectors.toList())) - .containsExactly(TABLE, TABLE); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(ZONE_ID)) - .collect(Collectors.toList())) - .containsExactly("global", ZONE); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(CLUSTER_ID)) - .collect(Collectors.toList())) - .containsExactly("unspecified", CLUSTER); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(CLIENT_NAME)) - .collect(Collectors.toList())) - .containsExactly("java-bigtable", "java-bigtable"); - assertThat( - attributes.getAllValues().stream().map(a -> a.get(METHOD)).collect(Collectors.toList())) - .containsExactly("Bigtable.ReadRows", "Bigtable.ReadRows"); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(STREAMING)) - .collect(Collectors.toList())) - .containsExactly(true, true); + Collection allMetricData = metricReader.collectAllMetrics(); + MetricData metricData = getMetricData(allMetricData, ATTEMPT_LATENCIES_NAME); + + Attributes expected1 = + baseAttributes + .toBuilder() + .put(STATUS, "UNAVAILABLE") + .put(TABLE_ID, TABLE) + .put(ZONE_ID, "global") + .put(CLUSTER_ID, "unspecified") + .put(METHOD, "Bigtable.ReadRows") + .put(CLIENT_NAME, "java-bigtable") + .put(STREAMING, true) + .build(); + + Attributes expected2 = + baseAttributes + .toBuilder() + .put(STATUS, "OK") + .put(TABLE_ID, TABLE) + .put(ZONE_ID, ZONE) + .put(CLUSTER_ID, CLUSTER) + .put(METHOD, "Bigtable.ReadRows") + .put(CLIENT_NAME, "java-bigtable") + .put(STREAMING, true) + .build(); + + verifyAttributes(metricData, expected1); + verifyAttributes(metricData, expected2); } @Test public void testBatchBlockingLatencies() throws InterruptedException { - when(mockFactory.newTracer(any(), any(), any())) - .thenReturn( - new BuiltinMetricsTracer( - ApiTracerFactory.OperationType.ServerStreaming, - SpanName.of("Bigtable", "MutateRows"), - mockInstruments, - baseAttributes)); - try (Batcher batcher = stub.newMutateRowsBatcher(TABLE, null)) { for (int i = 0; i < 6; i++) { batcher.add(RowMutationEntry.create("key").setCell("f", "q", "v")); @@ -651,172 +556,149 @@ public void testBatchBlockingLatencies() throws InterruptedException { batcher.close(); int expectedNumRequests = 6 / batchElementCount; - ArgumentCaptor throttledTime = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor attributes = ArgumentCaptor.forClass(Attributes.class); - verify(mockInstruments, timeout(5000).times(expectedNumRequests)) - .recordClientBlockingLatencies(throttledTime.capture(), attributes.capture()); + Collection allMetricData = metricReader.collectAllMetrics(); + MetricData applicationLatency = getMetricData(allMetricData, CLIENT_BLOCKING_LATENCIES_NAME); + Attributes expectedAttributes = + baseAttributes + .toBuilder() + .put(TABLE_ID, TABLE) + .put(ZONE_ID, ZONE) + .put(CLUSTER_ID, CLUSTER) + .put(METHOD, "Bigtable.MutateRows") + .put(CLIENT_NAME, "java-bigtable") + .build(); + + long value = getAggregatedValue(applicationLatency, expectedAttributes); // After the first request is sent, batcher will block on add because of the server latency. - // Blocking latency should be around server latency. - assertThat(throttledTime.getAllValues().get(1)).isAtLeast(SERVER_LATENCY - 10); - assertThat(throttledTime.getAllValues().get(2)).isAtLeast(SERVER_LATENCY - 10); - - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsConstants.PROJECT_ID)) - .collect(Collectors.toList())) - .containsExactly(PROJECT_ID, PROJECT_ID, PROJECT_ID); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsConstants.INSTANCE_ID)) - .collect(Collectors.toList())) - .containsExactly(INSTANCE_ID, INSTANCE_ID, INSTANCE_ID); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(BuiltinMetricsConstants.APP_PROFILE)) - .collect(Collectors.toList())) - .containsExactly(APP_PROFILE_ID, APP_PROFILE_ID, APP_PROFILE_ID); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(TABLE_ID)) - .collect(Collectors.toList())) - .containsExactly(TABLE, TABLE, TABLE); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(ZONE_ID)) - .collect(Collectors.toList())) - .containsExactly(ZONE, ZONE, ZONE); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(CLUSTER_ID)) - .collect(Collectors.toList())) - .containsExactly(CLUSTER, CLUSTER, CLUSTER); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(CLIENT_NAME)) - .collect(Collectors.toList())) - .containsExactly("java-bigtable", "java-bigtable", "java-bigtable"); - assertThat( - attributes.getAllValues().stream() - .map(a -> a.get(METHOD)) - .collect(Collectors.toList())) - .containsExactly("Bigtable.MutateRows", "Bigtable.MutateRows", "Bigtable.MutateRows"); + // Blocking latency should be around server latency. So each data point would be at least + // (SERVER_LATENCY - 10). + long expected = (SERVER_LATENCY - 10) * (expectedNumRequests - 1) / expectedNumRequests; + assertThat(value).isAtLeast(expected); } } @Test - public void testQueuedOnChannelServerStreamLatencies() throws InterruptedException { - when(mockFactory.newTracer(any(), any(), any())) - .thenReturn( - new BuiltinMetricsTracer( - ApiTracerFactory.OperationType.ServerStreaming, - SpanName.of("Bigtable", "ReadRows"), - mockInstruments, - baseAttributes)); - + public void testQueuedOnChannelServerStreamLatencies() { stub.readRowsCallable().all().call(Query.create(TABLE)); - ArgumentCaptor blockedTime = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor attributes = ArgumentCaptor.forClass(Attributes.class); - - verify(mockInstruments, timeout(1000).times(fakeService.attemptCounter.get())) - .recordClientBlockingLatencies(blockedTime.capture(), attributes.capture()); + Collection allMetricData = metricReader.collectAllMetrics(); + MetricData clientLatency = getMetricData(allMetricData, CLIENT_BLOCKING_LATENCIES_NAME); + + Attributes attributes = + baseAttributes + .toBuilder() + .put(TABLE_ID, TABLE) + .put(CLUSTER_ID, CLUSTER) + .put(ZONE_ID, ZONE) + .put(METHOD, "Bigtable.ReadRows") + .put(CLIENT_NAME, "java-bigtable") + .build(); - assertThat(blockedTime.getAllValues().get(1)).isAtLeast(CHANNEL_BLOCKING_LATENCY); + long value = getAggregatedValue(clientLatency, attributes); + assertThat(value).isAtLeast(CHANNEL_BLOCKING_LATENCY); } @Test - public void testQueuedOnChannelUnaryLatencies() throws InterruptedException { - when(mockFactory.newTracer(any(), any(), any())) - .thenReturn( - new BuiltinMetricsTracer( - ApiTracerFactory.OperationType.ServerStreaming, - SpanName.of("Bigtable", "MutateRow"), - mockInstruments, - baseAttributes)); + public void testQueuedOnChannelUnaryLatencies() { stub.mutateRowCallable().call(RowMutation.create(TABLE, "a-key").setCell("f", "q", "v")); - ArgumentCaptor blockedTime = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor attributes = ArgumentCaptor.forClass(Attributes.class); - - verify(mockInstruments, timeout(1000).times(fakeService.attemptCounter.get())) - .recordClientBlockingLatencies(blockedTime.capture(), attributes.capture()); + Collection allMetricData = metricReader.collectAllMetrics(); + MetricData clientLatency = getMetricData(allMetricData, CLIENT_BLOCKING_LATENCIES_NAME); + + Attributes attributes = + baseAttributes + .toBuilder() + .put(TABLE_ID, TABLE) + .put(CLUSTER_ID, CLUSTER) + .put(ZONE_ID, ZONE) + .put(METHOD, "Bigtable.MutateRow") + .put(CLIENT_NAME, "java-bigtable") + .build(); - assertThat(blockedTime.getAllValues().get(1)).isAtLeast(CHANNEL_BLOCKING_LATENCY); - assertThat(blockedTime.getAllValues().get(2)).isAtLeast(CHANNEL_BLOCKING_LATENCY); + long expected = CHANNEL_BLOCKING_LATENCY * 2 / 3; + long actual = getAggregatedValue(clientLatency, attributes); + assertThat(actual).isAtLeast(expected); } @Test public void testPermanentFailure() { - when(mockFactory.newTracer(any(), any(), any())) - .thenReturn( - new BuiltinMetricsTracer( - ApiTracerFactory.OperationType.ServerStreaming, - SpanName.of("Bigtable", "ReadRows"), - mockInstruments, - baseAttributes)); - try { Lists.newArrayList(stub.readRowsCallable().call(Query.create(BAD_TABLE_ID)).iterator()); Assert.fail("Request should throw not found error"); } catch (NotFoundException e) { } - ArgumentCaptor attemptLatency = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor operationLatency = ArgumentCaptor.forClass(Long.class); - ArgumentCaptor attemptAttributes = ArgumentCaptor.forClass(Attributes.class); - ArgumentCaptor operationAttributes = ArgumentCaptor.forClass(Attributes.class); - - verify(mockInstruments, timeout(50)) - .recordAttemptLatencies(attemptLatency.capture(), attemptAttributes.capture()); - verify(mockInstruments, timeout(50)) - .recordOperationLatencies(operationLatency.capture(), operationAttributes.capture()); - - // verify attempt attributes - assertThat( - attemptAttributes.getAllValues().stream() - .map(a -> a.get(STATUS)) - .collect(Collectors.toList())) - .containsExactly("NOT_FOUND"); - assertThat( - attemptAttributes.getAllValues().stream() - .map(a -> a.get(TABLE_ID)) - .collect(Collectors.toList())) - .containsExactly(BAD_TABLE_ID); - assertThat( - attemptAttributes.getAllValues().stream() - .map(a -> a.get(ZONE_ID)) - .collect(Collectors.toList())) - .containsExactly("global"); - assertThat( - attemptAttributes.getAllValues().stream() - .map(a -> a.get(CLUSTER_ID)) - .collect(Collectors.toList())) - .containsExactly("unspecified"); - - // verify operation attributes - assertThat( - operationAttributes.getAllValues().stream() - .map(a -> a.get(STATUS)) - .collect(Collectors.toList())) - .containsExactly("NOT_FOUND"); - assertThat( - operationAttributes.getAllValues().stream() - .map(a -> a.get(TABLE_ID)) - .collect(Collectors.toList())) - .containsExactly(BAD_TABLE_ID); - assertThat( - operationAttributes.getAllValues().stream() - .map(a -> a.get(ZONE_ID)) - .collect(Collectors.toList())) - .containsExactly("global"); - assertThat( - operationAttributes.getAllValues().stream() - .map(a -> a.get(CLUSTER_ID)) - .collect(Collectors.toList())) - .containsExactly("unspecified"); + Collection allMetricData = metricReader.collectAllMetrics(); + MetricData attemptLatency = getMetricData(allMetricData, ATTEMPT_LATENCIES_NAME); + + Attributes expected = + baseAttributes + .toBuilder() + .put(STATUS, "NOT_FOUND") + .put(TABLE_ID, BAD_TABLE_ID) + .put(CLUSTER_ID, "unspecified") + .put(ZONE_ID, "global") + .put(STREAMING, true) + .put(METHOD, "Bigtable.ReadRows") + .put(CLIENT_NAME, "java-bigtable") + .build(); + + verifyAttributes(attemptLatency, expected); + + MetricData opLatency = getMetricData(allMetricData, OPERATION_LATENCIES_NAME); + verifyAttributes(opLatency, expected); + } + + private MetricData getMetricData(Collection allMetricData, String metricName) { + List metricDataList = + allMetricData.stream() + .filter(md -> md.getName().equals(metricName)) + .collect(Collectors.toList()); + assertThat(metricDataList.size()).isEqualTo(1); + + return metricDataList.get(0); + } + + private long getAggregatedValue(MetricData metricData, Attributes attributes) { + switch (metricData.getType()) { + case HISTOGRAM: + HistogramPointData hd = + metricData.getHistogramData().getPoints().stream() + .filter(pd -> pd.getAttributes().equals(attributes)) + .collect(Collectors.toList()) + .get(0); + return (long) hd.getSum() / hd.getCount(); + case LONG_SUM: + LongPointData ld = + metricData.getLongSumData().getPoints().stream() + .filter(pd -> pd.getAttributes().equals(attributes)) + .collect(Collectors.toList()) + .get(0); + return ld.getValue(); + } + return 0; + } + + private void verifyAttributes(MetricData metricData, Attributes attributes) { + switch (metricData.getType()) { + case HISTOGRAM: + List hd = + metricData.getHistogramData().getPoints().stream() + .filter(pd -> pd.getAttributes().equals(attributes)) + .collect(Collectors.toList()); + assertThat(hd.size()).isGreaterThan(0); + break; + case LONG_SUM: + List ld = + metricData.getLongSumData().getPoints().stream() + .filter(pd -> pd.getAttributes().equals(attributes)) + .collect(Collectors.toList()); + assertThat(ld.size()).isGreaterThan(0); + break; + } } private static class FakeService extends BigtableGrpc.BigtableImplBase { diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/AbstractTestEnv.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/AbstractTestEnv.java index 069b1d979f..fd363099d9 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/AbstractTestEnv.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/AbstractTestEnv.java @@ -25,7 +25,6 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; -import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.io.IOException; import java.util.HashMap; import java.util.List; @@ -71,8 +70,6 @@ public abstract BigtableTableAdminClient getTableAdminClientForInstance(String i public abstract String getInstanceId(); - public abstract InMemoryMetricReader getMetricReader(); - /** Try to guess the primary cluster id */ public synchronized String getPrimaryClusterId() { if (primaryClusterId != null) { diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java index c2a2ed300c..ba0fda8b2c 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/CloudEnv.java @@ -26,7 +26,6 @@ import com.google.cloud.bigtable.data.v2.BigtableDataClient; import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings; -import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsView; import com.google.common.base.Joiner; import com.google.common.base.MoreObjects; import com.google.common.base.Predicate; @@ -42,11 +41,6 @@ import io.grpc.ManagedChannelBuilder; import io.grpc.Metadata; import io.grpc.MethodDescriptor; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.metrics.SdkMeterProvider; -import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; -import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -103,8 +97,6 @@ public boolean apply(InetSocketAddress input) { private BigtableTableAdminClient tableAdminClient; private BigtableInstanceAdminClient instanceAdminClient; - private final InMemoryMetricReader metricReader = InMemoryMetricReader.create(); - static CloudEnv fromSystemProperties() { return new CloudEnv( getOptionalProperty(DATA_ENDPOINT_PROPERTY_NAME, ""), @@ -135,7 +127,6 @@ private CloudEnv( setupRemoteAddrInterceptor(dataSettings.stubSettings()); configureUserAgent(dataSettings.stubSettings()); - configureMetricsReader(dataSettings.stubSettings()); this.tableAdminSettings = BigtableTableAdminSettings.newBuilder().setProjectId(projectId).setInstanceId(instanceId); @@ -274,20 +265,6 @@ private void configureUserAgent(EnhancedBigtableStubSettings.Builder stubSetting stubSettings.setHeaderProvider(FixedHeaderProvider.create(newHeaders)); } - private void configureMetricsReader(EnhancedBigtableStubSettings.Builder stubSettings) { - SdkMeterProviderBuilder meterProvider = - SdkMeterProvider.builder().registerMetricReader(metricReader); - try { - BuiltinMetricsView.registerBuiltinMetrics(projectId, meterProvider); - } catch (IOException e) { - throw new RuntimeException("Failed to register views"); - } - OpenTelemetry openTelemetry = - OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); - - stubSettings.setOpenTelemetry(openTelemetry); - } - @Override void start() throws IOException { dataClient = BigtableDataClient.create(dataSettings.build()); @@ -365,11 +342,6 @@ public BigtableTableAdminSettings getTableAdminSettings() { } } - @Override - public InMemoryMetricReader getMetricReader() { - return this.metricReader; - } - @Override public String getProjectId() { return projectId; diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/EmulatorEnv.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/EmulatorEnv.java index c570811434..bec3e0eef2 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/EmulatorEnv.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/test_helpers/env/EmulatorEnv.java @@ -24,7 +24,6 @@ import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.emulator.v2.Emulator; import com.google.common.base.Strings; -import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.io.IOException; import java.nio.file.Paths; @@ -141,11 +140,6 @@ public BigtableInstanceAdminClient getInstanceAdminClient() { throw new UnsupportedOperationException("InstanceAdminClient is not supported with emulator"); } - @Override - public InMemoryMetricReader getMetricReader() { - throw new UnsupportedOperationException("Metric reader is not supported with emulator"); - } - public String getKmsKeyName() { throw new UnsupportedOperationException("CMEK is not supported with emulator"); } From 28ca070dbf62efa799906b8b834fb38ab6df4228 Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Wed, 24 Jan 2024 10:39:52 -0500 Subject: [PATCH 19/34] rebase on otel --- google-cloud-bigtable/pom.xml | 9 + .../data/v2/stub/EnhancedBigtableStub.java | 45 +-- .../BigtableCloudMonitoringExporter.java | 6 +- .../stub/metrics/BigtableExporterUtils.java | 19 +- .../metrics/BuiltinMetricsAttributes.java | 34 --- .../stub/metrics/BuiltinMetricsConstants.java | 4 +- .../bigtable/data/v2/it/BuiltinMetricsIT.java | 266 +++++++++++++++--- .../BigtableCloudMonitoringExporterTest.java | 14 +- .../stub/metrics/BuiltinMetricsTestUtils.java | 103 +++++++ .../metrics/BuiltinMetricsTracerTest.java | 55 +--- 10 files changed, 380 insertions(+), 175 deletions(-) delete mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java create mode 100644 google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java diff --git a/google-cloud-bigtable/pom.xml b/google-cloud-bigtable/pom.xml index c95d989edc..66f7992551 100644 --- a/google-cloud-bigtable/pom.xml +++ b/google-cloud-bigtable/pom.xml @@ -339,6 +339,10 @@ io.opentelemetry opentelemetry-api + + io.opentelemetry + opentelemetry-sdk + io.opentelemetry opentelemetry-sdk-metrics @@ -347,6 +351,11 @@ io.opentelemetry opentelemetry-sdk-common + + io.opentelemetry + opentelemetry-sdk-testing + test + com.google.cloud google-cloud-monitoring diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java index 116cdb5b32..457e791e60 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java @@ -15,25 +15,9 @@ */ package com.google.cloud.bigtable.data.v2.stub; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_VIEW; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APP_PROFILE; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_VIEW; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_VIEW; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PROJECT_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_VIEW; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_SELECTOR; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_VIEW; import com.google.api.core.BetaApi; import com.google.api.core.InternalApi; @@ -110,10 +94,10 @@ import com.google.cloud.bigtable.data.v2.stub.changestream.GenerateInitialChangeStreamPartitionsUserCallable; import com.google.cloud.bigtable.data.v2.stub.changestream.ReadChangeStreamResumptionStrategy; import com.google.cloud.bigtable.data.v2.stub.changestream.ReadChangeStreamUserCallable; -import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableCloudMonitoringExporter; import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracerStreamingCallable; import com.google.cloud.bigtable.data.v2.stub.metrics.BigtableTracerUnaryCallable; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTracerFactory; +import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsView; import com.google.cloud.bigtable.data.v2.stub.metrics.CompositeTracerFactory; import com.google.cloud.bigtable.data.v2.stub.metrics.MetricsTracerFactory; import com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants; @@ -148,8 +132,7 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.metrics.SdkMeterProvider; -import io.opentelemetry.sdk.metrics.export.MetricExporter; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; import io.opentelemetry.sdk.resources.Resource; import java.io.IOException; import java.net.URI; @@ -328,26 +311,14 @@ private static BuiltinMetricsTracerFactory setupBuiltinMetricsTracerFactory( if (settings.getOpenTelemetry() != null) { return BuiltinMetricsTracerFactory.create(settings.getOpenTelemetry(), attributes); } else if (settings.isBuiltinMetricsEnabled()) { - MetricExporter metricExporter = - BigtableCloudMonitoringExporter.create( - settings.getProjectId(), settings.getCredentialsProvider().getCredentials()); Resource resource = Resource.create(attributes); - SdkMeterProvider meterProvider = - SdkMeterProvider.builder() - .setResource(resource) - .registerMetricReader(PeriodicMetricReader.create(metricExporter)) - .registerView(OPERATION_LATENCIES_SELECTOR, OPERATION_LATENCIES_VIEW) - .registerView(ATTEMPT_LATENCIES_SELECTOR, ATTEMPT_LATENCIES_VIEW) - .registerView(SERVER_LATENCIES_SELECTOR, SERVER_LATENCIES_VIEW) - .registerView(FIRST_RESPONSE_LATENCIES_SELECTOR, FIRST_RESPONSE_LATENCIES_VIEW) - .registerView( - APPLICATION_BLOCKING_LATENCIES_SELECTOR, APPLICATION_BLOCKING_LATENCIES_VIEW) - .registerView(CLIENT_BLOCKING_LATENCIES_SELECTOR, CLIENT_BLOCKING_LATENCIES_VIEW) - .registerView(RETRY_COUNT_SELECTOR, RETRY_COUNT_VIEW) - .registerView(CONNECTIVITY_ERROR_COUNT_SELECTOR, CONNECTIVITY_ERROR_COUNT_VIEW) - .build(); + SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder().setResource(resource); + BuiltinMetricsView.registerBuiltinMetrics( + settings.getProjectId(), + settings.getCredentialsProvider().getCredentials(), + meterProvider); OpenTelemetry openTelemetry = - OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build(); + OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); return BuiltinMetricsTracerFactory.create(openTelemetry, attributes); } return null; diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java index 5ca8271791..f2db06860b 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java @@ -19,6 +19,7 @@ import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; +import com.google.api.core.InternalApi; import com.google.api.gax.core.FixedCredentialsProvider; import com.google.auth.Credentials; import com.google.cloud.monitoring.v3.MetricServiceClient; @@ -49,7 +50,8 @@ *

The exporter will look for all bigtable owned metrics under bigtable.googleapis.com * instrumentation scope and upload it via the Google Cloud Monitoring API. */ -final class BigtableCloudMonitoringExporter implements MetricExporter { +@InternalApi +public final class BigtableCloudMonitoringExporter implements MetricExporter { private static final Logger logger = Logger.getLogger(BigtableCloudMonitoringExporter.class.getName()); @@ -64,7 +66,7 @@ final class BigtableCloudMonitoringExporter implements MetricExporter { private CompletableResultCode lastExportCode; - static BigtableCloudMonitoringExporter create(String projectId, Credentials credentials) + public static BigtableCloudMonitoringExporter create(String projectId, Credentials credentials) throws IOException { MetricServiceSettings.Builder settingsBuilder = MetricServiceSettings.newBuilder(); settingsBuilder.setCredentialsProvider(FixedCredentialsProvider.create(credentials)); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java index 7c3dc09fc4..5fc8499325 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java @@ -25,12 +25,12 @@ import static com.google.api.MetricDescriptor.ValueType.DISTRIBUTION; import static com.google.api.MetricDescriptor.ValueType.DOUBLE; import static com.google.api.MetricDescriptor.ValueType.INT64; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_UID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLUSTER_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.INSTANCE_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.PROJECT_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.TABLE_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ZONE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_UID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PROJECT_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID; import com.google.api.Distribution; import com.google.api.Metric; @@ -104,9 +104,10 @@ static List convertCollectionToListOfTimeSeries( List allTimeSeries = new ArrayList<>(); for (MetricData metricData : collection) { - // TODO: scope will be defined in BuiltinMetricsConstants. Update this field in the following - // PR. - if (!metricData.getInstrumentationScopeInfo().getName().equals("bigtable.googleapis.com")) { + if (!metricData + .getInstrumentationScopeInfo() + .getName() + .equals(BuiltinMetricsConstants.SCOPE)) { continue; } metricData.getData().getPoints().stream() diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java deleted file mode 100644 index e34659444b..0000000000 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsAttributes.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.cloud.bigtable.data.v2.stub.metrics; - -import io.opentelemetry.api.common.AttributeKey; - -class BuiltinMetricsAttributes { - - static final AttributeKey PROJECT_ID = AttributeKey.stringKey("project_id"); - static final AttributeKey INSTANCE_ID = AttributeKey.stringKey("instance"); - static final AttributeKey TABLE_ID = AttributeKey.stringKey("table"); - static final AttributeKey CLUSTER_ID = AttributeKey.stringKey("cluster"); - static final AttributeKey ZONE_ID = AttributeKey.stringKey("zone"); - - static final AttributeKey APP_PROFILE = AttributeKey.stringKey("app_profile"); - static final AttributeKey STREAMING = AttributeKey.booleanKey("streaming"); - static final AttributeKey METHOD = AttributeKey.stringKey("method"); - static final AttributeKey STATUS = AttributeKey.stringKey("status"); - static final AttributeKey CLIENT_NAME = AttributeKey.stringKey("client_name"); - static final AttributeKey CLIENT_UID = AttributeKey.stringKey("client_uid"); -} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java index d10829a5d4..a7cd656874 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java @@ -37,7 +37,7 @@ public class BuiltinMetricsConstants { static final AttributeKey CLIENT_UID = AttributeKey.stringKey("client_uid"); public static final AttributeKey APP_PROFILE = AttributeKey.stringKey("app_profile"); - static final AttributeKey STREAMING = AttributeKey.booleanKey("streaming"); + public static final AttributeKey STREAMING = AttributeKey.booleanKey("streaming"); static final AttributeKey METHOD = AttributeKey.stringKey("method"); static final AttributeKey STATUS = AttributeKey.stringKey("status"); static final AttributeKey CLIENT_NAME = AttributeKey.stringKey("client_name"); @@ -57,7 +57,7 @@ public class BuiltinMetricsConstants { 0.0, 0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0, 16.0, 20.0, 25.0, 30.0, 40.0, 50.0, 65.0, 80.0, 100.0, 130.0, 160.0, 200.0, 250.0, 300.0, 400.0, 500.0, 650.0, 800.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0, 50000.0, - 100000.0)); + 100000.0, 200000.0, 400000.0, 800000.0, 1600000.0)); static final String SCOPE = "bigtable.googleapis.com"; diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java index d3d355e39a..3c8f2428de 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java @@ -15,33 +15,63 @@ */ package com.google.cloud.bigtable.data.v2.it; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.getAggregatedValue; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.getMetricData; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.getStartTimeSeconds; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.verifyAttributes; +import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static com.google.common.truth.TruthJUnit.assume; import com.google.api.client.util.Lists; +import com.google.cloud.bigtable.admin.v2.BigtableInstanceAdminClient; import com.google.cloud.bigtable.admin.v2.BigtableTableAdminClient; +import com.google.cloud.bigtable.admin.v2.models.AppProfile; +import com.google.cloud.bigtable.admin.v2.models.CreateAppProfileRequest; import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest; import com.google.cloud.bigtable.admin.v2.models.Table; +import com.google.cloud.bigtable.data.v2.BigtableDataClient; +import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.data.v2.models.Query; import com.google.cloud.bigtable.data.v2.models.Row; import com.google.cloud.bigtable.data.v2.models.RowMutation; +import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; +import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsView; import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv; import com.google.cloud.bigtable.test_helpers.env.PrefixGenerator; import com.google.cloud.bigtable.test_helpers.env.TestEnvRule; import com.google.cloud.monitoring.v3.MetricServiceClient; import com.google.common.base.Stopwatch; +import com.google.common.collect.BoundType; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Range; import com.google.monitoring.v3.ListTimeSeriesRequest; import com.google.monitoring.v3.ListTimeSeriesResponse; +import com.google.monitoring.v3.Point; import com.google.monitoring.v3.ProjectName; import com.google.monitoring.v3.TimeInterval; +import com.google.monitoring.v3.TimeSeries; +import com.google.protobuf.Timestamp; import com.google.protobuf.util.Timestamps; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import java.util.stream.Collectors; +import org.junit.After; +import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; @@ -57,19 +87,28 @@ public class BuiltinMetricsIT { private static final Logger logger = Logger.getLogger(BuiltinMetricsIT.class.getName()); @Rule public Timeout globalTimeout = Timeout.seconds(900); - private static Table table; - private static BigtableTableAdminClient tableAdminClient; - private static MetricServiceClient metricClient; + + private Table tableCustomOtel; + private Table tableDefault; + private BigtableDataClient clientCustomOtel; + private BigtableDataClient clientDefault; + private BigtableTableAdminClient tableAdminClient; + private BigtableInstanceAdminClient instanceAdminClient; + private MetricServiceClient metricClient; + + private InMemoryMetricReader metricReader; + private String appProfileCustomOtel; + private String appProfileDefault; public static String[] VIEWS = { "operation_latencies", "attempt_latencies", "connectivity_error_count", - "application_blocking_latencies" + "application_blocking_latencies", }; - @BeforeClass - public static void setUpClass() throws IOException { + @Before + public void setup() throws IOException { assume() .withMessage("Builtin metrics integration test is not supported by emulator") .that(testEnvRule.env()) @@ -79,39 +118,138 @@ public static void setUpClass() throws IOException { metricClient = MetricServiceClient.create(); tableAdminClient = testEnvRule.env().getTableAdminClient(); + instanceAdminClient = testEnvRule.env().getInstanceAdminClient(); + appProfileCustomOtel = PrefixGenerator.newPrefix("test1"); + appProfileDefault = PrefixGenerator.newPrefix("test2"); + instanceAdminClient.createAppProfile( + CreateAppProfileRequest.of(testEnvRule.env().getInstanceId(), appProfileCustomOtel) + .setRoutingPolicy( + AppProfile.SingleClusterRoutingPolicy.of(testEnvRule.env().getPrimaryClusterId())) + .setIsolationPolicy(AppProfile.StandardIsolationPolicy.of(AppProfile.Priority.LOW))); + instanceAdminClient.createAppProfile( + CreateAppProfileRequest.of(testEnvRule.env().getInstanceId(), appProfileDefault) + .setRoutingPolicy( + AppProfile.SingleClusterRoutingPolicy.of(testEnvRule.env().getPrimaryClusterId())) + .setIsolationPolicy(AppProfile.StandardIsolationPolicy.of(AppProfile.Priority.LOW))); + + metricReader = InMemoryMetricReader.create(); + + SdkMeterProviderBuilder meterProvider = + SdkMeterProvider.builder().registerMetricReader(metricReader); + BuiltinMetricsView.registerBuiltinMetrics(testEnvRule.env().getProjectId(), meterProvider); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); + + BigtableDataSettings.Builder settings = testEnvRule.env().getDataClientSettings().toBuilder(); + + clientCustomOtel = + BigtableDataClient.create( + settings.setOpenTelemetry(openTelemetry).setAppProfileId(appProfileCustomOtel).build()); + clientDefault = BigtableDataClient.create(settings.setAppProfileId(appProfileDefault).build()); } - @AfterClass - public static void tearDown() { + @After + public void tearDown() { if (metricClient != null) { metricClient.close(); } - if (table != null) { - tableAdminClient.deleteTable(table.getId()); + if (tableCustomOtel != null) { + tableAdminClient.deleteTable(tableCustomOtel.getId()); + } + if (tableDefault != null) { + tableAdminClient.deleteTable(tableDefault.getId()); + } + if (instanceAdminClient != null) { + instanceAdminClient.deleteAppProfile( + testEnvRule.env().getInstanceId(), appProfileCustomOtel, true); + } + if (clientCustomOtel != null) { + clientCustomOtel.close(); + } + if (clientDefault != null) { + clientDefault.close(); } } @Test - public void testBuiltinMetrics() throws Exception { - logger.info("Started testing builtin metrics"); - table = + public void testBuiltinMetricsWithDefaultOTEL() throws Exception { + logger.info("Started testing builtin metrics with default OTEL"); + tableDefault = tableAdminClient.createTable( - CreateTableRequest.of(PrefixGenerator.newPrefix("BuiltinMetricsIT#test")) + CreateTableRequest.of(PrefixGenerator.newPrefix("BuiltinMetricsIT#test1")) .addFamily("cf")); - logger.info("Create table: " + table.getId()); + logger.info("Create default table: " + tableDefault.getId()); + clientDefault.mutateRow( + RowMutation.create(tableDefault.getId(), "a-new-key").setCell("cf", "q", "abc")); + ArrayList rows = + Lists.newArrayList(clientDefault.readRows(Query.create(tableDefault.getId()).limit(10))); + + Stopwatch stopwatch = Stopwatch.createStarted(); + + ProjectName name = ProjectName.of(testEnvRule.env().getProjectId()); + + Collection fromMetricReader = metricReader.collectAllMetrics(); + + // Restrict time to last 10 minutes and 5 minutes after the request + long startMillis = System.currentTimeMillis() - Duration.ofMinutes(10).toMillis(); + long endMillis = startMillis + Duration.ofMinutes(15).toMillis(); + TimeInterval interval = + TimeInterval.newBuilder() + .setStartTime(Timestamps.fromMillis(startMillis)) + .setEndTime(Timestamps.fromMillis(endMillis)) + .build(); + + for (String view : VIEWS) { + // Filter on instance and method name + // Verify that metrics are published for MutateRow request + String metricFilter = + String.format( + "metric.type=\"bigtable.googleapis.com/client/%s\" " + + "AND resource.labels.instance=\"%s\" AND metric.labels.method=\"Bigtable.MutateRow\"" + + " AND resource.labels.table=\"%s\" AND metric.labels.app_profile=\"%s\"", + view, testEnvRule.env().getInstanceId(), tableDefault.getId(), appProfileDefault); + ListTimeSeriesRequest.Builder requestBuilder = + ListTimeSeriesRequest.newBuilder() + .setName(name.toString()) + .setFilter(metricFilter) + .setInterval(interval) + .setView(ListTimeSeriesRequest.TimeSeriesView.FULL); + verifyMetrics(requestBuilder.build(), stopwatch, view, null); + + // Verify that metrics are published for ReadRows request + metricFilter = + String.format( + "metric.type=\"bigtable.googleapis.com/client/%s\" " + + "AND resource.labels.instance=\"%s\" AND metric.labels.method=\"Bigtable.ReadRows\"" + + " AND resource.labels.table=\"%s\" AND metric.labels.app_profile=\"%s\"", + view, testEnvRule.env().getInstanceId(), tableDefault.getId(), appProfileDefault); + requestBuilder.setFilter(metricFilter); + + verifyMetrics(requestBuilder.build(), stopwatch, view, null); + } + } + + @Test + public void testBuiltinMetricsWithCustomOTEL() throws Exception { + logger.info("Started testing builtin metrics with custom OTEL"); + tableCustomOtel = + tableAdminClient.createTable( + CreateTableRequest.of(PrefixGenerator.newPrefix("BuiltinMetricsIT#test2")) + .addFamily("cf")); + logger.info("Create custom table: " + tableCustomOtel.getId()); // Send a MutateRow and ReadRows request - testEnvRule - .env() - .getDataClient() - .mutateRow(RowMutation.create(table.getId(), "a-new-key").setCell("cf", "q", "abc")); + clientCustomOtel.mutateRow( + RowMutation.create(tableCustomOtel.getId(), "a-new-key").setCell("cf", "q", "abc")); ArrayList rows = Lists.newArrayList( - testEnvRule.env().getDataClient().readRows(Query.create(table.getId()).limit(10))); + clientCustomOtel.readRows(Query.create(tableCustomOtel.getId()).limit(10))); Stopwatch stopwatch = Stopwatch.createStarted(); ProjectName name = ProjectName.of(testEnvRule.env().getProjectId()); + Collection fromMetricReader = metricReader.collectAllMetrics(); + // Restrict time to last 10 minutes and 5 minutes after the request long startMillis = System.currentTimeMillis() - Duration.ofMinutes(10).toMillis(); long endMillis = startMillis + Duration.ofMinutes(15).toMillis(); @@ -122,14 +260,23 @@ public void testBuiltinMetrics() throws Exception { .build(); for (String view : VIEWS) { + String otelMetricName = "bigtable.googleapis.com/internal/client/" + view; + if (view.equals("application_blocking_latencies")) { + otelMetricName = "bigtable.googleapis.com/internal/client/application_latencies"; + } + MetricData dataFromReader = getMetricData(fromMetricReader, otelMetricName); + // Filter on instance and method name - // Verify that metrics are published for MutateRow request + // Verify that metrics are correct for MutateRows request String metricFilter = String.format( "metric.type=\"bigtable.googleapis.com/client/%s\" " + "AND resource.labels.instance=\"%s\" AND metric.labels.method=\"Bigtable.MutateRow\"" - + " AND resource.labels.table=\"%s\"", - view, testEnvRule.env().getInstanceId(), table.getId()); + + " AND resource.labels.table=\"%s\" AND metric.labels.app_profile=\"%s\"", + view, + testEnvRule.env().getInstanceId(), + tableCustomOtel.getId(), + appProfileCustomOtel); ListTimeSeriesRequest.Builder requestBuilder = ListTimeSeriesRequest.newBuilder() .setName(name.toString()) @@ -137,23 +284,27 @@ public void testBuiltinMetrics() throws Exception { .setInterval(interval) .setView(ListTimeSeriesRequest.TimeSeriesView.FULL); - verifyMetricsArePublished(requestBuilder.build(), stopwatch, view); + verifyMetrics(requestBuilder.build(), stopwatch, view, dataFromReader); - // Verify that metrics are published for ReadRows request + // Verify that metrics are correct for ReadRows request metricFilter = String.format( "metric.type=\"bigtable.googleapis.com/client/%s\" " + "AND resource.labels.instance=\"%s\" AND metric.labels.method=\"Bigtable.ReadRows\"" - + " AND resource.labels.table=\"%s\"", - view, testEnvRule.env().getInstanceId(), table.getId()); + + " AND resource.labels.table=\"%s\" AND metric.labels.app_profile=\"%s\"", + view, + testEnvRule.env().getInstanceId(), + tableCustomOtel.getId(), + appProfileCustomOtel); requestBuilder.setFilter(metricFilter); - verifyMetricsArePublished(requestBuilder.build(), stopwatch, view); + verifyMetrics(requestBuilder.build(), stopwatch, view, dataFromReader); } } - private void verifyMetricsArePublished( - ListTimeSeriesRequest request, Stopwatch stopwatch, String view) throws Exception { + private void verifyMetrics( + ListTimeSeriesRequest request, Stopwatch stopwatch, String view, MetricData dataFromReader) + throws Exception { ListTimeSeriesResponse response = metricClient.listTimeSeriesCallable().call(request); logger.log( Level.INFO, @@ -172,5 +323,56 @@ private void verifyMetricsArePublished( assertWithMessage("View " + view + " didn't return any data.") .that(response.getTimeSeriesCount()) .isGreaterThan(0); + + // Compare metric data with in memory metrics reader data if present + if (dataFromReader != null) { + for (TimeSeries ts : response.getTimeSeriesList()) { + Map attributesMap = + ImmutableMap.builder() + .putAll(ts.getResource().getLabelsMap()) + .putAll(ts.getMetric().getLabelsMap()) + .build(); + AttributesBuilder attributesBuilder = Attributes.builder(); + String streamingKey = BuiltinMetricsConstants.STREAMING.getKey(); + attributesMap.forEach( + (k, v) -> { + if (!k.equals(streamingKey)) { + attributesBuilder.put(k, v); + } + }); + if (attributesMap.containsKey(streamingKey)) { + attributesBuilder.put( + streamingKey, Boolean.parseBoolean(attributesMap.get(streamingKey))); + } + Attributes attributes = attributesBuilder.build(); + verifyAttributes(dataFromReader, attributes); + long expectedValue = getAggregatedValue(dataFromReader, attributes); + Timestamp startTime = getStartTimeSeconds(dataFromReader, attributes); + assertThat(startTime.getSeconds()).isGreaterThan(0); + List point = + ts.getPointsList().stream() + .filter( + p -> + Timestamps.between(p.getInterval().getStartTime(), startTime).getSeconds() + < 60) + .collect(Collectors.toList()); + if (point.size() > 0) { + long actualValue = (long) point.get(0).getValue().getDistributionValue().getMean(); + assertWithMessage( + "actual value does not match expected value, actual value " + + actualValue + + " expected value " + + expectedValue + + " actual start time " + + point.get(0).getInterval().getStartTime() + + " expected start time " + + startTime) + .that(actualValue) + .isIn( + Range.range( + expectedValue - 1, BoundType.CLOSED, expectedValue + 1, BoundType.CLOSED)); + } + } + } } } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java index 0f3bed1d90..f0bf83bf26 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java @@ -15,13 +15,13 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.APP_PROFILE; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLIENT_UID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.CLUSTER_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.INSTANCE_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.PROJECT_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.TABLE_ID; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsAttributes.ZONE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APP_PROFILE; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_UID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PROJECT_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java new file mode 100644 index 0000000000..3a8112a7f5 --- /dev/null +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java @@ -0,0 +1,103 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub.metrics; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.protobuf.Timestamp; +import com.google.protobuf.util.Timestamps; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.metrics.data.HistogramPointData; +import io.opentelemetry.sdk.metrics.data.LongPointData; +import io.opentelemetry.sdk.metrics.data.MetricData; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class BuiltinMetricsTestUtils { + + public static MetricData getMetricData(Collection allMetricData, String metricName) { + List metricDataList = + allMetricData.stream() + .filter(md -> md.getName().equals(metricName)) + .collect(Collectors.toList()); + if (metricDataList.size() == 0) { + allMetricData.stream().forEach(md -> System.out.println(md.getName())); + } + assertThat(metricDataList.size()).isEqualTo(1); + + return metricDataList.get(0); + } + + public static long getAggregatedValue(MetricData metricData, Attributes attributes) { + switch (metricData.getType()) { + case HISTOGRAM: + HistogramPointData hd = + metricData.getHistogramData().getPoints().stream() + .filter(pd -> pd.getAttributes().equals(attributes)) + .collect(Collectors.toList()) + .get(0); + return (long) hd.getSum() / hd.getCount(); + case LONG_SUM: + LongPointData ld = + metricData.getLongSumData().getPoints().stream() + .filter(pd -> pd.getAttributes().equals(attributes)) + .collect(Collectors.toList()) + .get(0); + return ld.getValue(); + } + return 0; + } + + public static Timestamp getStartTimeSeconds(MetricData metricData, Attributes attributes) { + switch (metricData.getType()) { + case HISTOGRAM: + HistogramPointData hd = + metricData.getHistogramData().getPoints().stream() + .filter(pd -> pd.getAttributes().equals(attributes)) + .collect(Collectors.toList()) + .get(0); + return Timestamps.fromNanos(hd.getStartEpochNanos()); + case LONG_SUM: + LongPointData ld = + metricData.getLongSumData().getPoints().stream() + .filter(pd -> pd.getAttributes().equals(attributes)) + .collect(Collectors.toList()) + .get(0); + return Timestamps.fromNanos(ld.getStartEpochNanos()); + } + return Timestamp.getDefaultInstance(); + } + + public static void verifyAttributes(MetricData metricData, Attributes attributes) { + switch (metricData.getType()) { + case HISTOGRAM: + List hd = + metricData.getHistogramData().getPoints().stream() + .filter(pd -> pd.getAttributes().equals(attributes)) + .collect(Collectors.toList()); + assertThat(hd.size()).isGreaterThan(0); + break; + case LONG_SUM: + List ld = + metricData.getLongSumData().getPoints().stream() + .filter(pd -> pd.getAttributes().equals(attributes)) + .collect(Collectors.toList()); + assertThat(ld.size()).isGreaterThan(0); + break; + } + } +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java index 8aaa26116d..938be62c91 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java @@ -30,6 +30,9 @@ import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STREAMING; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.getAggregatedValue; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.getMetricData; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.verifyAttributes; import static com.google.common.truth.Truth.assertThat; import com.google.api.client.util.Lists; @@ -85,8 +88,6 @@ import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.metrics.SdkMeterProvider; -import io.opentelemetry.sdk.metrics.data.HistogramPointData; -import io.opentelemetry.sdk.metrics.data.LongPointData; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; @@ -98,7 +99,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -652,55 +652,6 @@ public void testPermanentFailure() { verifyAttributes(opLatency, expected); } - private MetricData getMetricData(Collection allMetricData, String metricName) { - List metricDataList = - allMetricData.stream() - .filter(md -> md.getName().equals(metricName)) - .collect(Collectors.toList()); - assertThat(metricDataList.size()).isEqualTo(1); - - return metricDataList.get(0); - } - - private long getAggregatedValue(MetricData metricData, Attributes attributes) { - switch (metricData.getType()) { - case HISTOGRAM: - HistogramPointData hd = - metricData.getHistogramData().getPoints().stream() - .filter(pd -> pd.getAttributes().equals(attributes)) - .collect(Collectors.toList()) - .get(0); - return (long) hd.getSum() / hd.getCount(); - case LONG_SUM: - LongPointData ld = - metricData.getLongSumData().getPoints().stream() - .filter(pd -> pd.getAttributes().equals(attributes)) - .collect(Collectors.toList()) - .get(0); - return ld.getValue(); - } - return 0; - } - - private void verifyAttributes(MetricData metricData, Attributes attributes) { - switch (metricData.getType()) { - case HISTOGRAM: - List hd = - metricData.getHistogramData().getPoints().stream() - .filter(pd -> pd.getAttributes().equals(attributes)) - .collect(Collectors.toList()); - assertThat(hd.size()).isGreaterThan(0); - break; - case LONG_SUM: - List ld = - metricData.getLongSumData().getPoints().stream() - .filter(pd -> pd.getAttributes().equals(attributes)) - .collect(Collectors.toList()); - assertThat(ld.size()).isGreaterThan(0); - break; - } - } - private static class FakeService extends BigtableGrpc.BigtableImplBase { static List createFakeResponse() { From 9a435f76dcf87ad4e63a6e385b8bcf1b27a1254d Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Wed, 24 Jan 2024 11:04:48 -0500 Subject: [PATCH 20/34] revert changes in stats --- .../clirr-ignored-differences.xml | 6 ---- .../cloud/bigtable/stats/StatsWrapper.java | 12 +++++++ .../data/v2/BigtableDataSettings.java | 11 ++++--- .../data/v2/stub/EnhancedBigtableStub.java | 4 +-- .../stub/metrics/BuiltinMetricsConstants.java | 33 ++++++++++--------- 5 files changed, 37 insertions(+), 29 deletions(-) diff --git a/google-cloud-bigtable-stats/clirr-ignored-differences.xml b/google-cloud-bigtable-stats/clirr-ignored-differences.xml index fcdaa2b51c..a920751495 100644 --- a/google-cloud-bigtable-stats/clirr-ignored-differences.xml +++ b/google-cloud-bigtable-stats/clirr-ignored-differences.xml @@ -19,10 +19,4 @@ com/google/cloud/bigtable/stats/StatsRecorderWrapper void putBatchRequestThrottled(long) - - - 7002 - com/google/cloud/bigtable/stats/StatsWrapper - * - diff --git a/google-cloud-bigtable-stats/src/main/java/com/google/cloud/bigtable/stats/StatsWrapper.java b/google-cloud-bigtable-stats/src/main/java/com/google/cloud/bigtable/stats/StatsWrapper.java index 92057b713e..2b2cbe179d 100644 --- a/google-cloud-bigtable-stats/src/main/java/com/google/cloud/bigtable/stats/StatsWrapper.java +++ b/google-cloud-bigtable-stats/src/main/java/com/google/cloud/bigtable/stats/StatsWrapper.java @@ -39,6 +39,18 @@ public static StatsRecorderWrapper createRecorder( operationType, spanName, statsAttributes, Stats.getStatsRecorder()); } + // This is used in integration tests to get the tag value strings from view manager because Stats + // is relocated to com.google.bigtable.veneer.repackaged.io.opencensus. + @InternalApi("Visible for testing") + public static List getOperationLatencyViewTagValueStrings() { + return Stats.getViewManager().getView(BuiltinViewConstants.OPERATION_LATENCIES_VIEW.getName()) + .getAggregationMap().entrySet().stream() + .map(Map.Entry::getKey) + .flatMap(x -> x.stream()) + .map(x -> x.asString()) + .collect(Collectors.toCollection(ArrayList::new)); + } + // A workaround to run ITBuiltinViewConstantsTest as integration test. Integration test runs after // the packaging step. Opencensus classes will be relocated when they are packaged but the // integration test files will not be. So the integration tests can't reference any transitive diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java index 21b4743803..f9d017a09c 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java @@ -554,7 +554,9 @@ public boolean isBuiltinMetricsEnabled() { /** * Set a custom OpenTelemetry instance. * - *

To register builtin metrics on the custom OpenTelemetry: + *

To register builtin metrics on the custom OpenTelemetry: + * + *

{@code
      * SdkMeterProviderBuilder sdkMeterProvider = SdkMeterProvider.builder();
      *
      * // register Builtin metrics on your meter provider
@@ -565,11 +567,12 @@ public boolean isBuiltinMetricsEnabled() {
      * sdkMeterProvider.registerView(..);
      *
      * // create the OTEL instance
-     * OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().
-     *     setMeterProvider(sdkMeterProvider().build();
+     * OpenTelemetry openTelemetry = OpenTelemetrySdk
+     *     .builder()
+     *     .setMeterProvider(sdkMeterProvider().build());
      *
      * settings.setOpenTelemetry(openTelemetry);
-     * 
+     * }
*/ public Builder setOpenTelemetry(OpenTelemetry openTelemetry) { stubSettings.setOpenTelemetry(openTelemetry); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java index 457e791e60..ede419f73b 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java @@ -133,7 +133,6 @@ import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; -import io.opentelemetry.sdk.resources.Resource; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; @@ -311,8 +310,7 @@ private static BuiltinMetricsTracerFactory setupBuiltinMetricsTracerFactory( if (settings.getOpenTelemetry() != null) { return BuiltinMetricsTracerFactory.create(settings.getOpenTelemetry(), attributes); } else if (settings.isBuiltinMetricsEnabled()) { - Resource resource = Resource.create(attributes); - SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder().setResource(resource); + SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder(); BuiltinMetricsView.registerBuiltinMetrics( settings.getProjectId(), settings.getCredentialsProvider().getCredentials(), diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java index a7cd656874..f1b82b1ffa 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java @@ -57,18 +57,19 @@ public class BuiltinMetricsConstants { 0.0, 0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0, 16.0, 20.0, 25.0, 30.0, 40.0, 50.0, 65.0, 80.0, 100.0, 130.0, 160.0, 200.0, 250.0, 300.0, 400.0, 500.0, 650.0, 800.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0, 50000.0, - 100000.0, 200000.0, 400000.0, 800000.0, 1600000.0)); + 100000.0, 200000.0, 300000.0, 600000.0, 900000.0, 1200000.0, + 1800000.0)); // max is 30 minutes static final String SCOPE = "bigtable.googleapis.com"; - public static final InstrumentSelector OPERATION_LATENCIES_SELECTOR = + static final InstrumentSelector OPERATION_LATENCIES_SELECTOR = InstrumentSelector.builder() .setName(OPERATION_LATENCIES_NAME) .setMeterName(SCOPE) .setType(InstrumentType.HISTOGRAM) .setUnit("ms") .build(); - public static final InstrumentSelector ATTEMPT_LATENCIES_SELECTOR = + static final InstrumentSelector ATTEMPT_LATENCIES_SELECTOR = InstrumentSelector.builder() .setName(ATTEMPT_LATENCIES_NAME) .setMeterName(SCOPE) @@ -76,7 +77,7 @@ public class BuiltinMetricsConstants { .setUnit("ms") .build(); - public static final InstrumentSelector RETRY_COUNT_SELECTOR = + static final InstrumentSelector RETRY_COUNT_SELECTOR = InstrumentSelector.builder() .setName(RETRY_COUNT_NAME) .setMeterName(SCOPE) @@ -84,35 +85,35 @@ public class BuiltinMetricsConstants { .setUnit("1") .build(); - public static final InstrumentSelector SERVER_LATENCIES_SELECTOR = + static final InstrumentSelector SERVER_LATENCIES_SELECTOR = InstrumentSelector.builder() .setName(SERVER_LATENCIES_NAME) .setMeterName(SCOPE) .setType(InstrumentType.HISTOGRAM) .setUnit("ms") .build(); - public static final InstrumentSelector FIRST_RESPONSE_LATENCIES_SELECTOR = + static final InstrumentSelector FIRST_RESPONSE_LATENCIES_SELECTOR = InstrumentSelector.builder() .setName(FIRST_RESPONSE_LATENCIES_NAME) .setMeterName(SCOPE) .setType(InstrumentType.HISTOGRAM) .setUnit("ms") .build(); - public static final InstrumentSelector CLIENT_BLOCKING_LATENCIES_SELECTOR = + static final InstrumentSelector CLIENT_BLOCKING_LATENCIES_SELECTOR = InstrumentSelector.builder() .setName(CLIENT_BLOCKING_LATENCIES_NAME) .setMeterName(SCOPE) .setType(InstrumentType.HISTOGRAM) .setUnit("ms") .build(); - public static final InstrumentSelector APPLICATION_BLOCKING_LATENCIES_SELECTOR = + static final InstrumentSelector APPLICATION_BLOCKING_LATENCIES_SELECTOR = InstrumentSelector.builder() .setName(APPLICATION_BLOCKING_LATENCIES_NAME) .setMeterName(SCOPE) .setType(InstrumentType.HISTOGRAM) .setUnit("ms") .build(); - public static final InstrumentSelector CONNECTIVITY_ERROR_COUNT_SELECTOR = + static final InstrumentSelector CONNECTIVITY_ERROR_COUNT_SELECTOR = InstrumentSelector.builder() .setName(CONNECTIVITY_ERROR_COUNT_NAME) .setMeterName(SCOPE) @@ -125,37 +126,37 @@ public class BuiltinMetricsConstants { .setName("bigtable.googleapis.com/internal/client/operation_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) .build(); - public static final View ATTEMPT_LATENCIES_VIEW = + static final View ATTEMPT_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/attempt_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) .build(); - public static final View SERVER_LATENCIES_VIEW = + static final View SERVER_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/server_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) .build(); - public static final View FIRST_RESPONSE_LATENCIES_VIEW = + static final View FIRST_RESPONSE_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/first_response_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) .build(); - public static final View APPLICATION_BLOCKING_LATENCIES_VIEW = + static final View APPLICATION_BLOCKING_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/application_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) .build(); - public static final View CLIENT_BLOCKING_LATENCIES_VIEW = + static final View CLIENT_BLOCKING_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/throttling_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) .build(); - public static final View RETRY_COUNT_VIEW = + static final View RETRY_COUNT_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/retry_count") .setAggregation(Aggregation.sum()) .build(); - public static final View CONNECTIVITY_ERROR_COUNT_VIEW = + static final View CONNECTIVITY_ERROR_COUNT_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/connectivity_error_count") .setAggregation(Aggregation.sum()) From a797c96729a4ebcbbe6eafb4984a6b72506df4f9 Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Wed, 24 Jan 2024 11:28:46 -0500 Subject: [PATCH 21/34] fix import --- .../main/java/com/google/cloud/bigtable/stats/StatsWrapper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/google-cloud-bigtable-stats/src/main/java/com/google/cloud/bigtable/stats/StatsWrapper.java b/google-cloud-bigtable-stats/src/main/java/com/google/cloud/bigtable/stats/StatsWrapper.java index 2b2cbe179d..401a1cf975 100644 --- a/google-cloud-bigtable-stats/src/main/java/com/google/cloud/bigtable/stats/StatsWrapper.java +++ b/google-cloud-bigtable-stats/src/main/java/com/google/cloud/bigtable/stats/StatsWrapper.java @@ -22,6 +22,7 @@ import io.opencensus.stats.Stats; import io.opencensus.stats.View; import io.opencensus.tags.TagKey; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; From d534f7ff1ead88221459df4c131c8a8943442bcc Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Wed, 24 Jan 2024 15:59:25 -0500 Subject: [PATCH 22/34] update --- .../stub/metrics/BuiltinMetricsConstants.java | 93 ++++++++++++++++++- .../bigtable/data/v2/it/BuiltinMetricsIT.java | 13 ++- 2 files changed, 102 insertions(+), 4 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java index f1b82b1ffa..5994d69b4c 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java @@ -18,6 +18,7 @@ import com.google.api.core.InternalApi; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.sdk.metrics.Aggregation; import io.opentelemetry.sdk.metrics.InstrumentSelector; @@ -57,8 +58,7 @@ public class BuiltinMetricsConstants { 0.0, 0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0, 16.0, 20.0, 25.0, 30.0, 40.0, 50.0, 65.0, 80.0, 100.0, 130.0, 160.0, 200.0, 250.0, 300.0, 400.0, 500.0, 650.0, 800.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0, 50000.0, - 100000.0, 200000.0, 300000.0, 600000.0, 900000.0, 1200000.0, - 1800000.0)); // max is 30 minutes + 100000.0, 200000.0, 400000.0, 800000.0, 1600000.0, 3200000.0)); // max is 53.3 minutes static final String SCOPE = "bigtable.googleapis.com"; @@ -125,41 +125,130 @@ public class BuiltinMetricsConstants { View.builder() .setName("bigtable.googleapis.com/internal/client/operation_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) + .setAttributeFilter( + ImmutableSet.of( + PROJECT_ID.getKey(), + INSTANCE_ID.getKey(), + TABLE_ID.getKey(), + CLUSTER_ID.getKey(), + ZONE_ID.getKey(), + APP_PROFILE.getKey(), + STREAMING.getKey(), + METHOD.getKey(), + STATUS.getKey(), + CLIENT_NAME.getKey())) .build(); static final View ATTEMPT_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/attempt_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) + .setAttributeFilter( + ImmutableSet.of( + PROJECT_ID.getKey(), + INSTANCE_ID.getKey(), + TABLE_ID.getKey(), + CLUSTER_ID.getKey(), + ZONE_ID.getKey(), + APP_PROFILE.getKey(), + STREAMING.getKey(), + METHOD.getKey(), + STATUS.getKey(), + CLIENT_NAME.getKey())) .build(); static final View SERVER_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/server_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) + .setAttributeFilter( + ImmutableSet.of( + PROJECT_ID.getKey(), + INSTANCE_ID.getKey(), + TABLE_ID.getKey(), + CLUSTER_ID.getKey(), + ZONE_ID.getKey(), + APP_PROFILE.getKey(), + STREAMING.getKey(), + METHOD.getKey(), + STATUS.getKey(), + CLIENT_NAME.getKey())) .build(); static final View FIRST_RESPONSE_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/first_response_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) + .setAttributeFilter( + ImmutableSet.of( + PROJECT_ID.getKey(), + INSTANCE_ID.getKey(), + TABLE_ID.getKey(), + CLUSTER_ID.getKey(), + ZONE_ID.getKey(), + APP_PROFILE.getKey(), + METHOD.getKey(), + STATUS.getKey(), + CLIENT_NAME.getKey())) .build(); static final View APPLICATION_BLOCKING_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/application_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) + .setAttributeFilter( + ImmutableSet.of( + PROJECT_ID.getKey(), + INSTANCE_ID.getKey(), + TABLE_ID.getKey(), + CLUSTER_ID.getKey(), + ZONE_ID.getKey(), + APP_PROFILE.getKey(), + METHOD.getKey(), + CLIENT_NAME.getKey())) .build(); static final View CLIENT_BLOCKING_LATENCIES_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/throttling_latencies") .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) + .setAttributeFilter( + ImmutableSet.of( + PROJECT_ID.getKey(), + INSTANCE_ID.getKey(), + TABLE_ID.getKey(), + CLUSTER_ID.getKey(), + ZONE_ID.getKey(), + APP_PROFILE.getKey(), + METHOD.getKey(), + CLIENT_NAME.getKey())) .build(); static final View RETRY_COUNT_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/retry_count") .setAggregation(Aggregation.sum()) + .setAttributeFilter( + ImmutableSet.of( + PROJECT_ID.getKey(), + INSTANCE_ID.getKey(), + TABLE_ID.getKey(), + CLUSTER_ID.getKey(), + ZONE_ID.getKey(), + APP_PROFILE.getKey(), + METHOD.getKey(), + STATUS.getKey(), + CLIENT_NAME.getKey())) .build(); static final View CONNECTIVITY_ERROR_COUNT_VIEW = View.builder() .setName("bigtable.googleapis.com/internal/client/connectivity_error_count") .setAggregation(Aggregation.sum()) + .setAttributeFilter( + ImmutableSet.of( + PROJECT_ID.getKey(), + INSTANCE_ID.getKey(), + TABLE_ID.getKey(), + CLUSTER_ID.getKey(), + ZONE_ID.getKey(), + APP_PROFILE.getKey(), + METHOD.getKey(), + STATUS.getKey(), + CLIENT_NAME.getKey())) .build(); public static Map getAllViews() { diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java index 3c8f2428de..26566d5170 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java @@ -162,6 +162,8 @@ public void tearDown() { if (instanceAdminClient != null) { instanceAdminClient.deleteAppProfile( testEnvRule.env().getInstanceId(), appProfileCustomOtel, true); + instanceAdminClient.deleteAppProfile( + testEnvRule.env().getInstanceId(), appProfileDefault, true); } if (clientCustomOtel != null) { clientCustomOtel.close(); @@ -353,8 +355,15 @@ private void verifyMetrics( ts.getPointsList().stream() .filter( p -> - Timestamps.between(p.getInterval().getStartTime(), startTime).getSeconds() - < 60) + Timestamps.compare(p.getInterval().getStartTime(), startTime) >= 0 + && Timestamps.compare( + p.getInterval().getStartTime(), + Timestamps.add( + startTime, + com.google.protobuf.Duration.newBuilder() + .setSeconds(60) + .build())) + < 0) .collect(Collectors.toList()); if (point.size() > 0) { long actualValue = (long) point.get(0).getValue().getDistributionValue().getMean(); From 2dd6586541b4c51da545db31d3c2291b3f59fa43 Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Thu, 25 Jan 2024 15:37:05 -0500 Subject: [PATCH 23/34] merge back the endpoint change --- .../v2/stub/metrics/BigtableCloudMonitoringExporter.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java index f2db06860b..3d6d4d08f4 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java @@ -25,6 +25,7 @@ import com.google.cloud.monitoring.v3.MetricServiceClient; import com.google.cloud.monitoring.v3.MetricServiceSettings; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; import com.google.common.util.concurrent.MoreExecutors; import com.google.monitoring.v3.CreateTimeSeriesRequest; import com.google.monitoring.v3.ProjectName; @@ -55,6 +56,12 @@ public final class BigtableCloudMonitoringExporter implements MetricExporter { private static final Logger logger = Logger.getLogger(BigtableCloudMonitoringExporter.class.getName()); + + private static final String MONITORING_ENDPOINT = + MoreObjects.firstNonNull( + System.getProperty("bigtable.test-monitoring-endpoint"), + MetricServiceSettings.getDefaultEndpoint()); + private final MetricServiceClient client; private final String projectId; @@ -70,6 +77,7 @@ public static BigtableCloudMonitoringExporter create(String projectId, Credentia throws IOException { MetricServiceSettings.Builder settingsBuilder = MetricServiceSettings.newBuilder(); settingsBuilder.setCredentialsProvider(FixedCredentialsProvider.create(credentials)); + settingsBuilder.setEndpoint(MONITORING_ENDPOINT); org.threeten.bp.Duration timeout = Duration.ofMinutes(1); // TODO: createServiceTimeSeries needs special handling if the request failed. Leaving From 54a8d25432e0a665e6746cc1caf4ebb3c09a74ea Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Tue, 30 Jan 2024 14:27:06 -0500 Subject: [PATCH 24/34] refactor constants and settings --- .../data/v2/BigtableDataSettings.java | 79 ++--- .../data/v2/stub/EnhancedBigtableStub.java | 11 +- .../v2/stub/EnhancedBigtableStubSettings.java | 65 +--- .../stub/metrics/BuiltinMetricsConstants.java | 293 ++++++------------ .../v2/stub/metrics/BuiltinMetricsView.java | 5 +- .../CustomOpenTelemetryMetricsProvider.java | 63 ++++ .../stub/metrics/DefaultMetricsProvider.java | 21 ++ .../data/v2/stub/metrics/MetricsProvider.java | 18 ++ .../v2/stub/metrics/NoopMetricsProvider.java | 18 ++ .../v2/BigtableDataClientFactoryTest.java | 4 +- .../bigtable/data/v2/it/BuiltinMetricsIT.java | 6 +- .../v2/it/StreamingMetricsMetadataIT.java | 10 +- .../data/v2/it/UnaryMetricsMetadataIT.java | 9 +- 13 files changed, 275 insertions(+), 327 deletions(-) create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsProvider.java create mode 100644 google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java index f9d017a09c..84bb1c7d8c 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java @@ -29,10 +29,10 @@ import com.google.cloud.bigtable.data.v2.models.Row; import com.google.cloud.bigtable.data.v2.stub.BigtableBatchingCallSettings; import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings; +import com.google.cloud.bigtable.data.v2.stub.metrics.MetricsProvider; import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import io.grpc.ManagedChannelBuilder; -import io.opentelemetry.api.OpenTelemetry; import java.io.IOException; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -199,8 +199,9 @@ public static void enableGfeOpenCensusStats() { * Register built in metrics. * * @deprecated This is a no-op that doesn't do anything. Builtin metrics are enabled by default - * now. Please use {@link BigtableDataSettings.Builder#setBuiltinMetricsEnabled(boolean)} to - * enable or disable built-in metrics. + * now. Please refer to {@link + * BigtableDataSettings.Builder#setMetricsProvider(MetricsProvider)} on how to enable or + * disable built-in metrics. */ @Deprecated public static void enableBuiltinMetrics() throws IOException {} @@ -210,8 +211,8 @@ public static void enableBuiltinMetrics() throws IOException {} * for all the projects you're publishing to. * * @deprecated This is a no-op that doesn't do anything. Builtin metrics are enabled by default - * now. Please use {@link BigtableDataSettings.Builder#setBuiltinMetricsEnabled(boolean)} to - * enable or disable built-in metrics. + * now. Please refer {@link BigtableDataSettings.Builder#setMetricsProvider(MetricsProvider)} + * on how to enable or disable built-in metrics. */ public static void enableBuiltinMetrics(Credentials credentials) throws IOException {} @@ -277,18 +278,9 @@ public boolean isBulkMutationFlowControlEnabled() { return stubSettings.bulkMutateRowsSettings().isServerInitiatedFlowControlEnabled(); } - /** Gets if built-in metrics is enabled. */ - public boolean isBuiltinMetricsEnabled() { - return stubSettings.isBuiltinMetricsEnabled(); - } - - /** - * Gets the custom OpenTelemetry instance. If no custom OTEL instance is set, the client uses a - * default instance with builtin metrics enabled. Use {@link - * Builder#setBuiltinMetricsEnabled(boolean)} to disable the builtin metrics. - */ - public OpenTelemetry getOpenTelemetry() { - return stubSettings.getOpenTelemetry(); + /** Gets the {@link MetricsProvider}. * */ + public MetricsProvider getMetricsProvider() { + return stubSettings.getMetricsProvider(); } /** Returns the underlying RPC settings. */ @@ -540,52 +532,23 @@ public boolean isBulkMutationFlowControlEnabled() { return stubSettings.bulkMutateRowsSettings().isServerInitiatedFlowControlEnabled(); } - /** Set enable to true to enable builtin metrics. */ - public Builder setBuiltinMetricsEnabled(boolean enable) { - stubSettings.setBuiltinMetricsEnabled(enable); - return this; - } - - /** Gets if built-in metrics is enabled. */ - public boolean isBuiltinMetricsEnabled() { - return stubSettings.isBuiltinMetricsEnabled(); - } - /** - * Set a custom OpenTelemetry instance. - * - *

To register builtin metrics on the custom OpenTelemetry: - * - *

{@code
-     * SdkMeterProviderBuilder sdkMeterProvider = SdkMeterProvider.builder();
-     *
-     * // register Builtin metrics on your meter provider
-     * BuiltinMetricsViews.registerBuiltinMetrics("project-id", sdkMeterProvider);
-     *
-     * // register other metrics reader and views
-     * sdkMeterProvider.registerMetricReader(..);
-     * sdkMeterProvider.registerView(..);
-     *
-     * // create the OTEL instance
-     * OpenTelemetry openTelemetry = OpenTelemetrySdk
-     *     .builder()
-     *     .setMeterProvider(sdkMeterProvider().build());
-     *
-     * settings.setOpenTelemetry(openTelemetry);
-     * }
+ * Sets the {@link MetricsProvider}. By default, this is set to {@link + * com.google.cloud.bigtable.data.v2.stub.metrics.DefaultMetricsProvider} which will collect and + * export built-in metrics. To disable built-in metrics, set it to {@link + * com.google.cloud.bigtable.data.v2.stub.metrics.NoopMetricsProvider}. To use a custom + * OpenTelemetry, refer to {@link + * com.google.cloud.bigtable.data.v2.stub.metrics.CustomOpenTelemetryMetricsProvider} on how to + * set it up. */ - public Builder setOpenTelemetry(OpenTelemetry openTelemetry) { - stubSettings.setOpenTelemetry(openTelemetry); + public Builder setMetricsProvider(MetricsProvider metricsProvider) { + stubSettings.setMetricsProvider(metricsProvider); return this; } - /** - * Gets the custom OpenTelemetry instance. If no custom OTEL instance is set, the client uses a - * default instance with builtin metrics enabled. Use {@link #setBuiltinMetricsEnabled(boolean)} - * to disable the builtin metrics. - */ - public OpenTelemetry getOpenTelemetry() { - return stubSettings.getOpenTelemetry(); + /** Gets the {@link MetricsProvider}. */ + public MetricsProvider getMetricsProvider() { + return stubSettings.getMetricsProvider(); } /** diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java index ede419f73b..133cb3ea7f 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java @@ -99,6 +99,8 @@ import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTracerFactory; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsView; import com.google.cloud.bigtable.data.v2.stub.metrics.CompositeTracerFactory; +import com.google.cloud.bigtable.data.v2.stub.metrics.CustomOpenTelemetryMetricsProvider; +import com.google.cloud.bigtable.data.v2.stub.metrics.DefaultMetricsProvider; import com.google.cloud.bigtable.data.v2.stub.metrics.MetricsTracerFactory; import com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants; import com.google.cloud.bigtable.data.v2.stub.metrics.StatsHeadersServerStreamingCallable; @@ -307,9 +309,12 @@ public static ApiTracerFactory createBigtableTracerFactory( private static BuiltinMetricsTracerFactory setupBuiltinMetricsTracerFactory( EnhancedBigtableStubSettings.Builder settings, Attributes attributes) throws IOException { - if (settings.getOpenTelemetry() != null) { - return BuiltinMetricsTracerFactory.create(settings.getOpenTelemetry(), attributes); - } else if (settings.isBuiltinMetricsEnabled()) { + if (settings.getMetricsProvider() instanceof CustomOpenTelemetryMetricsProvider) { + CustomOpenTelemetryMetricsProvider customMetricsProvider = + (CustomOpenTelemetryMetricsProvider) settings.getMetricsProvider(); + return BuiltinMetricsTracerFactory.create( + customMetricsProvider.getOpenTelemetry(), attributes); + } else if (settings.getMetricsProvider() instanceof DefaultMetricsProvider) { SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder(); BuiltinMetricsView.registerBuiltinMetrics( settings.getProjectId(), diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java index 72d30497ba..4111bdba23 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java @@ -44,6 +44,8 @@ import com.google.cloud.bigtable.data.v2.models.ReadModifyWriteRow; import com.google.cloud.bigtable.data.v2.models.Row; import com.google.cloud.bigtable.data.v2.models.RowMutation; +import com.google.cloud.bigtable.data.v2.stub.metrics.DefaultMetricsProvider; +import com.google.cloud.bigtable.data.v2.stub.metrics.MetricsProvider; import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsBatchingDescriptor; import com.google.cloud.bigtable.data.v2.stub.readrows.ReadRowsBatchingDescriptor; import com.google.common.base.MoreObjects; @@ -51,7 +53,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import io.opentelemetry.api.OpenTelemetry; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -61,7 +62,6 @@ import java.util.Set; import java.util.logging.Logger; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import org.threeten.bp.Duration; /** @@ -231,9 +231,7 @@ public class EnhancedBigtableStubSettings extends StubSettings getJwtAudienceMapping() { return jwtAudienceMapping; } - public boolean isBuiltinMetricsEnabled() { - return isBuiltinMetricsEnabled; - } - - public OpenTelemetry getOpenTelemetry() { - return openTelemetry; + public MetricsProvider getMetricsProvider() { + return metricsProvider; } /** @@ -653,9 +645,7 @@ public static class Builder extends StubSettings.Builder jwtAudienceMapping) { return this; } - /** Returns true if builtin metrics is enabled. */ - public boolean isBuiltinMetricsEnabled() { - return isBuiltinMetricsEnabled; - } - - /** Set enable to true to enable builtin metrics. */ - public Builder setBuiltinMetricsEnabled(boolean enable) { - this.isBuiltinMetricsEnabled = enable; - return this; - } - - /** - * Set a custom OpenTelemetry instance. See {@link - * com.google.cloud.bigtable.data.v2.BigtableDataSettings.Builder#setOpenTelemetry(OpenTelemetry)} - * on how to register built-in metrics on your custom OTEL instance. - */ - public Builder setOpenTelemetry(OpenTelemetry openTelemetry) { - this.openTelemetry = openTelemetry; + public Builder setMetricsProvider(MetricsProvider metricsProvider) { + this.metricsProvider = metricsProvider; return this; } - /** - * Gets the custom OpenTelemetry instance. If no custom OTEL instance is set, the client uses a - * default instance with builtin metrics enabled. Use {@link #setBuiltinMetricsEnabled(boolean)} - * to disable the builtin metrics. - */ - public @Nullable OpenTelemetry getOpenTelemetry() { - return this.openTelemetry; + public MetricsProvider getMetricsProvider() { + return this.metricsProvider; } @InternalApi("Used for internal testing") @@ -1139,8 +1105,7 @@ public String toString() { generateInitialChangeStreamPartitionsSettings) .add("readChangeStreamSettings", readChangeStreamSettings) .add("pingAndWarmSettings", pingAndWarmSettings) - .add("isBuiltinMetricsEnabled", isBuiltinMetricsEnabled) - .add("openTelemetry", openTelemetry) + .add("metricsProvider", metricsProvider) .add("parent", super.toString()) .toString(); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java index 5994d69b4c..c7abc91007 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java @@ -25,6 +25,7 @@ import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.View; import java.util.Map; +import java.util.Set; /** Defining Bigtable builit-in metrics scope, attributes, metric names and views. */ @InternalApi @@ -43,7 +44,7 @@ public class BuiltinMetricsConstants { static final AttributeKey STATUS = AttributeKey.stringKey("status"); static final AttributeKey CLIENT_NAME = AttributeKey.stringKey("client_name"); - static final String OPERATION_LATENCIES_NAME = "operation_latencies"; + public static final String OPERATION_LATENCIES_NAME = "operation_latencies"; static final String ATTEMPT_LATENCIES_NAME = "attempt_latencies"; static final String RETRY_COUNT_NAME = "retry_count"; static final String CONNECTIVITY_ERROR_COUNT_NAME = "connectivity_error_count"; @@ -62,205 +63,103 @@ public class BuiltinMetricsConstants { static final String SCOPE = "bigtable.googleapis.com"; - static final InstrumentSelector OPERATION_LATENCIES_SELECTOR = - InstrumentSelector.builder() - .setName(OPERATION_LATENCIES_NAME) - .setMeterName(SCOPE) - .setType(InstrumentType.HISTOGRAM) - .setUnit("ms") - .build(); - static final InstrumentSelector ATTEMPT_LATENCIES_SELECTOR = - InstrumentSelector.builder() - .setName(ATTEMPT_LATENCIES_NAME) - .setMeterName(SCOPE) - .setType(InstrumentType.HISTOGRAM) - .setUnit("ms") - .build(); + static final Set COMMON_ATTRIBUTES = + ImmutableSet.of( + PROJECT_ID.getKey(), + INSTANCE_ID.getKey(), + TABLE_ID.getKey(), + APP_PROFILE.getKey(), + CLUSTER_ID.getKey(), + ZONE_ID.getKey(), + METHOD.getKey(), + CLIENT_NAME.getKey()); - static final InstrumentSelector RETRY_COUNT_SELECTOR = - InstrumentSelector.builder() - .setName(RETRY_COUNT_NAME) - .setMeterName(SCOPE) - .setType(InstrumentType.COUNTER) - .setUnit("1") - .build(); + static void defineView( + ImmutableMap.Builder viewMap, + String id, + Aggregation aggregation, + InstrumentType type, + String unit, + Set extraAttributes) { + InstrumentSelector selector = + InstrumentSelector.builder() + .setName(id) + .setMeterName(SCOPE) + .setType(type) + .setUnit(unit) + .build(); + Set attributesFilter = + ImmutableSet.builder().addAll(COMMON_ATTRIBUTES).addAll(extraAttributes).build(); + View view = + View.builder() + .setName(SCOPE + "/internal/client/" + id) + .setAggregation(aggregation) + .setAttributeFilter(attributesFilter) + .build(); - static final InstrumentSelector SERVER_LATENCIES_SELECTOR = - InstrumentSelector.builder() - .setName(SERVER_LATENCIES_NAME) - .setMeterName(SCOPE) - .setType(InstrumentType.HISTOGRAM) - .setUnit("ms") - .build(); - static final InstrumentSelector FIRST_RESPONSE_LATENCIES_SELECTOR = - InstrumentSelector.builder() - .setName(FIRST_RESPONSE_LATENCIES_NAME) - .setMeterName(SCOPE) - .setType(InstrumentType.HISTOGRAM) - .setUnit("ms") - .build(); - static final InstrumentSelector CLIENT_BLOCKING_LATENCIES_SELECTOR = - InstrumentSelector.builder() - .setName(CLIENT_BLOCKING_LATENCIES_NAME) - .setMeterName(SCOPE) - .setType(InstrumentType.HISTOGRAM) - .setUnit("ms") - .build(); - static final InstrumentSelector APPLICATION_BLOCKING_LATENCIES_SELECTOR = - InstrumentSelector.builder() - .setName(APPLICATION_BLOCKING_LATENCIES_NAME) - .setMeterName(SCOPE) - .setType(InstrumentType.HISTOGRAM) - .setUnit("ms") - .build(); - static final InstrumentSelector CONNECTIVITY_ERROR_COUNT_SELECTOR = - InstrumentSelector.builder() - .setName(CONNECTIVITY_ERROR_COUNT_NAME) - .setMeterName(SCOPE) - .setType(InstrumentType.COUNTER) - .setUnit("1") - .build(); - - public static final View OPERATION_LATENCIES_VIEW = - View.builder() - .setName("bigtable.googleapis.com/internal/client/operation_latencies") - .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) - .setAttributeFilter( - ImmutableSet.of( - PROJECT_ID.getKey(), - INSTANCE_ID.getKey(), - TABLE_ID.getKey(), - CLUSTER_ID.getKey(), - ZONE_ID.getKey(), - APP_PROFILE.getKey(), - STREAMING.getKey(), - METHOD.getKey(), - STATUS.getKey(), - CLIENT_NAME.getKey())) - .build(); - static final View ATTEMPT_LATENCIES_VIEW = - View.builder() - .setName("bigtable.googleapis.com/internal/client/attempt_latencies") - .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) - .setAttributeFilter( - ImmutableSet.of( - PROJECT_ID.getKey(), - INSTANCE_ID.getKey(), - TABLE_ID.getKey(), - CLUSTER_ID.getKey(), - ZONE_ID.getKey(), - APP_PROFILE.getKey(), - STREAMING.getKey(), - METHOD.getKey(), - STATUS.getKey(), - CLIENT_NAME.getKey())) - .build(); - static final View SERVER_LATENCIES_VIEW = - View.builder() - .setName("bigtable.googleapis.com/internal/client/server_latencies") - .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) - .setAttributeFilter( - ImmutableSet.of( - PROJECT_ID.getKey(), - INSTANCE_ID.getKey(), - TABLE_ID.getKey(), - CLUSTER_ID.getKey(), - ZONE_ID.getKey(), - APP_PROFILE.getKey(), - STREAMING.getKey(), - METHOD.getKey(), - STATUS.getKey(), - CLIENT_NAME.getKey())) - .build(); - static final View FIRST_RESPONSE_LATENCIES_VIEW = - View.builder() - .setName("bigtable.googleapis.com/internal/client/first_response_latencies") - .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) - .setAttributeFilter( - ImmutableSet.of( - PROJECT_ID.getKey(), - INSTANCE_ID.getKey(), - TABLE_ID.getKey(), - CLUSTER_ID.getKey(), - ZONE_ID.getKey(), - APP_PROFILE.getKey(), - METHOD.getKey(), - STATUS.getKey(), - CLIENT_NAME.getKey())) - .build(); - static final View APPLICATION_BLOCKING_LATENCIES_VIEW = - View.builder() - .setName("bigtable.googleapis.com/internal/client/application_latencies") - .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) - .setAttributeFilter( - ImmutableSet.of( - PROJECT_ID.getKey(), - INSTANCE_ID.getKey(), - TABLE_ID.getKey(), - CLUSTER_ID.getKey(), - ZONE_ID.getKey(), - APP_PROFILE.getKey(), - METHOD.getKey(), - CLIENT_NAME.getKey())) - .build(); - static final View CLIENT_BLOCKING_LATENCIES_VIEW = - View.builder() - .setName("bigtable.googleapis.com/internal/client/throttling_latencies") - .setAggregation(AGGREGATION_WITH_MILLIS_HISTOGRAM) - .setAttributeFilter( - ImmutableSet.of( - PROJECT_ID.getKey(), - INSTANCE_ID.getKey(), - TABLE_ID.getKey(), - CLUSTER_ID.getKey(), - ZONE_ID.getKey(), - APP_PROFILE.getKey(), - METHOD.getKey(), - CLIENT_NAME.getKey())) - .build(); - static final View RETRY_COUNT_VIEW = - View.builder() - .setName("bigtable.googleapis.com/internal/client/retry_count") - .setAggregation(Aggregation.sum()) - .setAttributeFilter( - ImmutableSet.of( - PROJECT_ID.getKey(), - INSTANCE_ID.getKey(), - TABLE_ID.getKey(), - CLUSTER_ID.getKey(), - ZONE_ID.getKey(), - APP_PROFILE.getKey(), - METHOD.getKey(), - STATUS.getKey(), - CLIENT_NAME.getKey())) - .build(); - static final View CONNECTIVITY_ERROR_COUNT_VIEW = - View.builder() - .setName("bigtable.googleapis.com/internal/client/connectivity_error_count") - .setAggregation(Aggregation.sum()) - .setAttributeFilter( - ImmutableSet.of( - PROJECT_ID.getKey(), - INSTANCE_ID.getKey(), - TABLE_ID.getKey(), - CLUSTER_ID.getKey(), - ZONE_ID.getKey(), - APP_PROFILE.getKey(), - METHOD.getKey(), - STATUS.getKey(), - CLIENT_NAME.getKey())) - .build(); + viewMap.put(selector, view); + } public static Map getAllViews() { - return new ImmutableMap.Builder() - .put(OPERATION_LATENCIES_SELECTOR, OPERATION_LATENCIES_VIEW) - .put(ATTEMPT_LATENCIES_SELECTOR, ATTEMPT_LATENCIES_VIEW) - .put(SERVER_LATENCIES_SELECTOR, SERVER_LATENCIES_VIEW) - .put(FIRST_RESPONSE_LATENCIES_SELECTOR, FIRST_RESPONSE_LATENCIES_VIEW) - .put(APPLICATION_BLOCKING_LATENCIES_SELECTOR, APPLICATION_BLOCKING_LATENCIES_VIEW) - .put(CLIENT_BLOCKING_LATENCIES_SELECTOR, CLIENT_BLOCKING_LATENCIES_VIEW) - .put(RETRY_COUNT_SELECTOR, RETRY_COUNT_VIEW) - .put(CONNECTIVITY_ERROR_COUNT_SELECTOR, CONNECTIVITY_ERROR_COUNT_VIEW) - .build(); + ImmutableMap.Builder views = ImmutableMap.builder(); + + defineView( + views, + OPERATION_LATENCIES_NAME, + AGGREGATION_WITH_MILLIS_HISTOGRAM, + InstrumentType.HISTOGRAM, + "ms", + ImmutableSet.of(STREAMING.getKey(), STATUS.getKey())); + defineView( + views, + ATTEMPT_LATENCIES_NAME, + AGGREGATION_WITH_MILLIS_HISTOGRAM, + InstrumentType.HISTOGRAM, + "ms", + ImmutableSet.of(STREAMING.getKey(), STATUS.getKey())); + defineView( + views, + SERVER_LATENCIES_NAME, + AGGREGATION_WITH_MILLIS_HISTOGRAM, + InstrumentType.HISTOGRAM, + "ms", + ImmutableSet.of(STREAMING.getKey(), STATUS.getKey())); + defineView( + views, + FIRST_RESPONSE_LATENCIES_NAME, + AGGREGATION_WITH_MILLIS_HISTOGRAM, + InstrumentType.HISTOGRAM, + "ms", + ImmutableSet.of(STATUS.getKey())); + defineView( + views, + APPLICATION_BLOCKING_LATENCIES_NAME, + AGGREGATION_WITH_MILLIS_HISTOGRAM, + InstrumentType.HISTOGRAM, + "ms", + ImmutableSet.of()); + defineView( + views, + CLIENT_BLOCKING_LATENCIES_NAME, + AGGREGATION_WITH_MILLIS_HISTOGRAM, + InstrumentType.HISTOGRAM, + "ms", + ImmutableSet.of()); + defineView( + views, + RETRY_COUNT_NAME, + Aggregation.sum(), + InstrumentType.COUNTER, + "1", + ImmutableSet.of(STATUS.getKey())); + defineView( + views, + CONNECTIVITY_ERROR_COUNT_NAME, + Aggregation.sum(), + InstrumentType.COUNTER, + "1", + ImmutableSet.of(STATUS.getKey())); + + return views.build(); } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java index 06581d5844..8e33f74d24 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java @@ -17,7 +17,6 @@ import com.google.auth.Credentials; import com.google.auth.oauth2.GoogleCredentials; -import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; import io.opentelemetry.sdk.metrics.View; @@ -29,9 +28,7 @@ /** * Register built-in metrics on a custom OpenTelemetry instance. This is for advanced usage, and is * only necessary when wanting to write built-in metrics to cloud monitoring and custom sinks. - * Please refer to {@link - * com.google.cloud.bigtable.data.v2.BigtableDataSettings.Builder#setOpenTelemetry(OpenTelemetry)} - * for example usage. + * Please refer to {@link CustomOpenTelemetryMetricsProvider} for example usage. */ public class BuiltinMetricsView { diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java new file mode 100644 index 0000000000..c03deba758 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java @@ -0,0 +1,63 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub.metrics; + +import io.opentelemetry.api.OpenTelemetry; + +/** + * Set a custom OpenTelemetry instance. + * + *

To register builtin metrics on the custom OpenTelemetry: + * + *

{@code
+ * SdkMeterProviderBuilder sdkMeterProvider = SdkMeterProvider.builder();
+ *
+ * // register Builtin metrics on your meter provider
+ * BuiltinMetricsViews.registerBuiltinMetrics("project-id", sdkMeterProvider);
+ *
+ * // register other metrics reader and views
+ * sdkMeterProvider.registerMetricReader(..);
+ * sdkMeterProvider.registerView(..);
+ *
+ * // create the OTEL instance
+ * OpenTelemetry openTelemetry = OpenTelemetrySdk
+ *     .builder()
+ *     .setMeterProvider(sdkMeterProvider().build());
+ *
+ *
+ * BigtableDataSettings settings = BigtableDataSettings.newBuilder()
+ *   .setProjectId("my-project")
+ *   .setInstanceId("my-instance-id")
+ *   .setMetricsProvider(CustomOpenTelemetryMetricsProvider.create(openTelemetry)
+ *   .build();
+ * }
+ */ +public final class CustomOpenTelemetryMetricsProvider implements MetricsProvider { + + private final OpenTelemetry otel; + + public static CustomOpenTelemetryMetricsProvider create(OpenTelemetry otel) { + return new CustomOpenTelemetryMetricsProvider(otel); + } + + private CustomOpenTelemetryMetricsProvider(OpenTelemetry otel) { + this.otel = otel; + } + + public OpenTelemetry getOpenTelemetry() { + return otel; + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java new file mode 100644 index 0000000000..7dd982dcb5 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java @@ -0,0 +1,21 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub.metrics; + +public final class DefaultMetricsProvider implements MetricsProvider { + + public DefaultMetricsProvider() {} +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsProvider.java new file mode 100644 index 0000000000..727718246a --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsProvider.java @@ -0,0 +1,18 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub.metrics; + +public interface MetricsProvider {} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java new file mode 100644 index 0000000000..7944b611c9 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java @@ -0,0 +1,18 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigtable.data.v2.stub.metrics; + +public class NoopMetricsProvider implements MetricsProvider {} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java index 926feda604..fe3cde4431 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java @@ -175,8 +175,7 @@ public void testNewClientsShareTransportChannel() throws Exception { // FixedCredentialProvider. // So disabling in the test code it's fine. try (BigtableDataClientFactory factory = - BigtableDataClientFactory.create( - defaultSettings.toBuilder().setBuiltinMetricsEnabled(false).build()); + BigtableDataClientFactory.create(defaultSettings.toBuilder().build()); BigtableDataClient ignored1 = factory.createForInstance("project1", "instance1"); BigtableDataClient ignored2 = factory.createForInstance("project2", "instance2"); BigtableDataClient ignored3 = factory.createForInstance("project3", "instance3")) { @@ -256,7 +255,6 @@ public void testCreateWithRefreshingChannel() throws Exception { // Builtin metrics will call getCredentialsProvider at which point it'll be a // FixedCredentialProvider. // So disabling in the test code it's fine. - .setBuiltinMetricsEnabled(false) .setRefreshingChannel(true); builder .stubSettings() diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java index 26566d5170..4d615080a5 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java @@ -37,6 +37,7 @@ import com.google.cloud.bigtable.data.v2.models.RowMutation; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsView; +import com.google.cloud.bigtable.data.v2.stub.metrics.CustomOpenTelemetryMetricsProvider; import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv; import com.google.cloud.bigtable.test_helpers.env.PrefixGenerator; import com.google.cloud.bigtable.test_helpers.env.TestEnvRule; @@ -144,7 +145,10 @@ public void setup() throws IOException { clientCustomOtel = BigtableDataClient.create( - settings.setOpenTelemetry(openTelemetry).setAppProfileId(appProfileCustomOtel).build()); + settings + .setMetricsProvider(CustomOpenTelemetryMetricsProvider.create(openTelemetry)) + .setAppProfileId(appProfileCustomOtel) + .build()); clientDefault = BigtableDataClient.create(settings.setAppProfileId(appProfileDefault).build()); } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java index 2aac584319..d92ba04ae5 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java @@ -27,6 +27,7 @@ import com.google.cloud.bigtable.data.v2.models.Row; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsView; +import com.google.cloud.bigtable.data.v2.stub.metrics.CustomOpenTelemetryMetricsProvider; import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv; import com.google.cloud.bigtable.test_helpers.env.TestEnvRule; import com.google.common.collect.Lists; @@ -71,8 +72,7 @@ public void setup() throws IOException { OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); - settings.setOpenTelemetry(openTelemetry); - + settings.setMetricsProvider(CustomOpenTelemetryMetricsProvider.create(openTelemetry)); client = BigtableDataClient.create(settings.build()); } @@ -101,8 +101,7 @@ public void testSuccess() throws Exception { List metrics = metricReader.collectAllMetrics().stream() - .filter( - m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_VIEW.getName())) + .filter(m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME)) .collect(Collectors.toList()); assertThat(metrics.size()).isEqualTo(1); @@ -132,8 +131,7 @@ public void testFailure() { List metrics = metricReader.collectAllMetrics().stream() - .filter( - m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_VIEW.getName())) + .filter(m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME)) .collect(Collectors.toList()); assertThat(metrics.size()).isEqualTo(1); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java index 28b4fd8758..9b5003b036 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java @@ -26,6 +26,7 @@ import com.google.cloud.bigtable.data.v2.models.RowMutation; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants; import com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsView; +import com.google.cloud.bigtable.data.v2.stub.metrics.CustomOpenTelemetryMetricsProvider; import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv; import com.google.cloud.bigtable.test_helpers.env.TestEnvRule; import io.opentelemetry.api.OpenTelemetry; @@ -70,7 +71,7 @@ public void setup() throws IOException { OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); - settings.setOpenTelemetry(openTelemetry); + settings.setMetricsProvider(CustomOpenTelemetryMetricsProvider.create(openTelemetry)); client = BigtableDataClient.create(settings.build()); } @@ -105,8 +106,7 @@ public void testSuccess() throws Exception { List metrics = metricReader.collectAllMetrics().stream() - .filter( - m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_VIEW.getName())) + .filter(m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME)) .collect(Collectors.toList()); assertThat(metrics.size()).isEqualTo(1); @@ -149,8 +149,7 @@ public void testFailure() throws Exception { List metrics = metricReader.collectAllMetrics().stream() - .filter( - m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_VIEW.getName())) + .filter(m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME)) .collect(Collectors.toList()); assertThat(metrics.size()).isEqualTo(1); From 96099b81f3145186a329378c25f86c4be81b7d16 Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Tue, 30 Jan 2024 17:38:41 -0500 Subject: [PATCH 25/34] refactor and fix tests --- .../data/v2/BigtableDataSettings.java | 15 ++++++++---- .../data/v2/stub/EnhancedBigtableStub.java | 24 +++++++------------ .../v2/stub/EnhancedBigtableStubSettings.java | 2 +- .../stub/metrics/BigtableExporterUtils.java | 2 +- .../stub/metrics/BuiltinMetricsConstants.java | 6 ++--- .../v2/stub/metrics/BuiltinMetricsTracer.java | 5 ++-- .../metrics/BuiltinMetricsTracerFactory.java | 4 ++-- .../CustomOpenTelemetryMetricsProvider.java | 2 +- .../stub/metrics/DefaultMetricsProvider.java | 11 ++++++++- .../v2/stub/metrics/NoopMetricsProvider.java | 13 +++++++++- .../v2/BigtableDataClientFactoryTest.java | 8 ++++++- .../v2/it/StreamingMetricsMetadataIT.java | 4 ++-- .../data/v2/it/UnaryMetricsMetadataIT.java | 4 ++-- .../EnhancedBigtableStubSettingsTest.java | 3 +-- .../BigtableCloudMonitoringExporterTest.java | 2 +- .../metrics/BuiltinMetricsTracerTest.java | 4 ++-- 16 files changed, 67 insertions(+), 42 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java index 84bb1c7d8c..a533f591a4 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java @@ -533,11 +533,16 @@ public boolean isBulkMutationFlowControlEnabled() { } /** - * Sets the {@link MetricsProvider}. By default, this is set to {@link - * com.google.cloud.bigtable.data.v2.stub.metrics.DefaultMetricsProvider} which will collect and - * export built-in metrics. To disable built-in metrics, set it to {@link - * com.google.cloud.bigtable.data.v2.stub.metrics.NoopMetricsProvider}. To use a custom - * OpenTelemetry, refer to {@link + * Sets the {@link MetricsProvider}. + * + *

By default, this is set to {@link + * com.google.cloud.bigtable.data.v2.stub.metrics.DefaultMetricsProvider#INSTANCE} which will + * collect and export client side metrics. + * + *

To disable client side metrics, set it to {@link + * com.google.cloud.bigtable.data.v2.stub.metrics.NoopMetricsProvider#INSTANCE}. + * + *

To use a custom OpenTelemetry instance, refer to {@link * com.google.cloud.bigtable.data.v2.stub.metrics.CustomOpenTelemetryMetricsProvider} on how to * set it up. */ diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java index 133cb3ea7f..4421d76a6f 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java @@ -102,6 +102,7 @@ import com.google.cloud.bigtable.data.v2.stub.metrics.CustomOpenTelemetryMetricsProvider; import com.google.cloud.bigtable.data.v2.stub.metrics.DefaultMetricsProvider; import com.google.cloud.bigtable.data.v2.stub.metrics.MetricsTracerFactory; +import com.google.cloud.bigtable.data.v2.stub.metrics.NoopMetricsProvider; import com.google.cloud.bigtable.data.v2.stub.metrics.RpcMeasureConstants; import com.google.cloud.bigtable.data.v2.stub.metrics.StatsHeadersServerStreamingCallable; import com.google.cloud.bigtable.data.v2.stub.metrics.StatsHeadersUnaryCallable; @@ -275,13 +276,9 @@ public static ApiTracerFactory createBigtableTracerFactory( new OpencensusTracerFactory( ImmutableMap.builder() // Annotate traces with the same tags as metrics - .put(RpcMeasureConstants.BIGTABLE_PROJECT_ID.getName(), settings.getProjectId()) - .put( - RpcMeasureConstants.BIGTABLE_INSTANCE_ID.getName(), - settings.getInstanceId()) - .put( - RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID.getName(), - settings.getAppProfileId()) + .put(RpcMeasureConstants.BIGTABLE_PROJECT_ID.getName(), projectId) + .put(RpcMeasureConstants.BIGTABLE_INSTANCE_ID.getName(), instanceId) + .put(RpcMeasureConstants.BIGTABLE_APP_PROFILE_ID.getName(), appProfileId) // Also annotate traces with library versions .put("gax", GaxGrpcProperties.getGaxGrpcVersion()) .put("grpc", GaxGrpcProperties.getGrpcVersion()) @@ -292,13 +289,7 @@ public static ApiTracerFactory createBigtableTracerFactory( // Add user configured tracer .add(settings.getTracerFactory()); Attributes otelAttributes = - Attributes.of( - PROJECT_ID, - settings.getProjectId(), - INSTANCE_ID, - settings.getInstanceId(), - APP_PROFILE, - settings.getAppProfileId()); + Attributes.of(PROJECT_ID, projectId, INSTANCE_ID, instanceId, APP_PROFILE, appProfileId); BuiltinMetricsTracerFactory builtinMetricsTracerFactory = setupBuiltinMetricsTracerFactory(settings.toBuilder(), otelAttributes); if (builtinMetricsTracerFactory != null) { @@ -323,8 +314,11 @@ private static BuiltinMetricsTracerFactory setupBuiltinMetricsTracerFactory( OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); return BuiltinMetricsTracerFactory.create(openTelemetry, attributes); + } else if (settings.getMetricsProvider() instanceof NoopMetricsProvider) { + return null; } - return null; + throw new IOException( + "Invalid MetricsProvider type " + settings.getMetricsProvider().getClass()); } private static void patchCredentials(EnhancedBigtableStubSettings.Builder settings) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java index 4111bdba23..7a33c59309 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java @@ -664,7 +664,7 @@ private Builder() { this.enableRoutingCookie = true; this.enableRetryInfo = true; - metricsProvider = new DefaultMetricsProvider(); + metricsProvider = DefaultMetricsProvider.INSTANCE; // Defaults provider BigtableStubSettings.Builder baseDefaults = BigtableStubSettings.newBuilder(); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java index 5fc8499325..b7aa774073 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java @@ -107,7 +107,7 @@ static List convertCollectionToListOfTimeSeries( if (!metricData .getInstrumentationScopeInfo() .getName() - .equals(BuiltinMetricsConstants.SCOPE)) { + .equals(BuiltinMetricsConstants.METER_NAME)) { continue; } metricData.getData().getPoints().stream() diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java index c7abc91007..fcc38226da 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java @@ -61,7 +61,7 @@ public class BuiltinMetricsConstants { 300.0, 400.0, 500.0, 650.0, 800.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0, 50000.0, 100000.0, 200000.0, 400000.0, 800000.0, 1600000.0, 3200000.0)); // max is 53.3 minutes - static final String SCOPE = "bigtable.googleapis.com"; + public static final String METER_NAME = "bigtable.googleapis.com/internal/client/"; static final Set COMMON_ATTRIBUTES = ImmutableSet.of( @@ -84,7 +84,7 @@ static void defineView( InstrumentSelector selector = InstrumentSelector.builder() .setName(id) - .setMeterName(SCOPE) + .setMeterName(METER_NAME) .setType(type) .setUnit(unit) .build(); @@ -92,7 +92,7 @@ static void defineView( ImmutableSet.builder().addAll(COMMON_ATTRIBUTES).addAll(extraAttributes).build(); View view = View.builder() - .setName(SCOPE + "/internal/client/" + id) + .setName(METER_NAME + id) .setAggregation(aggregation) .setAttributeFilter(attributesFilter) .build(); diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java index e47e9c8fab..8bb6ed2d82 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java @@ -299,9 +299,10 @@ private void recordOperationCompletion(@Nullable Throwable status) { operationLatenciesHistogram.record( operationLatency, attributes.toBuilder().put(STREAMING, isStreaming).put(STATUS, statusStr).build()); + + long applicationLatencyNano = operationLatencyNano - totalServerLatencyNano.get(); applicationBlockingLatenciesHistogram.record( - Duration.ofNanos(operationLatencyNano - totalServerLatencyNano.get()).toMillis(), - attributes); + Duration.ofNanos(applicationLatencyNano).toMillis(), attributes); if (operationType == OperationType.ServerStreaming && spanName.getMethodName().equals("ReadRows")) { diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java index aed3f06acb..575907ffde 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java @@ -20,9 +20,9 @@ import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.FIRST_RESPONSE_LATENCIES_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METER_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SCOPE; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_NAME; import com.google.api.core.InternalApi; @@ -65,7 +65,7 @@ public static BuiltinMetricsTracerFactory create( BuiltinMetricsTracerFactory(OpenTelemetry openTelemetry, Attributes attributes) { this.attributes = attributes; - Meter meter = openTelemetry.getMeter(SCOPE); + Meter meter = openTelemetry.getMeter(METER_NAME); operationLatenciesHistogram = meter diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java index c03deba758..55dd7abdac 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java @@ -37,7 +37,7 @@ * .builder() * .setMeterProvider(sdkMeterProvider().build()); * - * + * // Override MetricsProvider in BigtableDataSettings * BigtableDataSettings settings = BigtableDataSettings.newBuilder() * .setProjectId("my-project") * .setInstanceId("my-instance-id") diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java index 7dd982dcb5..f577f1ebe3 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java @@ -15,7 +15,16 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; +/** + * Set {@link + * com.google.cloud.bigtable.data.v2.BigtableDataSettings.Builder#setMetricsProvider(MetricsProvider)}, + * to {@link this#INSTANCE} to enable collecting and export client side metrics + * https://cloud.google.com/bigtable/docs/client-side-metrics. This is the default setting in {@link + * com.google.cloud.bigtable.data.v2.BigtableDataSettings}. + */ public final class DefaultMetricsProvider implements MetricsProvider { - public DefaultMetricsProvider() {} + public static DefaultMetricsProvider INSTANCE = new DefaultMetricsProvider(); + + private DefaultMetricsProvider() {} } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java index 7944b611c9..8a97beea93 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java @@ -15,4 +15,15 @@ */ package com.google.cloud.bigtable.data.v2.stub.metrics; -public class NoopMetricsProvider implements MetricsProvider {} +/** + * Set {@link + * com.google.cloud.bigtable.data.v2.BigtableDataSettings.Builder#setMetricsProvider(MetricsProvider)}, + * to {@link this#INSTANCE} to disable collecting and export client side metrics + * https://cloud.google.com/bigtable/docs/client-side-metrics. + */ +public class NoopMetricsProvider implements MetricsProvider { + + public static NoopMetricsProvider INSTANCE = new NoopMetricsProvider(); + + private NoopMetricsProvider() {} +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java index fe3cde4431..713288dab5 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java @@ -36,6 +36,7 @@ import com.google.bigtable.v2.ReadRowsResponse; import com.google.cloud.bigtable.data.v2.internal.NameUtil; import com.google.cloud.bigtable.data.v2.models.RowMutation; +import com.google.cloud.bigtable.data.v2.stub.metrics.NoopMetricsProvider; import com.google.common.base.Preconditions; import com.google.common.io.BaseEncoding; import io.grpc.Attributes; @@ -175,7 +176,11 @@ public void testNewClientsShareTransportChannel() throws Exception { // FixedCredentialProvider. // So disabling in the test code it's fine. try (BigtableDataClientFactory factory = - BigtableDataClientFactory.create(defaultSettings.toBuilder().build()); + BigtableDataClientFactory.create( + defaultSettings + .toBuilder() + .setMetricsProvider(NoopMetricsProvider.INSTANCE) + .build()); BigtableDataClient ignored1 = factory.createForInstance("project1", "instance1"); BigtableDataClient ignored2 = factory.createForInstance("project2", "instance2"); BigtableDataClient ignored3 = factory.createForInstance("project3", "instance3")) { @@ -255,6 +260,7 @@ public void testCreateWithRefreshingChannel() throws Exception { // Builtin metrics will call getCredentialsProvider at which point it'll be a // FixedCredentialProvider. // So disabling in the test code it's fine. + .setMetricsProvider(NoopMetricsProvider.INSTANCE) .setRefreshingChannel(true); builder .stubSettings() diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java index d92ba04ae5..0ce6b25736 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java @@ -101,7 +101,7 @@ public void testSuccess() throws Exception { List metrics = metricReader.collectAllMetrics().stream() - .filter(m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME)) + .filter(m -> m.getName().contains(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME)) .collect(Collectors.toList()); assertThat(metrics.size()).isEqualTo(1); @@ -131,7 +131,7 @@ public void testFailure() { List metrics = metricReader.collectAllMetrics().stream() - .filter(m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME)) + .filter(m -> m.getName().contains(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME)) .collect(Collectors.toList()); assertThat(metrics.size()).isEqualTo(1); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java index 9b5003b036..0428748128 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java @@ -106,7 +106,7 @@ public void testSuccess() throws Exception { List metrics = metricReader.collectAllMetrics().stream() - .filter(m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME)) + .filter(m -> m.getName().contains(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME)) .collect(Collectors.toList()); assertThat(metrics.size()).isEqualTo(1); @@ -149,7 +149,7 @@ public void testFailure() throws Exception { List metrics = metricReader.collectAllMetrics().stream() - .filter(m -> m.getName().equals(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME)) + .filter(m -> m.getName().contains(BuiltinMetricsConstants.OPERATION_LATENCIES_NAME)) .collect(Collectors.toList()); assertThat(metrics.size()).isEqualTo(1); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java index 42e60e9fa6..ca9ae7b00a 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettingsTest.java @@ -884,8 +884,7 @@ public void enableRetryInfoFalseValueTest() throws IOException { "generateInitialChangeStreamPartitionsSettings", "readChangeStreamSettings", "pingAndWarmSettings", - "isBuiltinMetricsEnabled", - "openTelemetry", + "metricsProvider", }; @Test diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java index f0bf83bf26..46e88b2dd9 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java @@ -103,7 +103,7 @@ public void setUp() { resource = Resource.create(Attributes.empty()); - scope = InstrumentationScopeInfo.create("bigtable.googleapis.com"); + scope = InstrumentationScopeInfo.create(BuiltinMetricsConstants.METER_NAME); } @After diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java index 938be62c91..4461872e0a 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java @@ -21,10 +21,10 @@ import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_NAME; +import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METER_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METHOD; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_NAME; -import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SCOPE; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_NAME; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STATUS; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STREAMING; @@ -159,7 +159,7 @@ public void setUp() throws Exception { .setResource(Resource.create(baseAttributes)) .build(); - Meter meter = meterProvider.get(SCOPE); + Meter meter = meterProvider.get(METER_NAME); OpenTelemetrySdk otel = OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build(); facotry = BuiltinMetricsTracerFactory.create(otel, baseAttributes); From 803f5b5097677791aa9db410172938060ddcf4c7 Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Wed, 31 Jan 2024 09:14:45 -0500 Subject: [PATCH 26/34] remove unused dependency --- google-cloud-bigtable/pom.xml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/google-cloud-bigtable/pom.xml b/google-cloud-bigtable/pom.xml index 66f7992551..78e4b55776 100644 --- a/google-cloud-bigtable/pom.xml +++ b/google-cloud-bigtable/pom.xml @@ -64,19 +64,6 @@ - - com.google.cloud - google-cloud-bigtable-stats - - - - io.opencensus - * - - - com.google.api From 66ab16a27fbc5950bae5bcd34ee6bcbb270a9690 Mon Sep 17 00:00:00 2001 From: Mattie Fu Date: Wed, 31 Jan 2024 09:29:46 -0500 Subject: [PATCH 27/34] add some javadoc --- .../data/v2/stub/EnhancedBigtableStub.java | 3 +-- .../v2/stub/EnhancedBigtableStubSettings.java | 15 +++++++++++++++ .../CustomOpenTelemetryMetricsProvider.java | 2 +- .../data/v2/stub/metrics/MetricsProvider.java | 7 +++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java index 4421d76a6f..596b79c845 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java @@ -317,8 +317,7 @@ private static BuiltinMetricsTracerFactory setupBuiltinMetricsTracerFactory( } else if (settings.getMetricsProvider() instanceof NoopMetricsProvider) { return null; } - throw new IOException( - "Invalid MetricsProvider type " + settings.getMetricsProvider().getClass()); + throw new IOException("Invalid MetricsProvider type " + settings.getMetricsProvider()); } private static void patchCredentials(EnhancedBigtableStubSettings.Builder settings) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java index 7a33c59309..e0c57348df 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java @@ -929,11 +929,26 @@ public Builder setJwtAudienceMapping(Map jwtAudienceMapping) { return this; } + /** + * Sets the {@link MetricsProvider}. + * + *

By default, this is set to {@link + * com.google.cloud.bigtable.data.v2.stub.metrics.DefaultMetricsProvider#INSTANCE} which will + * collect and export client side metrics. + * + *

To disable client side metrics, set it to {@link + * com.google.cloud.bigtable.data.v2.stub.metrics.NoopMetricsProvider#INSTANCE}. + * + *

To use a custom OpenTelemetry instance, refer to {@link + * com.google.cloud.bigtable.data.v2.stub.metrics.CustomOpenTelemetryMetricsProvider} on how to + * set it up. + */ public Builder setMetricsProvider(MetricsProvider metricsProvider) { this.metricsProvider = metricsProvider; return this; } + /** Gets the {@link MetricsProvider}. */ public MetricsProvider getMetricsProvider() { return this.metricsProvider; } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java index 55dd7abdac..5f483bbe46 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java @@ -20,7 +20,7 @@ /** * Set a custom OpenTelemetry instance. * - *

To register builtin metrics on the custom OpenTelemetry: + *

To register client side metrics on the custom OpenTelemetry: * *

{@code
  * SdkMeterProviderBuilder sdkMeterProvider = SdkMeterProvider.builder();
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsProvider.java
index 727718246a..251bb41619 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsProvider.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/MetricsProvider.java
@@ -15,4 +15,11 @@
  */
 package com.google.cloud.bigtable.data.v2.stub.metrics;
 
+import com.google.api.core.InternalExtensionOnly;
+
+/**
+ * Provide client side metrics https://cloud.google.com/bigtable/docs/client-side-metrics
+ * implementations.
+ */
+@InternalExtensionOnly
 public interface MetricsProvider {}

From 4f577faa68075e2c20a6dc54e023e3680504e4a1 Mon Sep 17 00:00:00 2001
From: Mattie Fu 
Date: Tue, 6 Feb 2024 14:43:16 -0500
Subject: [PATCH 28/34] address part of the comments

---
 .../data/v2/BigtableDataSettings.java         |  13 +-
 .../data/v2/stub/EnhancedBigtableStub.java    |  18 +-
 .../v2/stub/EnhancedBigtableStubSettings.java |   4 +-
 .../BigtableCloudMonitoringExporter.java      |   2 +
 .../stub/metrics/BigtableExporterUtils.java   |  19 +-
 .../stub/metrics/BuiltinMetricsConstants.java |  55 ++---
 .../v2/stub/metrics/BuiltinMetricsTracer.java |  48 ++--
 .../v2/stub/metrics/BuiltinMetricsView.java   |   8 +-
 .../CustomOpenTelemetryMetricsProvider.java   |   3 +-
 .../v2/stub/metrics/NoopMetricsProvider.java  |   2 +-
 .../bigtable/data/v2/it/BuiltinMetricsIT.java |  43 ++--
 .../v2/it/StreamingMetricsMetadataIT.java     |   8 +-
 .../data/v2/it/UnaryMetricsMetadataIT.java    |   8 +-
 .../BigtableCloudMonitoringExporterTest.java  |  56 ++---
 .../stub/metrics/BuiltinMetricsTestUtils.java |   4 +
 .../metrics/BuiltinMetricsTracerTest.java     | 206 +++++++++---------
 16 files changed, 266 insertions(+), 231 deletions(-)

diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java
index a533f591a4..9ee8648698 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java
@@ -35,7 +35,6 @@
 import io.grpc.ManagedChannelBuilder;
 import java.io.IOException;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Logger;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -75,7 +74,7 @@ public final class BigtableDataSettings {
 
   private static final Logger LOGGER = Logger.getLogger(BigtableDataSettings.class.getName());
   private static final String BIGTABLE_EMULATOR_HOST_ENV_VAR = "BIGTABLE_EMULATOR_HOST";
-  private static final AtomicBoolean BUILTIN_METRICS_REGISTERED = new AtomicBoolean(false);
+  @Nullable private static Credentials credentials;
 
   private final EnhancedBigtableStubSettings stubSettings;
 
@@ -214,7 +213,15 @@ public static void enableBuiltinMetrics() throws IOException {}
    *     now. Please refer {@link BigtableDataSettings.Builder#setMetricsProvider(MetricsProvider)}
    *     on how to enable or disable built-in metrics.
    */
-  public static void enableBuiltinMetrics(Credentials credentials) throws IOException {}
+  public static void enableBuiltinMetrics(Credentials credentials) throws IOException {
+    BigtableDataSettings.credentials = credentials;
+  }
+
+  /** Get the metrics credentials if it's set by {@link #enableBuiltinMetrics(Credentials)}. */
+  @InternalApi
+  public static Credentials getMetricsCredentials() {
+    return credentials;
+  }
 
   /** Returns the target project id. */
   public String getProjectId() {
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java
index 596b79c845..156618d7f8 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java
@@ -15,9 +15,9 @@
  */
 package com.google.cloud.bigtable.data.v2.stub;
 
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APP_PROFILE;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PROJECT_ID;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APP_PROFILE_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PROJECT_ID_KEY;
 
 import com.google.api.core.BetaApi;
 import com.google.api.core.InternalApi;
@@ -72,6 +72,7 @@
 import com.google.bigtable.v2.SampleRowKeysRequest;
 import com.google.bigtable.v2.SampleRowKeysResponse;
 import com.google.cloud.bigtable.Version;
+import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
 import com.google.cloud.bigtable.data.v2.internal.JwtCredentialsWithAudience;
 import com.google.cloud.bigtable.data.v2.internal.RequestContext;
 import com.google.cloud.bigtable.data.v2.models.BulkMutation;
@@ -289,7 +290,8 @@ public static ApiTracerFactory createBigtableTracerFactory(
         // Add user configured tracer
         .add(settings.getTracerFactory());
     Attributes otelAttributes =
-        Attributes.of(PROJECT_ID, projectId, INSTANCE_ID, instanceId, APP_PROFILE, appProfileId);
+        Attributes.of(
+            PROJECT_ID_KEY, projectId, INSTANCE_ID_KEY, instanceId, APP_PROFILE_KEY, appProfileId);
     BuiltinMetricsTracerFactory builtinMetricsTracerFactory =
         setupBuiltinMetricsTracerFactory(settings.toBuilder(), otelAttributes);
     if (builtinMetricsTracerFactory != null) {
@@ -307,10 +309,12 @@ private static BuiltinMetricsTracerFactory setupBuiltinMetricsTracerFactory(
           customMetricsProvider.getOpenTelemetry(), attributes);
     } else if (settings.getMetricsProvider() instanceof DefaultMetricsProvider) {
       SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder();
+      Credentials credentials =
+          BigtableDataSettings.getMetricsCredentials() != null
+              ? BigtableDataSettings.getMetricsCredentials()
+              : settings.getCredentialsProvider().getCredentials();
       BuiltinMetricsView.registerBuiltinMetrics(
-          settings.getProjectId(),
-          settings.getCredentialsProvider().getCredentials(),
-          meterProvider);
+          settings.getProjectId(), credentials, meterProvider);
       OpenTelemetry openTelemetry =
           OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build();
       return BuiltinMetricsTracerFactory.create(openTelemetry, attributes);
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java
index e0c57348df..dd8156d5cd 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubSettings.java
@@ -231,7 +231,7 @@ public class EnhancedBigtableStubSettings extends StubSettings jwtAudienceMapping) {
      * set it up.
      */
     public Builder setMetricsProvider(MetricsProvider metricsProvider) {
-      this.metricsProvider = metricsProvider;
+      this.metricsProvider = Preconditions.checkNotNull(metricsProvider);
       return this;
     }
 
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java
index 3d6d4d08f4..019fe88a50 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java
@@ -57,6 +57,8 @@ public final class BigtableCloudMonitoringExporter implements MetricExporter {
   private static final Logger logger =
       Logger.getLogger(BigtableCloudMonitoringExporter.class.getName());
 
+  // This system property can be used to override the monitoring endpoint
+  // to a different environment. It's meant for internal testing only.
   private static final String MONITORING_ENDPOINT =
       MoreObjects.firstNonNull(
           System.getProperty("bigtable.test-monitoring-endpoint"),
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java
index b7aa774073..e3cf50b070 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java
@@ -25,12 +25,12 @@
 import static com.google.api.MetricDescriptor.ValueType.DISTRIBUTION;
 import static com.google.api.MetricDescriptor.ValueType.DOUBLE;
 import static com.google.api.MetricDescriptor.ValueType.INT64;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_UID;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PROJECT_ID;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_UID_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PROJECT_ID_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID_KEY;
 
 import com.google.api.Distribution;
 import com.google.api.Metric;
@@ -70,7 +70,7 @@ class BigtableExporterUtils {
 
   // These metric labels will be promoted to the bigtable_table monitored resource fields
   private static final Set> PROMOTED_RESOURCE_LABELS =
-      ImmutableSet.of(PROJECT_ID, INSTANCE_ID, TABLE_ID, CLUSTER_ID, ZONE_ID);
+      ImmutableSet.of(PROJECT_ID_KEY, INSTANCE_ID_KEY, TABLE_ID_KEY, CLUSTER_ID_KEY, ZONE_ID_KEY);
 
   private BigtableExporterUtils() {}
 
@@ -96,7 +96,7 @@ static String getDefaultTaskValue() {
   }
 
   static String getProjectId(PointData pointData) {
-    return pointData.getAttributes().get(PROJECT_ID);
+    return pointData.getAttributes().get(PROJECT_ID_KEY);
   }
 
   static List convertCollectionToListOfTimeSeries(
@@ -108,6 +108,7 @@ static List convertCollectionToListOfTimeSeries(
           .getInstrumentationScopeInfo()
           .getName()
           .equals(BuiltinMetricsConstants.METER_NAME)) {
+        // Filter out metric data for instruments that are not part of the bigtable builtin metrics
         continue;
       }
       metricData.getData().getPoints().stream()
@@ -136,7 +137,7 @@ private static TimeSeries convertPointToTimeSeries(
         metricBuilder.putLabels(key.getKey(), String.valueOf(attributes.get(key)));
       }
     }
-    metricBuilder.putLabels(CLIENT_UID.getKey(), taskId);
+    metricBuilder.putLabels(CLIENT_UID_KEY.getKey(), taskId);
 
     TimeSeries.Builder builder =
         TimeSeries.newBuilder()
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java
index fcc38226da..f24c806ba7 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java
@@ -31,19 +31,22 @@
 @InternalApi
 public class BuiltinMetricsConstants {
 
-  public static final AttributeKey PROJECT_ID = AttributeKey.stringKey("project_id");
-  public static final AttributeKey INSTANCE_ID = AttributeKey.stringKey("instance");
-  public static final AttributeKey TABLE_ID = AttributeKey.stringKey("table");
-  public static final AttributeKey CLUSTER_ID = AttributeKey.stringKey("cluster");
-  public static final AttributeKey ZONE_ID = AttributeKey.stringKey("zone");
-  static final AttributeKey CLIENT_UID = AttributeKey.stringKey("client_uid");
+  // Metric attribute keys for monitored resource
+  public static final AttributeKey PROJECT_ID_KEY = AttributeKey.stringKey("project_id");
+  public static final AttributeKey INSTANCE_ID_KEY = AttributeKey.stringKey("instance");
+  public static final AttributeKey TABLE_ID_KEY = AttributeKey.stringKey("table");
+  public static final AttributeKey CLUSTER_ID_KEY = AttributeKey.stringKey("cluster");
+  public static final AttributeKey ZONE_ID_KEY = AttributeKey.stringKey("zone");
 
-  public static final AttributeKey APP_PROFILE = AttributeKey.stringKey("app_profile");
-  public static final AttributeKey STREAMING = AttributeKey.booleanKey("streaming");
-  static final AttributeKey METHOD = AttributeKey.stringKey("method");
-  static final AttributeKey STATUS = AttributeKey.stringKey("status");
-  static final AttributeKey CLIENT_NAME = AttributeKey.stringKey("client_name");
+  // Metric attribute keys for labels
+  public static final AttributeKey APP_PROFILE_KEY = AttributeKey.stringKey("app_profile");
+  public static final AttributeKey STREAMING_KEY = AttributeKey.booleanKey("streaming");
+  static final AttributeKey METHOD_KEY = AttributeKey.stringKey("method");
+  static final AttributeKey STATUS_KEY = AttributeKey.stringKey("status");
+  static final AttributeKey CLIENT_NAME_KEY = AttributeKey.stringKey("client_name");
+  static final AttributeKey CLIENT_UID_KEY = AttributeKey.stringKey("client_uid");
 
+  // Metric names
   public static final String OPERATION_LATENCIES_NAME = "operation_latencies";
   static final String ATTEMPT_LATENCIES_NAME = "attempt_latencies";
   static final String RETRY_COUNT_NAME = "retry_count";
@@ -53,6 +56,8 @@ public class BuiltinMetricsConstants {
   static final String APPLICATION_BLOCKING_LATENCIES_NAME = "application_latencies";
   static final String CLIENT_BLOCKING_LATENCIES_NAME = "throttling_latencies";
 
+  // Buckets under 100,000 are identical to buckets for server side metrics handler_latencies.
+  // Extending client side bucket to up to 3,200,000.
   private static final Aggregation AGGREGATION_WITH_MILLIS_HISTOGRAM =
       Aggregation.explicitBucketHistogram(
           ImmutableList.of(
@@ -65,14 +70,14 @@ public class BuiltinMetricsConstants {
 
   static final Set COMMON_ATTRIBUTES =
       ImmutableSet.of(
-          PROJECT_ID.getKey(),
-          INSTANCE_ID.getKey(),
-          TABLE_ID.getKey(),
-          APP_PROFILE.getKey(),
-          CLUSTER_ID.getKey(),
-          ZONE_ID.getKey(),
-          METHOD.getKey(),
-          CLIENT_NAME.getKey());
+          PROJECT_ID_KEY.getKey(),
+          INSTANCE_ID_KEY.getKey(),
+          TABLE_ID_KEY.getKey(),
+          APP_PROFILE_KEY.getKey(),
+          CLUSTER_ID_KEY.getKey(),
+          ZONE_ID_KEY.getKey(),
+          METHOD_KEY.getKey(),
+          CLIENT_NAME_KEY.getKey());
 
   static void defineView(
       ImmutableMap.Builder viewMap,
@@ -109,28 +114,28 @@ public static Map getAllViews() {
         AGGREGATION_WITH_MILLIS_HISTOGRAM,
         InstrumentType.HISTOGRAM,
         "ms",
-        ImmutableSet.of(STREAMING.getKey(), STATUS.getKey()));
+        ImmutableSet.of(STREAMING_KEY.getKey(), STATUS_KEY.getKey()));
     defineView(
         views,
         ATTEMPT_LATENCIES_NAME,
         AGGREGATION_WITH_MILLIS_HISTOGRAM,
         InstrumentType.HISTOGRAM,
         "ms",
-        ImmutableSet.of(STREAMING.getKey(), STATUS.getKey()));
+        ImmutableSet.of(STREAMING_KEY.getKey(), STATUS_KEY.getKey()));
     defineView(
         views,
         SERVER_LATENCIES_NAME,
         AGGREGATION_WITH_MILLIS_HISTOGRAM,
         InstrumentType.HISTOGRAM,
         "ms",
-        ImmutableSet.of(STREAMING.getKey(), STATUS.getKey()));
+        ImmutableSet.of(STREAMING_KEY.getKey(), STATUS_KEY.getKey()));
     defineView(
         views,
         FIRST_RESPONSE_LATENCIES_NAME,
         AGGREGATION_WITH_MILLIS_HISTOGRAM,
         InstrumentType.HISTOGRAM,
         "ms",
-        ImmutableSet.of(STATUS.getKey()));
+        ImmutableSet.of(STATUS_KEY.getKey()));
     defineView(
         views,
         APPLICATION_BLOCKING_LATENCIES_NAME,
@@ -151,14 +156,14 @@ public static Map getAllViews() {
         Aggregation.sum(),
         InstrumentType.COUNTER,
         "1",
-        ImmutableSet.of(STATUS.getKey()));
+        ImmutableSet.of(STATUS_KEY.getKey()));
     defineView(
         views,
         CONNECTIVITY_ERROR_COUNT_NAME,
         Aggregation.sum(),
         InstrumentType.COUNTER,
         "1",
-        ImmutableSet.of(STATUS.getKey()));
+        ImmutableSet.of(STATUS_KEY.getKey()));
 
     return views.build();
   }
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
index 8bb6ed2d82..c8c7485b36 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
@@ -16,13 +16,13 @@
 package com.google.cloud.bigtable.data.v2.stub.metrics;
 
 import static com.google.api.gax.tracing.ApiTracerFactory.OperationType;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METHOD;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STATUS;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STREAMING;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METHOD_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STATUS_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STREAMING_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID_KEY;
 
 import com.google.api.gax.retrying.ServerStreamingAttemptException;
 import com.google.api.gax.tracing.SpanName;
@@ -279,11 +279,11 @@ private void recordOperationCompletion(@Nullable Throwable status) {
     Attributes attributes =
         baseAttributes
             .toBuilder()
-            .put(TABLE_ID, tableId)
-            .put(CLUSTER_ID, cluster)
-            .put(ZONE_ID, zone)
-            .put(METHOD, spanName.toString())
-            .put(CLIENT_NAME, NAME)
+            .put(TABLE_ID_KEY, tableId)
+            .put(CLUSTER_ID_KEY, cluster)
+            .put(ZONE_ID_KEY, zone)
+            .put(METHOD_KEY, spanName.toString())
+            .put(CLIENT_NAME_KEY, NAME)
             .build();
 
     long operationLatency = operationTimer.elapsed(TimeUnit.MILLISECONDS);
@@ -292,13 +292,13 @@ private void recordOperationCompletion(@Nullable Throwable status) {
     // Only record when retry count is greater than 0 so the retry
     // graph will be less confusing
     if (attemptCount > 1) {
-      retryCounter.add(attemptCount - 1, attributes.toBuilder().put(STATUS, statusStr).build());
+      retryCounter.add(attemptCount - 1, attributes.toBuilder().put(STATUS_KEY, statusStr).build());
     }
 
     // serverLatencyTimer should already be stopped in recordAttemptCompletion
     operationLatenciesHistogram.record(
         operationLatency,
-        attributes.toBuilder().put(STREAMING, isStreaming).put(STATUS, statusStr).build());
+        attributes.toBuilder().put(STREAMING_KEY, isStreaming).put(STATUS_KEY, statusStr).build());
 
     long applicationLatencyNano = operationLatencyNano - totalServerLatencyNano.get();
     applicationBlockingLatenciesHistogram.record(
@@ -308,7 +308,7 @@ private void recordOperationCompletion(@Nullable Throwable status) {
         && spanName.getMethodName().equals("ReadRows")) {
       firstResponseLatenciesHistogram.record(
           firstResponsePerOpTimer.elapsed(TimeUnit.MILLISECONDS),
-          attributes.toBuilder().put(STATUS, Util.extractStatus(status)).build());
+          attributes.toBuilder().put(STATUS_KEY, Util.extractStatus(status)).build());
     }
   }
 
@@ -329,11 +329,11 @@ private void recordAttemptCompletion(@Nullable Throwable status) {
     Attributes attributes =
         baseAttributes
             .toBuilder()
-            .put(TABLE_ID, tableId)
-            .put(CLUSTER_ID, cluster)
-            .put(ZONE_ID, zone)
-            .put(METHOD, spanName.toString())
-            .put(CLIENT_NAME, NAME)
+            .put(TABLE_ID_KEY, tableId)
+            .put(CLUSTER_ID_KEY, cluster)
+            .put(ZONE_ID_KEY, zone)
+            .put(METHOD_KEY, spanName.toString())
+            .put(CLIENT_NAME_KEY, NAME)
             .build();
 
     clientBlockingLatenciesHistogram.record(totalClientBlockingTime.get(), attributes);
@@ -349,14 +349,14 @@ private void recordAttemptCompletion(@Nullable Throwable status) {
 
     attemptLatenciesHistogram.record(
         attemptTimer.elapsed(TimeUnit.MILLISECONDS),
-        attributes.toBuilder().put(STREAMING, isStreaming).put(STATUS, statusStr).build());
+        attributes.toBuilder().put(STREAMING_KEY, isStreaming).put(STATUS_KEY, statusStr).build());
 
     if (serverLatencies != null) {
       serverLatenciesHistogram.record(
-          serverLatencies, attributes.toBuilder().put(STATUS, statusStr).build());
-      connectivityErrorCounter.add(0, attributes.toBuilder().put(STATUS, statusStr).build());
+          serverLatencies, attributes.toBuilder().put(STATUS_KEY, statusStr).build());
+      connectivityErrorCounter.add(0, attributes.toBuilder().put(STATUS_KEY, statusStr).build());
     } else {
-      connectivityErrorCounter.add(1, attributes.toBuilder().put(STATUS, statusStr).build());
+      connectivityErrorCounter.add(1, attributes.toBuilder().put(STATUS_KEY, statusStr).build());
     }
   }
 }
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java
index 8e33f74d24..8c7c552841 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java
@@ -26,12 +26,14 @@
 import java.util.Map;
 
 /**
- * Register built-in metrics on a custom OpenTelemetry instance. This is for advanced usage, and is
- * only necessary when wanting to write built-in metrics to cloud monitoring and custom sinks.
- * Please refer to {@link CustomOpenTelemetryMetricsProvider} for example usage.
+ * A util class to register built-in metrics on a custom OpenTelemetry instance. This is for
+ * advanced usage, and is only necessary when wanting to write built-in metrics to cloud monitoring
+ * and custom sinks. Please refer to {@link CustomOpenTelemetryMetricsProvider} for example usage.
  */
 public class BuiltinMetricsView {
 
+  private BuiltinMetricsView() {}
+
   /**
    * Register built-in metrics on the {@link SdkMeterProviderBuilder} with application default
    * credentials.
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java
index 5f483bbe46..0dd21d31e8 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java
@@ -35,7 +35,8 @@
  * // create the OTEL instance
  * OpenTelemetry openTelemetry = OpenTelemetrySdk
  *     .builder()
- *     .setMeterProvider(sdkMeterProvider().build());
+ *     .setMeterProvider(sdkMeterProvider.build())
+ *     .build();
  *
  * // Override MetricsProvider in BigtableDataSettings
  * BigtableDataSettings settings = BigtableDataSettings.newBuilder()
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java
index 8a97beea93..1ef694e1e8 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java
@@ -21,7 +21,7 @@
  * to {@link this#INSTANCE} to disable collecting and export client side metrics
  * https://cloud.google.com/bigtable/docs/client-side-metrics.
  */
-public class NoopMetricsProvider implements MetricsProvider {
+public final class NoopMetricsProvider implements MetricsProvider {
 
   public static NoopMetricsProvider INSTANCE = new NoopMetricsProvider();
 
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java
index 4d615080a5..8211fd7639 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java
@@ -185,20 +185,21 @@ public void testBuiltinMetricsWithDefaultOTEL() throws Exception {
             CreateTableRequest.of(PrefixGenerator.newPrefix("BuiltinMetricsIT#test1"))
                 .addFamily("cf"));
     logger.info("Create default table: " + tableDefault.getId());
+
+    // Send a MutateRow and ReadRows request and measure the latencies for these requests.
     clientDefault.mutateRow(
         RowMutation.create(tableDefault.getId(), "a-new-key").setCell("cf", "q", "abc"));
     ArrayList rows =
         Lists.newArrayList(clientDefault.readRows(Query.create(tableDefault.getId()).limit(10)));
 
-    Stopwatch stopwatch = Stopwatch.createStarted();
+    // This stopwatch is used for to limit fetching of metric data in verifyMetrics
+    Stopwatch metricsPollingStopwatch = Stopwatch.createStarted();
 
     ProjectName name = ProjectName.of(testEnvRule.env().getProjectId());
 
-    Collection fromMetricReader = metricReader.collectAllMetrics();
-
-    // Restrict time to last 10 minutes and 5 minutes after the request
-    long startMillis = System.currentTimeMillis() - Duration.ofMinutes(10).toMillis();
-    long endMillis = startMillis + Duration.ofMinutes(15).toMillis();
+    // Restrict time to last minutes and 5 minutes after the request
+    long startMillis = System.currentTimeMillis() - Duration.ofMinutes(1).toMillis();
+    long endMillis = startMillis + Duration.ofMinutes(6).toMillis();
     TimeInterval interval =
         TimeInterval.newBuilder()
             .setStartTime(Timestamps.fromMillis(startMillis))
@@ -220,7 +221,7 @@ public void testBuiltinMetricsWithDefaultOTEL() throws Exception {
               .setFilter(metricFilter)
               .setInterval(interval)
               .setView(ListTimeSeriesRequest.TimeSeriesView.FULL);
-      verifyMetrics(requestBuilder.build(), stopwatch, view, null);
+      verifyMetrics(requestBuilder.build(), metricsPollingStopwatch, view, null);
 
       // Verify that metrics are published for ReadRows request
       metricFilter =
@@ -231,7 +232,7 @@ public void testBuiltinMetricsWithDefaultOTEL() throws Exception {
               view, testEnvRule.env().getInstanceId(), tableDefault.getId(), appProfileDefault);
       requestBuilder.setFilter(metricFilter);
 
-      verifyMetrics(requestBuilder.build(), stopwatch, view, null);
+      verifyMetrics(requestBuilder.build(), metricsPollingStopwatch, view, null);
     }
   }
 
@@ -243,22 +244,24 @@ public void testBuiltinMetricsWithCustomOTEL() throws Exception {
             CreateTableRequest.of(PrefixGenerator.newPrefix("BuiltinMetricsIT#test2"))
                 .addFamily("cf"));
     logger.info("Create custom table: " + tableCustomOtel.getId());
-    // Send a MutateRow and ReadRows request
+
+    // Send a MutateRow and ReadRows request and measure the latencies for these requests.
     clientCustomOtel.mutateRow(
         RowMutation.create(tableCustomOtel.getId(), "a-new-key").setCell("cf", "q", "abc"));
     ArrayList rows =
         Lists.newArrayList(
             clientCustomOtel.readRows(Query.create(tableCustomOtel.getId()).limit(10)));
 
-    Stopwatch stopwatch = Stopwatch.createStarted();
+    // This stopwatch is used for to limit fetching of metric data in verifyMetrics
+    Stopwatch metricsPollingStopwatch = Stopwatch.createStarted();
 
     ProjectName name = ProjectName.of(testEnvRule.env().getProjectId());
 
     Collection fromMetricReader = metricReader.collectAllMetrics();
 
     // Restrict time to last 10 minutes and 5 minutes after the request
-    long startMillis = System.currentTimeMillis() - Duration.ofMinutes(10).toMillis();
-    long endMillis = startMillis + Duration.ofMinutes(15).toMillis();
+    long startMillis = System.currentTimeMillis() - Duration.ofMinutes(1).toMillis();
+    long endMillis = startMillis + Duration.ofMinutes(6).toMillis();
     TimeInterval interval =
         TimeInterval.newBuilder()
             .setStartTime(Timestamps.fromMillis(startMillis))
@@ -290,7 +293,7 @@ public void testBuiltinMetricsWithCustomOTEL() throws Exception {
               .setInterval(interval)
               .setView(ListTimeSeriesRequest.TimeSeriesView.FULL);
 
-      verifyMetrics(requestBuilder.build(), stopwatch, view, dataFromReader);
+      verifyMetrics(requestBuilder.build(), metricsPollingStopwatch, view, dataFromReader);
 
       // Verify that metrics are correct for ReadRows request
       metricFilter =
@@ -304,12 +307,15 @@ public void testBuiltinMetricsWithCustomOTEL() throws Exception {
               appProfileCustomOtel);
       requestBuilder.setFilter(metricFilter);
 
-      verifyMetrics(requestBuilder.build(), stopwatch, view, dataFromReader);
+      verifyMetrics(requestBuilder.build(), metricsPollingStopwatch, view, dataFromReader);
     }
   }
 
   private void verifyMetrics(
-      ListTimeSeriesRequest request, Stopwatch stopwatch, String view, MetricData dataFromReader)
+      ListTimeSeriesRequest request,
+      Stopwatch metricsPollingStopwatch,
+      String view,
+      MetricData dataFromReader)
       throws Exception {
     ListTimeSeriesResponse response = metricClient.listTimeSeriesCallable().call(request);
     logger.log(
@@ -319,8 +325,9 @@ private void verifyMetrics(
             + ", has timeseries="
             + response.getTimeSeriesCount()
             + " stopwatch elapsed "
-            + stopwatch.elapsed(TimeUnit.MINUTES));
-    while (response.getTimeSeriesCount() == 0 && stopwatch.elapsed(TimeUnit.MINUTES) < 10) {
+            + metricsPollingStopwatch.elapsed(TimeUnit.MINUTES));
+    while (response.getTimeSeriesCount() == 0
+        && metricsPollingStopwatch.elapsed(TimeUnit.MINUTES) < 10) {
       // Call listTimeSeries every minute
       Thread.sleep(Duration.ofMinutes(1).toMillis());
       response = metricClient.listTimeSeriesCallable().call(request);
@@ -339,7 +346,7 @@ private void verifyMetrics(
                 .putAll(ts.getMetric().getLabelsMap())
                 .build();
         AttributesBuilder attributesBuilder = Attributes.builder();
-        String streamingKey = BuiltinMetricsConstants.STREAMING.getKey();
+        String streamingKey = BuiltinMetricsConstants.STREAMING_KEY.getKey();
         attributesMap.forEach(
             (k, v) -> {
               if (!k.equals(streamingKey)) {
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java
index 0ce6b25736..d898c882ac 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/StreamingMetricsMetadataIT.java
@@ -110,11 +110,11 @@ public void testSuccess() throws Exception {
     List pointData = new ArrayList<>(metricData.getData().getPoints());
     List clusterAttributes =
         pointData.stream()
-            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID))
+            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID_KEY))
             .collect(Collectors.toList());
     List zoneAttributes =
         pointData.stream()
-            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID))
+            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID_KEY))
             .collect(Collectors.toList());
 
     assertThat(clusterAttributes).contains(clusters.get(0).getId());
@@ -140,11 +140,11 @@ public void testFailure() {
     List pointData = new ArrayList<>(metricData.getData().getPoints());
     List clusterAttributes =
         pointData.stream()
-            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID))
+            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID_KEY))
             .collect(Collectors.toList());
     List zoneAttributes =
         pointData.stream()
-            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID))
+            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID_KEY))
             .collect(Collectors.toList());
 
     assertThat(clusterAttributes).contains("unspecified");
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java
index 0428748128..8efd2372b3 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/UnaryMetricsMetadataIT.java
@@ -115,11 +115,11 @@ public void testSuccess() throws Exception {
     List pointData = new ArrayList<>(metricData.getData().getPoints());
     List clusterAttributes =
         pointData.stream()
-            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID))
+            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID_KEY))
             .collect(Collectors.toList());
     List zoneAttributes =
         pointData.stream()
-            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID))
+            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID_KEY))
             .collect(Collectors.toList());
 
     assertThat(clusterAttributes).contains(clusters.get(0).getId());
@@ -158,11 +158,11 @@ public void testFailure() throws Exception {
     List pointData = new ArrayList<>(metricData.getData().getPoints());
     List clusterAttributes =
         pointData.stream()
-            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID))
+            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.CLUSTER_ID_KEY))
             .collect(Collectors.toList());
     List zoneAttributes =
         pointData.stream()
-            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID))
+            .map(pd -> pd.getAttributes().get(BuiltinMetricsConstants.ZONE_ID_KEY))
             .collect(Collectors.toList());
 
     assertThat(clusterAttributes).contains("unspecified");
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java
index 46e88b2dd9..057ccdc353 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java
@@ -15,13 +15,13 @@
  */
 package com.google.cloud.bigtable.data.v2.stub.metrics;
 
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APP_PROFILE;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_UID;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PROJECT_ID;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APP_PROFILE_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_UID_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.INSTANCE_ID_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.PROJECT_ID_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID_KEY;
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -93,12 +93,12 @@ public void setUp() {
 
     attributes =
         Attributes.builder()
-            .put(PROJECT_ID, projectId)
-            .put(INSTANCE_ID, instanceId)
-            .put(TABLE_ID, tableId)
-            .put(CLUSTER_ID, cluster)
-            .put(ZONE_ID, zone)
-            .put(APP_PROFILE, appProfileId)
+            .put(PROJECT_ID_KEY, projectId)
+            .put(INSTANCE_ID_KEY, instanceId)
+            .put(TABLE_ID_KEY, tableId)
+            .put(CLUSTER_ID_KEY, cluster)
+            .put(ZONE_ID_KEY, zone)
+            .put(APP_PROFILE_KEY, appProfileId)
             .build();
 
     resource = Resource.create(Attributes.empty());
@@ -146,16 +146,17 @@ public void testExportingSumData() {
 
     assertThat(timeSeries.getResource().getLabelsMap())
         .containsExactly(
-            PROJECT_ID.getKey(), projectId,
-            INSTANCE_ID.getKey(), instanceId,
-            TABLE_ID.getKey(), tableId,
-            CLUSTER_ID.getKey(), cluster,
-            ZONE_ID.getKey(), zone);
+            PROJECT_ID_KEY.getKey(), projectId,
+            INSTANCE_ID_KEY.getKey(), instanceId,
+            TABLE_ID_KEY.getKey(), tableId,
+            CLUSTER_ID_KEY.getKey(), cluster,
+            ZONE_ID_KEY.getKey(), zone);
 
     assertThat(timeSeries.getMetric().getLabelsMap()).hasSize(2);
     assertThat(timeSeries.getMetric().getLabelsMap())
-        .containsAtLeast(APP_PROFILE.getKey(), appProfileId);
-    assertThat(timeSeries.getMetric().getLabelsMap()).containsAtLeast(CLIENT_UID.getKey(), taskId);
+        .containsAtLeast(APP_PROFILE_KEY.getKey(), appProfileId);
+    assertThat(timeSeries.getMetric().getLabelsMap())
+        .containsAtLeast(CLIENT_UID_KEY.getKey(), taskId);
     assertThat(timeSeries.getPoints(0).getValue().getInt64Value()).isEqualTo(fakeValue);
     assertThat(timeSeries.getPoints(0).getInterval().getStartTime().getNanos())
         .isEqualTo(startEpoch);
@@ -207,16 +208,17 @@ public void testExportingHistogramData() {
 
     assertThat(timeSeries.getResource().getLabelsMap())
         .containsExactly(
-            PROJECT_ID.getKey(), projectId,
-            INSTANCE_ID.getKey(), instanceId,
-            TABLE_ID.getKey(), tableId,
-            CLUSTER_ID.getKey(), cluster,
-            ZONE_ID.getKey(), zone);
+            PROJECT_ID_KEY.getKey(), projectId,
+            INSTANCE_ID_KEY.getKey(), instanceId,
+            TABLE_ID_KEY.getKey(), tableId,
+            CLUSTER_ID_KEY.getKey(), cluster,
+            ZONE_ID_KEY.getKey(), zone);
 
     assertThat(timeSeries.getMetric().getLabelsMap()).hasSize(2);
     assertThat(timeSeries.getMetric().getLabelsMap())
-        .containsAtLeast(APP_PROFILE.getKey(), appProfileId);
-    assertThat(timeSeries.getMetric().getLabelsMap()).containsAtLeast(CLIENT_UID.getKey(), taskId);
+        .containsAtLeast(APP_PROFILE_KEY.getKey(), appProfileId);
+    assertThat(timeSeries.getMetric().getLabelsMap())
+        .containsAtLeast(CLIENT_UID_KEY.getKey(), taskId);
     Distribution distribution = timeSeries.getPoints(0).getValue().getDistributionValue();
     assertThat(distribution.getCount()).isEqualTo(3);
     assertThat(timeSeries.getPoints(0).getInterval().getStartTime().getNanos())
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java
index 3a8112a7f5..6184f5afb2 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java
@@ -17,6 +17,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import com.google.api.core.InternalApi;
 import com.google.protobuf.Timestamp;
 import com.google.protobuf.util.Timestamps;
 import io.opentelemetry.api.common.Attributes;
@@ -27,8 +28,11 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
+@InternalApi
 public class BuiltinMetricsTestUtils {
 
+  private BuiltinMetricsTestUtils() {}
+
   public static MetricData getMetricData(Collection allMetricData, String metricName) {
     List metricDataList =
         allMetricData.stream()
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java
index 4461872e0a..12e9978b8a 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java
@@ -18,18 +18,18 @@
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.APPLICATION_BLOCKING_LATENCIES_NAME;
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ATTEMPT_LATENCIES_NAME;
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_BLOCKING_LATENCIES_NAME;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID_KEY;
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_NAME;
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METER_NAME;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METHOD;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METHOD_KEY;
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_NAME;
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_NAME;
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.SERVER_LATENCIES_NAME;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STATUS;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STREAMING;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STATUS_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.STREAMING_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.TABLE_ID_KEY;
+import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.ZONE_ID_KEY;
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.getAggregatedValue;
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.getMetricData;
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.verifyAttributes;
@@ -148,9 +148,9 @@ public void setUp() throws Exception {
 
     baseAttributes =
         Attributes.builder()
-            .put(BuiltinMetricsConstants.PROJECT_ID, PROJECT_ID)
-            .put(BuiltinMetricsConstants.INSTANCE_ID, INSTANCE_ID)
-            .put(BuiltinMetricsConstants.APP_PROFILE, APP_PROFILE_ID)
+            .put(BuiltinMetricsConstants.PROJECT_ID_KEY, PROJECT_ID)
+            .put(BuiltinMetricsConstants.INSTANCE_ID_KEY, INSTANCE_ID)
+            .put(BuiltinMetricsConstants.APP_PROFILE_KEY, APP_PROFILE_ID)
             .build();
 
     SdkMeterProvider meterProvider =
@@ -283,13 +283,13 @@ public void testReadRowsOperationLatencies() {
     Attributes expectedAttributes =
         baseAttributes
             .toBuilder()
-            .put(STATUS, "OK")
-            .put(TABLE_ID, TABLE)
-            .put(ZONE_ID, ZONE)
-            .put(CLUSTER_ID, CLUSTER)
-            .put(METHOD, "Bigtable.ReadRows")
-            .put(STREAMING, true)
-            .put(CLIENT_NAME, "java-bigtable")
+            .put(STATUS_KEY, "OK")
+            .put(TABLE_ID_KEY, TABLE)
+            .put(ZONE_ID_KEY, ZONE)
+            .put(CLUSTER_ID_KEY, CLUSTER)
+            .put(METHOD_KEY, "Bigtable.ReadRows")
+            .put(STREAMING_KEY, true)
+            .put(CLIENT_NAME_KEY, "java-bigtable")
             .build();
 
     Collection allMetricData = metricReader.collectAllMetrics();
@@ -307,12 +307,12 @@ public void testGfeMetrics() {
     Attributes expectedAttributes =
         baseAttributes
             .toBuilder()
-            .put(STATUS, "OK")
-            .put(TABLE_ID, TABLE)
-            .put(ZONE_ID, ZONE)
-            .put(CLUSTER_ID, CLUSTER)
-            .put(CLIENT_NAME, "java-bigtable")
-            .put(METHOD, "Bigtable.ReadRows")
+            .put(STATUS_KEY, "OK")
+            .put(TABLE_ID_KEY, TABLE)
+            .put(ZONE_ID_KEY, ZONE)
+            .put(CLUSTER_ID_KEY, CLUSTER)
+            .put(CLIENT_NAME_KEY, "java-bigtable")
+            .put(METHOD_KEY, "Bigtable.ReadRows")
             .build();
 
     Collection allMetricData = metricReader.collectAllMetrics();
@@ -327,22 +327,22 @@ public void testGfeMetrics() {
     Attributes expected1 =
         baseAttributes
             .toBuilder()
-            .put(STATUS, "UNAVAILABLE")
-            .put(TABLE_ID, TABLE)
-            .put(ZONE_ID, "global")
-            .put(CLUSTER_ID, "unspecified")
-            .put(METHOD, "Bigtable.ReadRows")
-            .put(CLIENT_NAME, "java-bigtable")
+            .put(STATUS_KEY, "UNAVAILABLE")
+            .put(TABLE_ID_KEY, TABLE)
+            .put(ZONE_ID_KEY, "global")
+            .put(CLUSTER_ID_KEY, "unspecified")
+            .put(METHOD_KEY, "Bigtable.ReadRows")
+            .put(CLIENT_NAME_KEY, "java-bigtable")
             .build();
     Attributes expected2 =
         baseAttributes
             .toBuilder()
-            .put(STATUS, "OK")
-            .put(TABLE_ID, TABLE)
-            .put(ZONE_ID, ZONE)
-            .put(CLUSTER_ID, CLUSTER)
-            .put(METHOD, "Bigtable.ReadRows")
-            .put(CLIENT_NAME, "java-bigtable")
+            .put(STATUS_KEY, "OK")
+            .put(TABLE_ID_KEY, TABLE)
+            .put(ZONE_ID_KEY, ZONE)
+            .put(CLUSTER_ID_KEY, CLUSTER)
+            .put(METHOD_KEY, "Bigtable.ReadRows")
+            .put(CLIENT_NAME_KEY, "java-bigtable")
             .build();
 
     verifyAttributes(connectivityErrorCountMetricData, expected1);
@@ -394,11 +394,11 @@ public void onComplete() {
     Attributes expectedAttributes =
         baseAttributes
             .toBuilder()
-            .put(TABLE_ID, TABLE)
-            .put(ZONE_ID, ZONE)
-            .put(CLUSTER_ID, CLUSTER)
-            .put(CLIENT_NAME, "java-bigtable")
-            .put(METHOD, "Bigtable.ReadRows")
+            .put(TABLE_ID_KEY, TABLE)
+            .put(ZONE_ID_KEY, ZONE)
+            .put(CLUSTER_ID_KEY, CLUSTER)
+            .put(CLIENT_NAME_KEY, "java-bigtable")
+            .put(METHOD_KEY, "Bigtable.ReadRows")
             .build();
     long value = getAggregatedValue(applicationLatency, expectedAttributes);
 
@@ -408,7 +408,7 @@ public void onComplete() {
     long operationLatencyValue =
         getAggregatedValue(
             operationLatency,
-            expectedAttributes.toBuilder().put(STATUS, "OK").put(STREAMING, true).build());
+            expectedAttributes.toBuilder().put(STATUS_KEY, "OK").put(STREAMING_KEY, true).build());
     assertThat(value).isAtMost(operationLatencyValue - SERVER_LATENCY);
   }
 
@@ -431,11 +431,11 @@ public void testReadRowsApplicationLatencyWithManualFlowControl() throws Excepti
     Attributes expectedAttributes =
         baseAttributes
             .toBuilder()
-            .put(TABLE_ID, TABLE)
-            .put(ZONE_ID, ZONE)
-            .put(CLUSTER_ID, CLUSTER)
-            .put(CLIENT_NAME, "java-bigtable")
-            .put(METHOD, "Bigtable.ReadRows")
+            .put(TABLE_ID_KEY, TABLE)
+            .put(ZONE_ID_KEY, ZONE)
+            .put(CLUSTER_ID_KEY, CLUSTER)
+            .put(CLIENT_NAME_KEY, "java-bigtable")
+            .put(METHOD_KEY, "Bigtable.ReadRows")
             .build();
 
     long value = getAggregatedValue(applicationLatency, expectedAttributes);
@@ -448,7 +448,7 @@ public void testReadRowsApplicationLatencyWithManualFlowControl() throws Excepti
     long operationLatencyValue =
         getAggregatedValue(
             operationLatency,
-            expectedAttributes.toBuilder().put(STATUS, "OK").put(STREAMING, true).build());
+            expectedAttributes.toBuilder().put(STATUS_KEY, "OK").put(STREAMING_KEY, true).build());
     assertThat(value).isAtMost(operationLatencyValue - SERVER_LATENCY);
   }
 
@@ -462,12 +462,12 @@ public void testRetryCount() throws InterruptedException {
     Attributes expectedAttributes =
         baseAttributes
             .toBuilder()
-            .put(TABLE_ID, TABLE)
-            .put(ZONE_ID, ZONE)
-            .put(CLUSTER_ID, CLUSTER)
-            .put(CLIENT_NAME, "java-bigtable")
-            .put(METHOD, "Bigtable.MutateRow")
-            .put(STATUS, "OK")
+            .put(TABLE_ID_KEY, TABLE)
+            .put(ZONE_ID_KEY, ZONE)
+            .put(CLUSTER_ID_KEY, CLUSTER)
+            .put(CLIENT_NAME_KEY, "java-bigtable")
+            .put(METHOD_KEY, "Bigtable.MutateRow")
+            .put(STATUS_KEY, "OK")
             .build();
 
     long value = getAggregatedValue(metricData, expectedAttributes);
@@ -485,25 +485,25 @@ public void testMutateRowAttemptsTagValues() {
     Attributes expected1 =
         baseAttributes
             .toBuilder()
-            .put(STATUS, "UNAVAILABLE")
-            .put(TABLE_ID, TABLE)
-            .put(ZONE_ID, "global")
-            .put(CLUSTER_ID, "unspecified")
-            .put(METHOD, "Bigtable.MutateRow")
-            .put(CLIENT_NAME, "java-bigtable")
-            .put(STREAMING, false)
+            .put(STATUS_KEY, "UNAVAILABLE")
+            .put(TABLE_ID_KEY, TABLE)
+            .put(ZONE_ID_KEY, "global")
+            .put(CLUSTER_ID_KEY, "unspecified")
+            .put(METHOD_KEY, "Bigtable.MutateRow")
+            .put(CLIENT_NAME_KEY, "java-bigtable")
+            .put(STREAMING_KEY, false)
             .build();
 
     Attributes expected2 =
         baseAttributes
             .toBuilder()
-            .put(STATUS, "OK")
-            .put(TABLE_ID, TABLE)
-            .put(ZONE_ID, ZONE)
-            .put(CLUSTER_ID, CLUSTER)
-            .put(METHOD, "Bigtable.MutateRow")
-            .put(CLIENT_NAME, "java-bigtable")
-            .put(STREAMING, false)
+            .put(STATUS_KEY, "OK")
+            .put(TABLE_ID_KEY, TABLE)
+            .put(ZONE_ID_KEY, ZONE)
+            .put(CLUSTER_ID_KEY, CLUSTER)
+            .put(METHOD_KEY, "Bigtable.MutateRow")
+            .put(CLIENT_NAME_KEY, "java-bigtable")
+            .put(STREAMING_KEY, false)
             .build();
 
     verifyAttributes(metricData, expected1);
@@ -520,25 +520,25 @@ public void testReadRowsAttemptsTagValues() {
     Attributes expected1 =
         baseAttributes
             .toBuilder()
-            .put(STATUS, "UNAVAILABLE")
-            .put(TABLE_ID, TABLE)
-            .put(ZONE_ID, "global")
-            .put(CLUSTER_ID, "unspecified")
-            .put(METHOD, "Bigtable.ReadRows")
-            .put(CLIENT_NAME, "java-bigtable")
-            .put(STREAMING, true)
+            .put(STATUS_KEY, "UNAVAILABLE")
+            .put(TABLE_ID_KEY, TABLE)
+            .put(ZONE_ID_KEY, "global")
+            .put(CLUSTER_ID_KEY, "unspecified")
+            .put(METHOD_KEY, "Bigtable.ReadRows")
+            .put(CLIENT_NAME_KEY, "java-bigtable")
+            .put(STREAMING_KEY, true)
             .build();
 
     Attributes expected2 =
         baseAttributes
             .toBuilder()
-            .put(STATUS, "OK")
-            .put(TABLE_ID, TABLE)
-            .put(ZONE_ID, ZONE)
-            .put(CLUSTER_ID, CLUSTER)
-            .put(METHOD, "Bigtable.ReadRows")
-            .put(CLIENT_NAME, "java-bigtable")
-            .put(STREAMING, true)
+            .put(STATUS_KEY, "OK")
+            .put(TABLE_ID_KEY, TABLE)
+            .put(ZONE_ID_KEY, ZONE)
+            .put(CLUSTER_ID_KEY, CLUSTER)
+            .put(METHOD_KEY, "Bigtable.ReadRows")
+            .put(CLIENT_NAME_KEY, "java-bigtable")
+            .put(STREAMING_KEY, true)
             .build();
 
     verifyAttributes(metricData, expected1);
@@ -563,11 +563,11 @@ public void testBatchBlockingLatencies() throws InterruptedException {
       Attributes expectedAttributes =
           baseAttributes
               .toBuilder()
-              .put(TABLE_ID, TABLE)
-              .put(ZONE_ID, ZONE)
-              .put(CLUSTER_ID, CLUSTER)
-              .put(METHOD, "Bigtable.MutateRows")
-              .put(CLIENT_NAME, "java-bigtable")
+              .put(TABLE_ID_KEY, TABLE)
+              .put(ZONE_ID_KEY, ZONE)
+              .put(CLUSTER_ID_KEY, CLUSTER)
+              .put(METHOD_KEY, "Bigtable.MutateRows")
+              .put(CLIENT_NAME_KEY, "java-bigtable")
               .build();
 
       long value = getAggregatedValue(applicationLatency, expectedAttributes);
@@ -589,11 +589,11 @@ public void testQueuedOnChannelServerStreamLatencies() {
     Attributes attributes =
         baseAttributes
             .toBuilder()
-            .put(TABLE_ID, TABLE)
-            .put(CLUSTER_ID, CLUSTER)
-            .put(ZONE_ID, ZONE)
-            .put(METHOD, "Bigtable.ReadRows")
-            .put(CLIENT_NAME, "java-bigtable")
+            .put(TABLE_ID_KEY, TABLE)
+            .put(CLUSTER_ID_KEY, CLUSTER)
+            .put(ZONE_ID_KEY, ZONE)
+            .put(METHOD_KEY, "Bigtable.ReadRows")
+            .put(CLIENT_NAME_KEY, "java-bigtable")
             .build();
 
     long value = getAggregatedValue(clientLatency, attributes);
@@ -611,11 +611,11 @@ public void testQueuedOnChannelUnaryLatencies() {
     Attributes attributes =
         baseAttributes
             .toBuilder()
-            .put(TABLE_ID, TABLE)
-            .put(CLUSTER_ID, CLUSTER)
-            .put(ZONE_ID, ZONE)
-            .put(METHOD, "Bigtable.MutateRow")
-            .put(CLIENT_NAME, "java-bigtable")
+            .put(TABLE_ID_KEY, TABLE)
+            .put(CLUSTER_ID_KEY, CLUSTER)
+            .put(ZONE_ID_KEY, ZONE)
+            .put(METHOD_KEY, "Bigtable.MutateRow")
+            .put(CLIENT_NAME_KEY, "java-bigtable")
             .build();
 
     long expected = CHANNEL_BLOCKING_LATENCY * 2 / 3;
@@ -637,13 +637,13 @@ public void testPermanentFailure() {
     Attributes expected =
         baseAttributes
             .toBuilder()
-            .put(STATUS, "NOT_FOUND")
-            .put(TABLE_ID, BAD_TABLE_ID)
-            .put(CLUSTER_ID, "unspecified")
-            .put(ZONE_ID, "global")
-            .put(STREAMING, true)
-            .put(METHOD, "Bigtable.ReadRows")
-            .put(CLIENT_NAME, "java-bigtable")
+            .put(STATUS_KEY, "NOT_FOUND")
+            .put(TABLE_ID_KEY, BAD_TABLE_ID)
+            .put(CLUSTER_ID_KEY, "unspecified")
+            .put(ZONE_ID_KEY, "global")
+            .put(STREAMING_KEY, true)
+            .put(METHOD_KEY, "Bigtable.ReadRows")
+            .put(CLIENT_NAME_KEY, "java-bigtable")
             .build();
 
     verifyAttributes(attemptLatency, expected);

From 0f21a312eda235bc8529008158cf91c34660c598 Mon Sep 17 00:00:00 2001
From: Mattie Fu 
Date: Tue, 6 Feb 2024 15:31:47 -0500
Subject: [PATCH 29/34] update test

---
 .../bigtable/data/v2/it/BuiltinMetricsIT.java | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java
index 8211fd7639..e8637c2726 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java
@@ -80,6 +80,7 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 import org.threeten.bp.Duration;
+import org.threeten.bp.Instant;
 
 @RunWith(JUnit4.class)
 public class BuiltinMetricsIT {
@@ -198,12 +199,12 @@ public void testBuiltinMetricsWithDefaultOTEL() throws Exception {
     ProjectName name = ProjectName.of(testEnvRule.env().getProjectId());
 
     // Restrict time to last minutes and 5 minutes after the request
-    long startMillis = System.currentTimeMillis() - Duration.ofMinutes(1).toMillis();
-    long endMillis = startMillis + Duration.ofMinutes(6).toMillis();
+    Instant start = Instant.now().minus(Duration.ofMinutes(1));
+    Instant end = start.plus(Duration.ofMinutes(6));
     TimeInterval interval =
         TimeInterval.newBuilder()
-            .setStartTime(Timestamps.fromMillis(startMillis))
-            .setEndTime(Timestamps.fromMillis(endMillis))
+            .setStartTime(Timestamps.fromMillis(start.toEpochMilli()))
+            .setEndTime(Timestamps.fromMillis(end.toEpochMilli()))
             .build();
 
     for (String view : VIEWS) {
@@ -259,13 +260,13 @@ public void testBuiltinMetricsWithCustomOTEL() throws Exception {
 
     Collection fromMetricReader = metricReader.collectAllMetrics();
 
-    // Restrict time to last 10 minutes and 5 minutes after the request
-    long startMillis = System.currentTimeMillis() - Duration.ofMinutes(1).toMillis();
-    long endMillis = startMillis + Duration.ofMinutes(6).toMillis();
+    // Restrict time to last minutes and 5 minutes after the request
+    Instant start = Instant.now().minus(Duration.ofMinutes(1));
+    Instant end = start.plus(Duration.ofMinutes(6));
     TimeInterval interval =
         TimeInterval.newBuilder()
-            .setStartTime(Timestamps.fromMillis(startMillis))
-            .setEndTime(Timestamps.fromMillis(endMillis))
+            .setStartTime(Timestamps.fromMillis(start.toEpochMilli()))
+            .setEndTime(Timestamps.fromMillis(end.toEpochMilli()))
             .build();
 
     for (String view : VIEWS) {

From 56f6b71cc619220f9cced7a2ff15f177846ec2fc Mon Sep 17 00:00:00 2001
From: Mattie Fu 
Date: Wed, 7 Feb 2024 11:09:10 -0500
Subject: [PATCH 30/34] test with nano

---
 .../RateLimitingServerStreamingCallable.java   |  2 +-
 .../stub/metrics/BigtableGrpcStreamTracer.java |  2 +-
 .../v2/stub/metrics/BuiltinMetricsTracer.java  | 18 +++++++++---------
 .../metrics/TracedBatcherUnaryCallable.java    |  4 +++-
 4 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RateLimitingServerStreamingCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RateLimitingServerStreamingCallable.java
index 6208fce89e..97cc2f73ec 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RateLimitingServerStreamingCallable.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/RateLimitingServerStreamingCallable.java
@@ -86,7 +86,7 @@ public void call(
     stopwatch.stop();
     if (context.getTracer() instanceof BigtableTracer) {
       ((BigtableTracer) context.getTracer())
-          .batchRequestThrottled(stopwatch.elapsed(TimeUnit.MILLISECONDS));
+          .batchRequestThrottled(stopwatch.elapsed(TimeUnit.NANOSECONDS));
     }
     RateLimitingResponseObserver innerObserver =
         new RateLimitingResponseObserver(limiter, lastQpsChangeTime, responseObserver);
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableGrpcStreamTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableGrpcStreamTracer.java
index 1cda49934c..3b2242385a 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableGrpcStreamTracer.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableGrpcStreamTracer.java
@@ -42,7 +42,7 @@ public void streamCreated(Attributes transportAttrs, Metadata headers) {
 
   @Override
   public void outboundMessageSent(int seqNo, long optionalWireSize, long optionalUncompressedSize) {
-    tracer.grpcChannelQueuedLatencies(stopwatch.elapsed(TimeUnit.MILLISECONDS));
+    tracer.grpcChannelQueuedLatencies(stopwatch.elapsed(TimeUnit.NANOSECONDS));
   }
 
   static class Factory extends ClientStreamTracer.Factory {
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
index c8c7485b36..c5312c4699 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
@@ -253,13 +253,13 @@ public void setLocations(String zone, String cluster) {
   }
 
   @Override
-  public void batchRequestThrottled(long throttledTimeMs) {
-    totalClientBlockingTime.addAndGet(throttledTimeMs);
+  public void batchRequestThrottled(long throttledTimeNanos) {
+    totalClientBlockingTime.addAndGet(Duration.ofNanos(throttledTimeNanos).toMillis());
   }
 
   @Override
-  public void grpcChannelQueuedLatencies(long queuedTimeMs) {
-    totalClientBlockingTime.addAndGet(queuedTimeMs);
+  public void grpcChannelQueuedLatencies(long queuedTimeNanos) {
+    totalClientBlockingTime.addAndGet(queuedTimeNanos);
   }
 
   @Override
@@ -286,7 +286,6 @@ private void recordOperationCompletion(@Nullable Throwable status) {
             .put(CLIENT_NAME_KEY, NAME)
             .build();
 
-    long operationLatency = operationTimer.elapsed(TimeUnit.MILLISECONDS);
     long operationLatencyNano = operationTimer.elapsed(TimeUnit.NANOSECONDS);
 
     // Only record when retry count is greater than 0 so the retry
@@ -297,7 +296,7 @@ private void recordOperationCompletion(@Nullable Throwable status) {
 
     // serverLatencyTimer should already be stopped in recordAttemptCompletion
     operationLatenciesHistogram.record(
-        operationLatency,
+        Duration.ofNanos(operationLatencyNano).toMillis(),
         attributes.toBuilder().put(STREAMING_KEY, isStreaming).put(STATUS_KEY, statusStr).build());
 
     long applicationLatencyNano = operationLatencyNano - totalServerLatencyNano.get();
@@ -307,7 +306,7 @@ private void recordOperationCompletion(@Nullable Throwable status) {
     if (operationType == OperationType.ServerStreaming
         && spanName.getMethodName().equals("ReadRows")) {
       firstResponseLatenciesHistogram.record(
-          firstResponsePerOpTimer.elapsed(TimeUnit.MILLISECONDS),
+          Duration.ofNanos(firstResponsePerOpTimer.elapsed(TimeUnit.NANOSECONDS)).toMillis(),
           attributes.toBuilder().put(STATUS_KEY, Util.extractStatus(status)).build());
     }
   }
@@ -336,7 +335,8 @@ private void recordAttemptCompletion(@Nullable Throwable status) {
             .put(CLIENT_NAME_KEY, NAME)
             .build();
 
-    clientBlockingLatenciesHistogram.record(totalClientBlockingTime.get(), attributes);
+    clientBlockingLatenciesHistogram.record(
+        Duration.ofNanos(totalClientBlockingTime.get()).toMillis(), attributes);
 
     // Patch the status until it's fixed in gax. When an attempt failed,
     // it'll throw a ServerStreamingAttemptException. Unwrap the exception
@@ -348,7 +348,7 @@ private void recordAttemptCompletion(@Nullable Throwable status) {
     String statusStr = Util.extractStatus(status);
 
     attemptLatenciesHistogram.record(
-        attemptTimer.elapsed(TimeUnit.MILLISECONDS),
+        Duration.ofNanos(attemptTimer.elapsed(TimeUnit.NANOSECONDS)).toMillis(),
         attributes.toBuilder().put(STREAMING_KEY, isStreaming).put(STATUS_KEY, statusStr).build());
 
     if (serverLatencies != null) {
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/TracedBatcherUnaryCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/TracedBatcherUnaryCallable.java
index b7140f0156..ce73d75dc1 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/TracedBatcherUnaryCallable.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/TracedBatcherUnaryCallable.java
@@ -21,6 +21,7 @@
 import com.google.api.gax.rpc.ApiCallContext;
 import com.google.api.gax.rpc.UnaryCallable;
 import com.google.api.gax.tracing.ApiTracer;
+import org.threeten.bp.Duration;
 
 /**
  * This callable will extract total throttled time from {@link ApiCallContext} and add it to {@link
@@ -42,7 +43,8 @@ public ApiFuture futureCall(RequestT request, ApiCallContext context)
       // this should always be true
       if (tracer instanceof BigtableTracer) {
         ((BigtableTracer) tracer)
-            .batchRequestThrottled(context.getOption(Batcher.THROTTLED_TIME_KEY));
+            .batchRequestThrottled(
+                Duration.ofMillis(context.getOption(Batcher.THROTTLED_TIME_KEY)).toNanos());
       }
     }
     return innerCallable.futureCall(request, context);

From 14f22a8524cc1f3bcc44e7495d0e6b1ac9b2485c Mon Sep 17 00:00:00 2001
From: Mattie Fu 
Date: Thu, 8 Feb 2024 15:11:34 -0500
Subject: [PATCH 31/34] measure everything in nanos and publish with double
 histogram

---
 .../stub/metrics/BuiltinMetricsConstants.java |  49 +++---
 .../v2/stub/metrics/BuiltinMetricsTracer.java |  42 ++---
 .../metrics/BuiltinMetricsTracerFactory.java  |  20 +--
 .../bigtable/data/v2/it/BuiltinMetricsIT.java | 152 ++++++++++--------
 4 files changed, 138 insertions(+), 125 deletions(-)

diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java
index f24c806ba7..3b307166aa 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java
@@ -26,6 +26,7 @@
 import io.opentelemetry.sdk.metrics.View;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /** Defining Bigtable builit-in metrics scope, attributes, metric names and views. */
 @InternalApi
@@ -39,6 +40,8 @@ public class BuiltinMetricsConstants {
   public static final AttributeKey ZONE_ID_KEY = AttributeKey.stringKey("zone");
 
   // Metric attribute keys for labels
+  // We need to access APP_PROFILE_KEY in EnhancedBigtableStubSettings and STREAMING_KEY in
+  // IT tests, so they're public.
   public static final AttributeKey APP_PROFILE_KEY = AttributeKey.stringKey("app_profile");
   public static final AttributeKey STREAMING_KEY = AttributeKey.booleanKey("streaming");
   static final AttributeKey METHOD_KEY = AttributeKey.stringKey("method");
@@ -61,23 +64,23 @@ public class BuiltinMetricsConstants {
   private static final Aggregation AGGREGATION_WITH_MILLIS_HISTOGRAM =
       Aggregation.explicitBucketHistogram(
           ImmutableList.of(
-              0.0, 0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0,
-              16.0, 20.0, 25.0, 30.0, 40.0, 50.0, 65.0, 80.0, 100.0, 130.0, 160.0, 200.0, 250.0,
-              300.0, 400.0, 500.0, 650.0, 800.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0, 50000.0,
-              100000.0, 200000.0, 400000.0, 800000.0, 1600000.0, 3200000.0)); // max is 53.3 minutes
+              0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0, 16.0, 20.0, 25.0, 30.0, 40.0,
+              50.0, 65.0, 80.0, 100.0, 130.0, 160.0, 200.0, 250.0, 300.0, 400.0, 500.0, 650.0,
+              800.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0, 50000.0, 100000.0, 200000.0,
+              400000.0, 800000.0, 1600000.0, 3200000.0)); // max is 53.3 minutes
 
   public static final String METER_NAME = "bigtable.googleapis.com/internal/client/";
 
-  static final Set COMMON_ATTRIBUTES =
+  static final Set COMMON_ATTRIBUTES =
       ImmutableSet.of(
-          PROJECT_ID_KEY.getKey(),
-          INSTANCE_ID_KEY.getKey(),
-          TABLE_ID_KEY.getKey(),
-          APP_PROFILE_KEY.getKey(),
-          CLUSTER_ID_KEY.getKey(),
-          ZONE_ID_KEY.getKey(),
-          METHOD_KEY.getKey(),
-          CLIENT_NAME_KEY.getKey());
+          PROJECT_ID_KEY,
+          INSTANCE_ID_KEY,
+          TABLE_ID_KEY,
+          APP_PROFILE_KEY,
+          CLUSTER_ID_KEY,
+          ZONE_ID_KEY,
+          METHOD_KEY,
+          CLIENT_NAME_KEY);
 
   static void defineView(
       ImmutableMap.Builder viewMap,
@@ -85,7 +88,7 @@ static void defineView(
       Aggregation aggregation,
       InstrumentType type,
       String unit,
-      Set extraAttributes) {
+      Set extraAttributes) {
     InstrumentSelector selector =
         InstrumentSelector.builder()
             .setName(id)
@@ -94,7 +97,11 @@ static void defineView(
             .setUnit(unit)
             .build();
     Set attributesFilter =
-        ImmutableSet.builder().addAll(COMMON_ATTRIBUTES).addAll(extraAttributes).build();
+        ImmutableSet.builder()
+            .addAll(
+                COMMON_ATTRIBUTES.stream().map(AttributeKey::getKey).collect(Collectors.toSet()))
+            .addAll(extraAttributes.stream().map(AttributeKey::getKey).collect(Collectors.toSet()))
+            .build();
     View view =
         View.builder()
             .setName(METER_NAME + id)
@@ -114,28 +121,28 @@ public static Map getAllViews() {
         AGGREGATION_WITH_MILLIS_HISTOGRAM,
         InstrumentType.HISTOGRAM,
         "ms",
-        ImmutableSet.of(STREAMING_KEY.getKey(), STATUS_KEY.getKey()));
+        ImmutableSet.of(STREAMING_KEY, STATUS_KEY));
     defineView(
         views,
         ATTEMPT_LATENCIES_NAME,
         AGGREGATION_WITH_MILLIS_HISTOGRAM,
         InstrumentType.HISTOGRAM,
         "ms",
-        ImmutableSet.of(STREAMING_KEY.getKey(), STATUS_KEY.getKey()));
+        ImmutableSet.of(STREAMING_KEY, STATUS_KEY));
     defineView(
         views,
         SERVER_LATENCIES_NAME,
         AGGREGATION_WITH_MILLIS_HISTOGRAM,
         InstrumentType.HISTOGRAM,
         "ms",
-        ImmutableSet.of(STREAMING_KEY.getKey(), STATUS_KEY.getKey()));
+        ImmutableSet.of(STREAMING_KEY, STATUS_KEY));
     defineView(
         views,
         FIRST_RESPONSE_LATENCIES_NAME,
         AGGREGATION_WITH_MILLIS_HISTOGRAM,
         InstrumentType.HISTOGRAM,
         "ms",
-        ImmutableSet.of(STATUS_KEY.getKey()));
+        ImmutableSet.of(STATUS_KEY));
     defineView(
         views,
         APPLICATION_BLOCKING_LATENCIES_NAME,
@@ -156,14 +163,14 @@ public static Map getAllViews() {
         Aggregation.sum(),
         InstrumentType.COUNTER,
         "1",
-        ImmutableSet.of(STATUS_KEY.getKey()));
+        ImmutableSet.of(STATUS_KEY));
     defineView(
         views,
         CONNECTIVITY_ERROR_COUNT_NAME,
         Aggregation.sum(),
         InstrumentType.COUNTER,
         "1",
-        ImmutableSet.of(STATUS_KEY.getKey()));
+        ImmutableSet.of(STATUS_KEY));
 
     return views.build();
   }
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
index c5312c4699..844f159c31 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
@@ -29,8 +29,8 @@
 import com.google.common.base.Stopwatch;
 import com.google.common.math.IntMath;
 import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.metrics.DoubleHistogram;
 import io.opentelemetry.api.metrics.LongCounter;
-import io.opentelemetry.api.metrics.LongHistogram;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -71,25 +71,27 @@ class BuiltinMetricsTracer extends BigtableTracer {
 
   private boolean flowControlIsDisabled = false;
 
-  private AtomicInteger requestLeft = new AtomicInteger(0);
+  private final AtomicInteger requestLeft = new AtomicInteger(0);
 
   // Monitored resource labels
   private String tableId = "unspecified";
   private String zone = "global";
   private String cluster = "unspecified";
 
-  private AtomicLong totalClientBlockingTime = new AtomicLong(0);
+  private final AtomicLong totalClientBlockingTime = new AtomicLong(0);
 
   private final Attributes baseAttributes;
 
   private Long serverLatencies = null;
 
-  private final LongHistogram operationLatenciesHistogram;
-  private final LongHistogram attemptLatenciesHistogram;
-  private final LongHistogram serverLatenciesHistogram;
-  private final LongHistogram firstResponseLatenciesHistogram;
-  private final LongHistogram clientBlockingLatenciesHistogram;
-  private final LongHistogram applicationBlockingLatenciesHistogram;
+  private final double toMs = 1e-6;
+
+  private final DoubleHistogram operationLatenciesHistogram;
+  private final DoubleHistogram attemptLatenciesHistogram;
+  private final DoubleHistogram serverLatenciesHistogram;
+  private final DoubleHistogram firstResponseLatenciesHistogram;
+  private final DoubleHistogram clientBlockingLatenciesHistogram;
+  private final DoubleHistogram applicationBlockingLatenciesHistogram;
   private final LongCounter connectivityErrorCounter;
   private final LongCounter retryCounter;
 
@@ -97,12 +99,12 @@ class BuiltinMetricsTracer extends BigtableTracer {
       OperationType operationType,
       SpanName spanName,
       Attributes attributes,
-      LongHistogram operationLatenciesHistogram,
-      LongHistogram attemptLatenciesHistogram,
-      LongHistogram serverLatenciesHistogram,
-      LongHistogram firstResponseLatenciesHistogram,
-      LongHistogram clientBlockingLatenciesHistogram,
-      LongHistogram applicationBlockingLatenciesHistogram,
+      DoubleHistogram operationLatenciesHistogram,
+      DoubleHistogram attemptLatenciesHistogram,
+      DoubleHistogram serverLatenciesHistogram,
+      DoubleHistogram firstResponseLatenciesHistogram,
+      DoubleHistogram clientBlockingLatenciesHistogram,
+      DoubleHistogram applicationBlockingLatenciesHistogram,
       LongCounter connectivityErrorCounter,
       LongCounter retryCounter) {
     this.operationType = operationType;
@@ -300,13 +302,12 @@ private void recordOperationCompletion(@Nullable Throwable status) {
         attributes.toBuilder().put(STREAMING_KEY, isStreaming).put(STATUS_KEY, statusStr).build());
 
     long applicationLatencyNano = operationLatencyNano - totalServerLatencyNano.get();
-    applicationBlockingLatenciesHistogram.record(
-        Duration.ofNanos(applicationLatencyNano).toMillis(), attributes);
+    applicationBlockingLatenciesHistogram.record(applicationLatencyNano * toMs, attributes);
 
     if (operationType == OperationType.ServerStreaming
         && spanName.getMethodName().equals("ReadRows")) {
       firstResponseLatenciesHistogram.record(
-          Duration.ofNanos(firstResponsePerOpTimer.elapsed(TimeUnit.NANOSECONDS)).toMillis(),
+          firstResponsePerOpTimer.elapsed(TimeUnit.NANOSECONDS) * toMs,
           attributes.toBuilder().put(STATUS_KEY, Util.extractStatus(status)).build());
     }
   }
@@ -335,8 +336,7 @@ private void recordAttemptCompletion(@Nullable Throwable status) {
             .put(CLIENT_NAME_KEY, NAME)
             .build();
 
-    clientBlockingLatenciesHistogram.record(
-        Duration.ofNanos(totalClientBlockingTime.get()).toMillis(), attributes);
+    clientBlockingLatenciesHistogram.record(totalClientBlockingTime.get() * toMs, attributes);
 
     // Patch the status until it's fixed in gax. When an attempt failed,
     // it'll throw a ServerStreamingAttemptException. Unwrap the exception
@@ -348,7 +348,7 @@ private void recordAttemptCompletion(@Nullable Throwable status) {
     String statusStr = Util.extractStatus(status);
 
     attemptLatenciesHistogram.record(
-        Duration.ofNanos(attemptTimer.elapsed(TimeUnit.NANOSECONDS)).toMillis(),
+        attemptTimer.elapsed(TimeUnit.NANOSECONDS) * toMs,
         attributes.toBuilder().put(STREAMING_KEY, isStreaming).put(STATUS_KEY, statusStr).build());
 
     if (serverLatencies != null) {
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java
index 575907ffde..f0ac656978 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerFactory.java
@@ -32,8 +32,8 @@
 import com.google.api.gax.tracing.SpanName;
 import io.opentelemetry.api.OpenTelemetry;
 import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.metrics.DoubleHistogram;
 import io.opentelemetry.api.metrics.LongCounter;
-import io.opentelemetry.api.metrics.LongHistogram;
 import io.opentelemetry.api.metrics.Meter;
 import java.io.IOException;
 
@@ -49,12 +49,12 @@ public class BuiltinMetricsTracerFactory extends BaseApiTracerFactory {
   private static final String MILLISECOND = "ms";
   private static final String COUNT = "1";
 
-  private final LongHistogram operationLatenciesHistogram;
-  private final LongHistogram attemptLatenciesHistogram;
-  private final LongHistogram serverLatenciesHistogram;
-  private final LongHistogram firstResponseLatenciesHistogram;
-  private final LongHistogram clientBlockingLatenciesHistogram;
-  private final LongHistogram applicationBlockingLatenciesHistogram;
+  private final DoubleHistogram operationLatenciesHistogram;
+  private final DoubleHistogram attemptLatenciesHistogram;
+  private final DoubleHistogram serverLatenciesHistogram;
+  private final DoubleHistogram firstResponseLatenciesHistogram;
+  private final DoubleHistogram clientBlockingLatenciesHistogram;
+  private final DoubleHistogram applicationBlockingLatenciesHistogram;
   private final LongCounter connectivityErrorCounter;
   private final LongCounter retryCounter;
 
@@ -70,7 +70,6 @@ public static BuiltinMetricsTracerFactory create(
     operationLatenciesHistogram =
         meter
             .histogramBuilder(OPERATION_LATENCIES_NAME)
-            .ofLongs()
             .setDescription(
                 "Total time until final operation success or failure, including retries and backoff.")
             .setUnit(MILLISECOND)
@@ -78,14 +77,12 @@ public static BuiltinMetricsTracerFactory create(
     attemptLatenciesHistogram =
         meter
             .histogramBuilder(ATTEMPT_LATENCIES_NAME)
-            .ofLongs()
             .setDescription("Client observed latency per RPC attempt.")
             .setUnit(MILLISECOND)
             .build();
     serverLatenciesHistogram =
         meter
             .histogramBuilder(SERVER_LATENCIES_NAME)
-            .ofLongs()
             .setDescription(
                 "The latency measured from the moment that the RPC entered the Google data center until the RPC was completed.")
             .setUnit(MILLISECOND)
@@ -93,7 +90,6 @@ public static BuiltinMetricsTracerFactory create(
     firstResponseLatenciesHistogram =
         meter
             .histogramBuilder(FIRST_RESPONSE_LATENCIES_NAME)
-            .ofLongs()
             .setDescription(
                 "Latency from operation start until the response headers were received. The publishing of the measurement will be delayed until the attempt response has been received.")
             .setUnit(MILLISECOND)
@@ -101,7 +97,6 @@ public static BuiltinMetricsTracerFactory create(
     clientBlockingLatenciesHistogram =
         meter
             .histogramBuilder(CLIENT_BLOCKING_LATENCIES_NAME)
-            .ofLongs()
             .setDescription(
                 "The artificial latency introduced by the client to limit the number of outstanding requests. The publishing of the measurement will be delayed until the attempt trailers have been received.")
             .setUnit(MILLISECOND)
@@ -109,7 +104,6 @@ public static BuiltinMetricsTracerFactory create(
     applicationBlockingLatenciesHistogram =
         meter
             .histogramBuilder(APPLICATION_BLOCKING_LATENCIES_NAME)
-            .ofLongs()
             .setDescription(
                 "The latency of the client application consuming available response data.")
             .setUnit(MILLISECOND)
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java
index e8637c2726..01267a0c0c 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java
@@ -111,6 +111,9 @@ public class BuiltinMetricsIT {
 
   @Before
   public void setup() throws IOException {
+    // This test tests 2 things. End-to-end test using the default OTEL instance created by the
+    // client, and also end-to-end test using a custom OTEL instance set by the customer. In
+    // both tests, a BigtableCloudMonitoringExporter is created to export data to Cloud Monitoring.
     assume()
         .withMessage("Builtin metrics integration test is not supported by emulator")
         .that(testEnvRule.env())
@@ -134,6 +137,9 @@ public void setup() throws IOException {
                 AppProfile.SingleClusterRoutingPolicy.of(testEnvRule.env().getPrimaryClusterId()))
             .setIsolationPolicy(AppProfile.StandardIsolationPolicy.of(AppProfile.Priority.LOW)));
 
+    // When using the custom OTEL instance, we can also register a InMemoryMetricReader on the
+    // SdkMeterProvider to verify the data exported on Cloud Monitoring with the in memory metric
+    // data collected in InMemoryMetricReader.
     metricReader = InMemoryMetricReader.create();
 
     SdkMeterProviderBuilder meterProvider =
@@ -187,6 +193,8 @@ public void testBuiltinMetricsWithDefaultOTEL() throws Exception {
                 .addFamily("cf"));
     logger.info("Create default table: " + tableDefault.getId());
 
+    Instant start = Instant.now().minus(Duration.ofSeconds(10));
+
     // Send a MutateRow and ReadRows request and measure the latencies for these requests.
     clientDefault.mutateRow(
         RowMutation.create(tableDefault.getId(), "a-new-key").setCell("cf", "q", "abc"));
@@ -198,9 +206,10 @@ public void testBuiltinMetricsWithDefaultOTEL() throws Exception {
 
     ProjectName name = ProjectName.of(testEnvRule.env().getProjectId());
 
-    // Restrict time to last minutes and 5 minutes after the request
-    Instant start = Instant.now().minus(Duration.ofMinutes(1));
-    Instant end = start.plus(Duration.ofMinutes(6));
+    // Interval is set in the monarch request when query metric timestamps.
+    // Restrict it to before we send to request and 1 minute after we send the request. If
+    // it turns out to be still flaky we can increase the filter range.
+    Instant end = Instant.now().plus(Duration.ofMinutes(1));
     TimeInterval interval =
         TimeInterval.newBuilder()
             .setStartTime(Timestamps.fromMillis(start.toEpochMilli()))
@@ -222,7 +231,7 @@ public void testBuiltinMetricsWithDefaultOTEL() throws Exception {
               .setFilter(metricFilter)
               .setInterval(interval)
               .setView(ListTimeSeriesRequest.TimeSeriesView.FULL);
-      verifyMetrics(requestBuilder.build(), metricsPollingStopwatch, view, null);
+      verifyMetricsArePublished(requestBuilder.build(), metricsPollingStopwatch, view);
 
       // Verify that metrics are published for ReadRows request
       metricFilter =
@@ -233,7 +242,7 @@ public void testBuiltinMetricsWithDefaultOTEL() throws Exception {
               view, testEnvRule.env().getInstanceId(), tableDefault.getId(), appProfileDefault);
       requestBuilder.setFilter(metricFilter);
 
-      verifyMetrics(requestBuilder.build(), metricsPollingStopwatch, view, null);
+      verifyMetricsArePublished(requestBuilder.build(), metricsPollingStopwatch, view);
     }
   }
 
@@ -246,6 +255,7 @@ public void testBuiltinMetricsWithCustomOTEL() throws Exception {
                 .addFamily("cf"));
     logger.info("Create custom table: " + tableCustomOtel.getId());
 
+    Instant start = Instant.now().minus(Duration.ofSeconds(10));
     // Send a MutateRow and ReadRows request and measure the latencies for these requests.
     clientCustomOtel.mutateRow(
         RowMutation.create(tableCustomOtel.getId(), "a-new-key").setCell("cf", "q", "abc"));
@@ -260,9 +270,10 @@ public void testBuiltinMetricsWithCustomOTEL() throws Exception {
 
     Collection fromMetricReader = metricReader.collectAllMetrics();
 
-    // Restrict time to last minutes and 5 minutes after the request
-    Instant start = Instant.now().minus(Duration.ofMinutes(1));
-    Instant end = start.plus(Duration.ofMinutes(6));
+    // Interval is set in the monarch request when query metric timestamps.
+    // Restrict it to before we send to request and 1 minute after we send the request. If
+    // it turns out to be still flaky we can increase the filter range.
+    Instant end = start.plus(Duration.ofMinutes(1));
     TimeInterval interval =
         TimeInterval.newBuilder()
             .setStartTime(Timestamps.fromMillis(start.toEpochMilli()))
@@ -294,7 +305,9 @@ public void testBuiltinMetricsWithCustomOTEL() throws Exception {
               .setInterval(interval)
               .setView(ListTimeSeriesRequest.TimeSeriesView.FULL);
 
-      verifyMetrics(requestBuilder.build(), metricsPollingStopwatch, view, dataFromReader);
+      ListTimeSeriesResponse response =
+          verifyMetricsArePublished(requestBuilder.build(), metricsPollingStopwatch, view);
+      verifyMetricsWithMetricsReader(response, dataFromReader);
 
       // Verify that metrics are correct for ReadRows request
       metricFilter =
@@ -308,15 +321,13 @@ public void testBuiltinMetricsWithCustomOTEL() throws Exception {
               appProfileCustomOtel);
       requestBuilder.setFilter(metricFilter);
 
-      verifyMetrics(requestBuilder.build(), metricsPollingStopwatch, view, dataFromReader);
+      response = verifyMetricsArePublished(requestBuilder.build(), metricsPollingStopwatch, view);
+      verifyMetricsWithMetricsReader(response, dataFromReader);
     }
   }
 
-  private void verifyMetrics(
-      ListTimeSeriesRequest request,
-      Stopwatch metricsPollingStopwatch,
-      String view,
-      MetricData dataFromReader)
+  private ListTimeSeriesResponse verifyMetricsArePublished(
+      ListTimeSeriesRequest request, Stopwatch metricsPollingStopwatch, String view)
       throws Exception {
     ListTimeSeriesResponse response = metricClient.listTimeSeriesCallable().call(request);
     logger.log(
@@ -338,61 +349,62 @@ private void verifyMetrics(
         .that(response.getTimeSeriesCount())
         .isGreaterThan(0);
 
-    // Compare metric data with in memory metrics reader data if present
-    if (dataFromReader != null) {
-      for (TimeSeries ts : response.getTimeSeriesList()) {
-        Map attributesMap =
-            ImmutableMap.builder()
-                .putAll(ts.getResource().getLabelsMap())
-                .putAll(ts.getMetric().getLabelsMap())
-                .build();
-        AttributesBuilder attributesBuilder = Attributes.builder();
-        String streamingKey = BuiltinMetricsConstants.STREAMING_KEY.getKey();
-        attributesMap.forEach(
-            (k, v) -> {
-              if (!k.equals(streamingKey)) {
-                attributesBuilder.put(k, v);
-              }
-            });
-        if (attributesMap.containsKey(streamingKey)) {
-          attributesBuilder.put(
-              streamingKey, Boolean.parseBoolean(attributesMap.get(streamingKey)));
-        }
-        Attributes attributes = attributesBuilder.build();
-        verifyAttributes(dataFromReader, attributes);
-        long expectedValue = getAggregatedValue(dataFromReader, attributes);
-        Timestamp startTime = getStartTimeSeconds(dataFromReader, attributes);
-        assertThat(startTime.getSeconds()).isGreaterThan(0);
-        List point =
-            ts.getPointsList().stream()
-                .filter(
-                    p ->
-                        Timestamps.compare(p.getInterval().getStartTime(), startTime) >= 0
-                            && Timestamps.compare(
-                                    p.getInterval().getStartTime(),
-                                    Timestamps.add(
-                                        startTime,
-                                        com.google.protobuf.Duration.newBuilder()
-                                            .setSeconds(60)
-                                            .build()))
-                                < 0)
-                .collect(Collectors.toList());
-        if (point.size() > 0) {
-          long actualValue = (long) point.get(0).getValue().getDistributionValue().getMean();
-          assertWithMessage(
-                  "actual value does not match expected value, actual value "
-                      + actualValue
-                      + " expected value "
-                      + expectedValue
-                      + " actual start time "
-                      + point.get(0).getInterval().getStartTime()
-                      + " expected start time "
-                      + startTime)
-              .that(actualValue)
-              .isIn(
-                  Range.range(
-                      expectedValue - 1, BoundType.CLOSED, expectedValue + 1, BoundType.CLOSED));
-        }
+    return response;
+  }
+
+  private void verifyMetricsWithMetricsReader(
+      ListTimeSeriesResponse response, MetricData dataFromReader) {
+    for (TimeSeries ts : response.getTimeSeriesList()) {
+      Map attributesMap =
+          ImmutableMap.builder()
+              .putAll(ts.getResource().getLabelsMap())
+              .putAll(ts.getMetric().getLabelsMap())
+              .build();
+      AttributesBuilder attributesBuilder = Attributes.builder();
+      String streamingKey = BuiltinMetricsConstants.STREAMING_KEY.getKey();
+      attributesMap.forEach(
+          (k, v) -> {
+            if (!k.equals(streamingKey)) {
+              attributesBuilder.put(k, v);
+            }
+          });
+      if (attributesMap.containsKey(streamingKey)) {
+        attributesBuilder.put(streamingKey, Boolean.parseBoolean(attributesMap.get(streamingKey)));
+      }
+      Attributes attributes = attributesBuilder.build();
+      verifyAttributes(dataFromReader, attributes);
+      long expectedValue = getAggregatedValue(dataFromReader, attributes);
+      Timestamp startTime = getStartTimeSeconds(dataFromReader, attributes);
+      assertThat(startTime.getSeconds()).isGreaterThan(0);
+      List point =
+          ts.getPointsList().stream()
+              .filter(
+                  p ->
+                      Timestamps.compare(p.getInterval().getStartTime(), startTime) >= 0
+                          && Timestamps.compare(
+                                  p.getInterval().getStartTime(),
+                                  Timestamps.add(
+                                      startTime,
+                                      com.google.protobuf.Duration.newBuilder()
+                                          .setSeconds(60)
+                                          .build()))
+                              < 0)
+              .collect(Collectors.toList());
+      if (point.size() > 0) {
+        long actualValue = (long) point.get(0).getValue().getDistributionValue().getMean();
+        assertWithMessage(
+                "actual value does not match expected value, actual value "
+                    + actualValue
+                    + " expected value "
+                    + expectedValue
+                    + " actual start time "
+                    + point.get(0).getInterval().getStartTime()
+                    + " expected start time "
+                    + startTime)
+            .that(actualValue)
+            .isIn(
+                Range.range(
+                    expectedValue - 1, BoundType.CLOSED, expectedValue + 1, BoundType.CLOSED));
       }
     }
   }

From 523724ba0e5750255fe11f87c739933aec6378a9 Mon Sep 17 00:00:00 2001
From: Mattie Fu 
Date: Tue, 20 Feb 2024 12:26:11 -0500
Subject: [PATCH 32/34] address comments

---
 .../data/v2/BigtableDataSettings.java         |  9 ++-
 .../stub/metrics/BuiltinMetricsConstants.java |  2 +-
 .../v2/stub/metrics/BuiltinMetricsTracer.java | 60 +++++++++++--------
 .../v2/BigtableDataClientFactoryTest.java     |  4 --
 .../bigtable/data/v2/it/BuiltinMetricsIT.java | 20 +++----
 .../stub/metrics/BuiltinMetricsTestUtils.java |  2 +-
 .../metrics/BuiltinMetricsTracerTest.java     | 20 +++----
 7 files changed, 59 insertions(+), 58 deletions(-)

diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java
index 9ee8648698..cd625963be 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java
@@ -74,7 +74,10 @@ public final class BigtableDataSettings {
 
   private static final Logger LOGGER = Logger.getLogger(BigtableDataSettings.class.getName());
   private static final String BIGTABLE_EMULATOR_HOST_ENV_VAR = "BIGTABLE_EMULATOR_HOST";
-  @Nullable private static Credentials credentials;
+  // This is the legacy credential override used in the deprecated enableBuiltinMetrics method to
+  // override the default credentials set on the Bigtable client. Keeping it for backward
+  // compatibility.
+  @Deprecated @Nullable private static Credentials legacyMetricCredentialOverride;
 
   private final EnhancedBigtableStubSettings stubSettings;
 
@@ -214,13 +217,13 @@ public static void enableBuiltinMetrics() throws IOException {}
    *     on how to enable or disable built-in metrics.
    */
   public static void enableBuiltinMetrics(Credentials credentials) throws IOException {
-    BigtableDataSettings.credentials = credentials;
+    BigtableDataSettings.legacyMetricCredentialOverride = credentials;
   }
 
   /** Get the metrics credentials if it's set by {@link #enableBuiltinMetrics(Credentials)}. */
   @InternalApi
   public static Credentials getMetricsCredentials() {
-    return credentials;
+    return legacyMetricCredentialOverride;
   }
 
   /** Returns the target project id. */
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java
index 3b307166aa..807191b7e2 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsConstants.java
@@ -135,7 +135,7 @@ public static Map getAllViews() {
         AGGREGATION_WITH_MILLIS_HISTOGRAM,
         InstrumentType.HISTOGRAM,
         "ms",
-        ImmutableSet.of(STREAMING_KEY, STATUS_KEY));
+        ImmutableSet.of(STATUS_KEY));
     defineView(
         views,
         FIRST_RESPONSE_LATENCIES_NAME,
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
index 844f159c31..ef5e517e68 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
@@ -84,8 +84,10 @@ class BuiltinMetricsTracer extends BigtableTracer {
 
   private Long serverLatencies = null;
 
-  private final double toMs = 1e-6;
-
+  // OpenCensus (and server) histogram buckets use [start, end), however OpenTelemetry uses (start,
+  // end]. To work around this, we measure all the latencies in nanoseconds and convert them
+  // to milliseconds and use DoubleHistogram. This should minimize the chance of a data
+  // point fall on the bucket boundary that causes off by one errors.
   private final DoubleHistogram operationLatenciesHistogram;
   private final DoubleHistogram attemptLatenciesHistogram;
   private final DoubleHistogram serverLatenciesHistogram;
@@ -278,6 +280,8 @@ private void recordOperationCompletion(@Nullable Throwable status) {
     boolean isStreaming = operationType == OperationType.ServerStreaming;
     String statusStr = Util.extractStatus(status);
 
+    // Publish metric data with all the attributes. The attributes get filtered in
+    // BuiltinMetricsConstants when we construct the views.
     Attributes attributes =
         baseAttributes
             .toBuilder()
@@ -286,6 +290,8 @@ private void recordOperationCompletion(@Nullable Throwable status) {
             .put(ZONE_ID_KEY, zone)
             .put(METHOD_KEY, spanName.toString())
             .put(CLIENT_NAME_KEY, NAME)
+            .put(STREAMING_KEY, isStreaming)
+            .put(STATUS_KEY, statusStr)
             .build();
 
     long operationLatencyNano = operationTimer.elapsed(TimeUnit.NANOSECONDS);
@@ -293,22 +299,19 @@ private void recordOperationCompletion(@Nullable Throwable status) {
     // Only record when retry count is greater than 0 so the retry
     // graph will be less confusing
     if (attemptCount > 1) {
-      retryCounter.add(attemptCount - 1, attributes.toBuilder().put(STATUS_KEY, statusStr).build());
+      retryCounter.add(attemptCount - 1, attributes);
     }
 
-    // serverLatencyTimer should already be stopped in recordAttemptCompletion
-    operationLatenciesHistogram.record(
-        Duration.ofNanos(operationLatencyNano).toMillis(),
-        attributes.toBuilder().put(STREAMING_KEY, isStreaming).put(STATUS_KEY, statusStr).build());
+    operationLatenciesHistogram.record(convertToMs(operationLatencyNano), attributes);
 
+    // serverLatencyTimer should already be stopped in recordAttemptCompletion
     long applicationLatencyNano = operationLatencyNano - totalServerLatencyNano.get();
-    applicationBlockingLatenciesHistogram.record(applicationLatencyNano * toMs, attributes);
+    applicationBlockingLatenciesHistogram.record(convertToMs(applicationLatencyNano), attributes);
 
     if (operationType == OperationType.ServerStreaming
         && spanName.getMethodName().equals("ReadRows")) {
       firstResponseLatenciesHistogram.record(
-          firstResponsePerOpTimer.elapsed(TimeUnit.NANOSECONDS) * toMs,
-          attributes.toBuilder().put(STATUS_KEY, Util.extractStatus(status)).build());
+          convertToMs(firstResponsePerOpTimer.elapsed(TimeUnit.NANOSECONDS)), attributes);
     }
   }
 
@@ -326,6 +329,15 @@ private void recordAttemptCompletion(@Nullable Throwable status) {
 
     boolean isStreaming = operationType == OperationType.ServerStreaming;
 
+    // Patch the status until it's fixed in gax. When an attempt failed,
+    // it'll throw a ServerStreamingAttemptException. Unwrap the exception
+    // so it could get processed by extractStatus
+    if (status instanceof ServerStreamingAttemptException) {
+      status = status.getCause();
+    }
+
+    String statusStr = Util.extractStatus(status);
+
     Attributes attributes =
         baseAttributes
             .toBuilder()
@@ -334,29 +346,25 @@ private void recordAttemptCompletion(@Nullable Throwable status) {
             .put(ZONE_ID_KEY, zone)
             .put(METHOD_KEY, spanName.toString())
             .put(CLIENT_NAME_KEY, NAME)
+            .put(STREAMING_KEY, isStreaming)
+            .put(STATUS_KEY, statusStr)
             .build();
 
-    clientBlockingLatenciesHistogram.record(totalClientBlockingTime.get() * toMs, attributes);
-
-    // Patch the status until it's fixed in gax. When an attempt failed,
-    // it'll throw a ServerStreamingAttemptException. Unwrap the exception
-    // so it could get processed by extractStatus
-    if (status instanceof ServerStreamingAttemptException) {
-      status = status.getCause();
-    }
-
-    String statusStr = Util.extractStatus(status);
+    clientBlockingLatenciesHistogram.record(convertToMs(totalClientBlockingTime.get()), attributes);
 
     attemptLatenciesHistogram.record(
-        attemptTimer.elapsed(TimeUnit.NANOSECONDS) * toMs,
-        attributes.toBuilder().put(STREAMING_KEY, isStreaming).put(STATUS_KEY, statusStr).build());
+        convertToMs(attemptTimer.elapsed(TimeUnit.NANOSECONDS)), attributes);
 
     if (serverLatencies != null) {
-      serverLatenciesHistogram.record(
-          serverLatencies, attributes.toBuilder().put(STATUS_KEY, statusStr).build());
-      connectivityErrorCounter.add(0, attributes.toBuilder().put(STATUS_KEY, statusStr).build());
+      serverLatenciesHistogram.record(serverLatencies, attributes);
+      connectivityErrorCounter.add(0, attributes);
     } else {
-      connectivityErrorCounter.add(1, attributes.toBuilder().put(STATUS_KEY, statusStr).build());
+      connectivityErrorCounter.add(1, attributes);
     }
   }
+
+  private double convertToMs(long nanoSeconds) {
+    double toMs = 1e-6;
+    return nanoSeconds * toMs;
+  }
 }
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java
index 490450b0d7..16f9b25c5f 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientFactoryTest.java
@@ -171,10 +171,6 @@ public void tearDown() {
   @Test
   public void testNewClientsShareTransportChannel() throws Exception {
     // Create 3 lightweight clients
-
-    // Builtin metrics will call getCredentialsProvider at which point it'll be a
-    // FixedCredentialProvider.
-    // So disabling in the test code it's fine.
     try (BigtableDataClientFactory factory =
             BigtableDataClientFactory.create(
                 defaultSettings
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java
index 01267a0c0c..484cbb26dc 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BuiltinMetricsIT.java
@@ -281,9 +281,9 @@ public void testBuiltinMetricsWithCustomOTEL() throws Exception {
             .build();
 
     for (String view : VIEWS) {
-      String otelMetricName = "bigtable.googleapis.com/internal/client/" + view;
+      String otelMetricName = view;
       if (view.equals("application_blocking_latencies")) {
-        otelMetricName = "bigtable.googleapis.com/internal/client/application_latencies";
+        otelMetricName = "application_latencies";
       }
       MetricData dataFromReader = getMetricData(fromMetricReader, otelMetricName);
 
@@ -330,16 +330,16 @@ private ListTimeSeriesResponse verifyMetricsArePublished(
       ListTimeSeriesRequest request, Stopwatch metricsPollingStopwatch, String view)
       throws Exception {
     ListTimeSeriesResponse response = metricClient.listTimeSeriesCallable().call(request);
-    logger.log(
-        Level.INFO,
-        "Checking for view "
-            + view
-            + ", has timeseries="
-            + response.getTimeSeriesCount()
-            + " stopwatch elapsed "
-            + metricsPollingStopwatch.elapsed(TimeUnit.MINUTES));
     while (response.getTimeSeriesCount() == 0
         && metricsPollingStopwatch.elapsed(TimeUnit.MINUTES) < 10) {
+      logger.log(
+          Level.INFO,
+          "Checking for view "
+              + view
+              + ", has timeseries="
+              + response.getTimeSeriesCount()
+              + " stopwatch elapsed "
+              + metricsPollingStopwatch.elapsed(TimeUnit.MINUTES));
       // Call listTimeSeries every minute
       Thread.sleep(Duration.ofMinutes(1).toMillis());
       response = metricClient.listTimeSeriesCallable().call(request);
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java
index 6184f5afb2..af402952da 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTestUtils.java
@@ -36,7 +36,7 @@ private BuiltinMetricsTestUtils() {}
   public static MetricData getMetricData(Collection allMetricData, String metricName) {
     List metricDataList =
         allMetricData.stream()
-            .filter(md -> md.getName().equals(metricName))
+            .filter(md -> md.getName().equals(BuiltinMetricsConstants.METER_NAME + metricName))
             .collect(Collectors.toList());
     if (metricDataList.size() == 0) {
       allMetricData.stream().forEach(md -> System.out.println(md.getName()));
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java
index 12e9978b8a..17f5001820 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java
@@ -21,7 +21,6 @@
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLIENT_NAME_KEY;
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CLUSTER_ID_KEY;
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.CONNECTIVITY_ERROR_COUNT_NAME;
-import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METER_NAME;
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.METHOD_KEY;
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.OPERATION_LATENCIES_NAME;
 import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsConstants.RETRY_COUNT_NAME;
@@ -85,11 +84,10 @@
 import io.grpc.stub.ServerCallStreamObserver;
 import io.grpc.stub.StreamObserver;
 import io.opentelemetry.api.common.Attributes;
-import io.opentelemetry.api.metrics.Meter;
 import io.opentelemetry.sdk.OpenTelemetrySdk;
 import io.opentelemetry.sdk.metrics.SdkMeterProvider;
+import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
 import io.opentelemetry.sdk.metrics.data.MetricData;
-import io.opentelemetry.sdk.resources.Resource;
 import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -134,8 +132,6 @@ public class BuiltinMetricsTracerTest {
 
   private EnhancedBigtableStub stub;
 
-  private BuiltinMetricsTracerFactory facotry;
-
   private int batchElementCount = 2;
 
   private Attributes baseAttributes;
@@ -153,16 +149,14 @@ public void setUp() throws Exception {
             .put(BuiltinMetricsConstants.APP_PROFILE_KEY, APP_PROFILE_ID)
             .build();
 
-    SdkMeterProvider meterProvider =
-        SdkMeterProvider.builder()
-            .registerMetricReader(metricReader)
-            .setResource(Resource.create(baseAttributes))
-            .build();
+    SdkMeterProviderBuilder meterProvider =
+        SdkMeterProvider.builder().registerMetricReader(metricReader);
 
-    Meter meter = meterProvider.get(METER_NAME);
+    BuiltinMetricsView.registerBuiltinMetrics(PROJECT_ID, meterProvider);
 
-    OpenTelemetrySdk otel = OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build();
-    facotry = BuiltinMetricsTracerFactory.create(otel, baseAttributes);
+    OpenTelemetrySdk otel =
+        OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build();
+    BuiltinMetricsTracerFactory facotry = BuiltinMetricsTracerFactory.create(otel, baseAttributes);
 
     // Add an interceptor to add server-timing in headers
     ServerInterceptor trailersInterceptor =

From db6c49649cad90c5121327c69153ffa9f474c2f6 Mon Sep 17 00:00:00 2001
From: Mattie Fu 
Date: Tue, 20 Feb 2024 12:37:16 -0500
Subject: [PATCH 33/34] fix test

---
 .../data/v2/stub/metrics/BuiltinMetricsTracerTest.java    | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java
index 17f5001820..3a601e5036 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java
@@ -85,8 +85,10 @@
 import io.grpc.stub.StreamObserver;
 import io.opentelemetry.api.common.Attributes;
 import io.opentelemetry.sdk.OpenTelemetrySdk;
+import io.opentelemetry.sdk.metrics.InstrumentSelector;
 import io.opentelemetry.sdk.metrics.SdkMeterProvider;
 import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
+import io.opentelemetry.sdk.metrics.View;
 import io.opentelemetry.sdk.metrics.data.MetricData;
 import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
 import java.util.ArrayList;
@@ -94,6 +96,7 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -152,7 +155,10 @@ public void setUp() throws Exception {
     SdkMeterProviderBuilder meterProvider =
         SdkMeterProvider.builder().registerMetricReader(metricReader);
 
-    BuiltinMetricsView.registerBuiltinMetrics(PROJECT_ID, meterProvider);
+    for (Map.Entry entry :
+        BuiltinMetricsConstants.getAllViews().entrySet()) {
+      meterProvider.registerView(entry.getKey(), entry.getValue());
+    }
 
     OpenTelemetrySdk otel =
         OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build();

From 8f511cff8633b00b0cd8223a0543964dee46b0b4 Mon Sep 17 00:00:00 2001
From: Mattie Fu 
Date: Wed, 21 Feb 2024 10:44:35 -0500
Subject: [PATCH 34/34] add toString

---
 .../google/cloud/bigtable/data/v2/BigtableDataSettings.java  | 1 +
 .../bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java  | 2 +-
 .../v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java  | 5 +++++
 .../data/v2/stub/metrics/DefaultMetricsProvider.java         | 5 +++++
 .../bigtable/data/v2/stub/metrics/NoopMetricsProvider.java   | 5 +++++
 5 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java
index cd625963be..928159aa6d 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataSettings.java
@@ -216,6 +216,7 @@ public static void enableBuiltinMetrics() throws IOException {}
    *     now. Please refer {@link BigtableDataSettings.Builder#setMetricsProvider(MetricsProvider)}
    *     on how to enable or disable built-in metrics.
    */
+  @Deprecated
   public static void enableBuiltinMetrics(Credentials credentials) throws IOException {
     BigtableDataSettings.legacyMetricCredentialOverride = credentials;
   }
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
index ef5e517e68..6bbed1363d 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracer.java
@@ -363,7 +363,7 @@ private void recordAttemptCompletion(@Nullable Throwable status) {
     }
   }
 
-  private double convertToMs(long nanoSeconds) {
+  private static double convertToMs(long nanoSeconds) {
     double toMs = 1e-6;
     return nanoSeconds * toMs;
   }
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java
index 0dd21d31e8..bcd1fe488f 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/CustomOpenTelemetryMetricsProvider.java
@@ -61,4 +61,9 @@ private CustomOpenTelemetryMetricsProvider(OpenTelemetry otel) {
   public OpenTelemetry getOpenTelemetry() {
     return otel;
   }
+
+  @Override
+  public String toString() {
+    return otel.toString();
+  }
 }
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java
index f577f1ebe3..0f3ee0c98f 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/DefaultMetricsProvider.java
@@ -27,4 +27,9 @@ public final class DefaultMetricsProvider implements MetricsProvider {
   public static DefaultMetricsProvider INSTANCE = new DefaultMetricsProvider();
 
   private DefaultMetricsProvider() {}
+
+  @Override
+  public String toString() {
+    return "DefaultMetricsProvider";
+  }
 }
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java
index 1ef694e1e8..7b5bcbc50a 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/NoopMetricsProvider.java
@@ -26,4 +26,9 @@ public final class NoopMetricsProvider implements MetricsProvider {
   public static NoopMetricsProvider INSTANCE = new NoopMetricsProvider();
 
   private NoopMetricsProvider() {}
+
+  @Override
+  public String toString() {
+    return "NoopMetricsProvider";
+  }
 }