Skip to content

Commit

Permalink
feat: add built in metrics measure and views
Browse files Browse the repository at this point in the history
  • Loading branch information
mutianf committed May 11, 2022
1 parent cf8b38b commit 0e91197
Show file tree
Hide file tree
Showing 8 changed files with 1,110 additions and 28 deletions.
35 changes: 33 additions & 2 deletions google-cloud-bigtable-stats/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@
</dependencyManagement>

<dependencies>
<dependency>
<groupId>com.google.api</groupId>
<artifactId>gax</artifactId>
</dependency>
<dependency>
<groupId>com.google.api</groupId>
<artifactId>api-common</artifactId>
</dependency>

<dependency>
<groupId>io.opencensus</groupId>
<artifactId>opencensus-api</artifactId>
Expand All @@ -38,8 +47,30 @@
<artifactId>opencensus-impl</artifactId>
</dependency>
<dependency>
<groupId>io.opencensus</groupId>
<artifactId>opencensus-exporter-stats-stackdriver</artifactId>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>


<dependency>
<groupId>com.google.truth.extensions</groupId>
<artifactId>truth-proto-extension</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.truth</groupId>
<artifactId>truth</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-api</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright 2022 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.stats;

import static io.opencensus.stats.Measure.MeasureLong;

import com.google.api.core.InternalApi;
import io.opencensus.tags.TagKey;

@InternalApi("For internal use only")
public class BuiltinMeasureConstants {
// TagKeys
public static final TagKey PROJECT_ID = TagKey.create("project_id");
public static final TagKey INSTANCE_ID = TagKey.create("instance_id");
public static final TagKey APP_PROFILE = TagKey.create("app_profile");
public static final TagKey METHOD = TagKey.create("method");
public static final TagKey STREAMING = TagKey.create("streaming");
public static final TagKey STATUS = TagKey.create("status");
public static final TagKey CLIENT_NAME = TagKey.create("client_name");
public static final TagKey CLIENT_ID = TagKey.create("client_id");

// Monitored resource TagKeys
public static final TagKey TABLE = TagKey.create("table");
public static final TagKey CLUSTER = TagKey.create("cluster");
public static final TagKey ZONE = TagKey.create("zone");

// Units
private static final String COUNT = "1";
private static final String MILLISECOND = "ms";

// Measurements
static final MeasureLong OPERATION_LATENCIES =
MeasureLong.create(
"bigtable.googleapis.com/internal/client/operation_latencies",
"Total time until final operation success or failure, including retries and backoff.",
MILLISECOND);

static final MeasureLong ATTEMPT_LATENCIES =
MeasureLong.create(
"bigtable.googleapis.com/internal/client/attempt_latencies",
"Client observed latency per RPC attempt.",
MILLISECOND);

static final MeasureLong RETRY_COUNT =
MeasureLong.create(
"bigtable.googleapis.com/internal/client/retry_count",
"The number of additional RPCs sent after the initial attempt.",
COUNT);

static final MeasureLong FIRST_RESPONSE_LATENCIES =
MeasureLong.create(
"bigtable.googleapis.com/internal/client/first_response_latencies",
"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.",
MILLISECOND);

static final MeasureLong SERVER_LATENCIES =
MeasureLong.create(
"bigtable.googleapis.com/internal/client/server_latencies",
"The latency measured from the moment that the RPC entered the Google data center until the RPC was completed.",
MILLISECOND);

static final MeasureLong CONNECTIVITY_ERROR_COUNT =
MeasureLong.create(
"bigtable.googleapis.com/internal/client/connectivity_error_count",
"Number of requests that failed to reach the Google datacenter. (Requests without google response headers).",
COUNT);

static final MeasureLong APPLICATION_LATENCIES =
MeasureLong.create(
"bigtable.googleapis.com/internal/client/application_latencies",
"The latency of the client application consuming available response data.",
MILLISECOND);

static final MeasureLong THROTTLING_LATENCIES =
MeasureLong.create(
"bigtable.googleapis.com/internal/client/throttling_latencies",
"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.",
MILLISECOND);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
* Copyright 2022 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.stats;

import com.google.api.core.InternalApi;
import com.google.api.gax.tracing.ApiTracerFactory.OperationType;
import com.google.api.gax.tracing.SpanName;
import io.opencensus.stats.MeasureMap;
import io.opencensus.stats.StatsRecorder;
import io.opencensus.tags.TagContext;
import io.opencensus.tags.TagContextBuilder;
import io.opencensus.tags.TagKey;
import io.opencensus.tags.TagValue;
import io.opencensus.tags.Tagger;
import io.opencensus.tags.Tags;
import java.util.Map;

/** Add built-in metrics to the measure map * */
@InternalApi("For internal use only")
public class BuiltinMetricsRecorder {

private final OperationType operationType;

private final Tagger tagger;
private final StatsRecorder statsRecorder;
private final TagContext parentContext;
private final SpanName spanName;
private final Map<String, String> statsAttributes;

private MeasureMap attemptLevelNoStreaming;
private MeasureMap attemptLevelWithStreaming;
private MeasureMap operationLevelNoStreaming;
private MeasureMap operationLevelWithStreaming;

public BuiltinMetricsRecorder(
OperationType operationType,
SpanName spanName,
Map<String, String> statsAttributes,
StatsWrapper builtinMetricsWrapper) {
this.operationType = operationType;
this.tagger = Tags.getTagger();
this.statsRecorder = builtinMetricsWrapper.getStatsRecorder();
this.spanName = spanName;
this.parentContext = tagger.getCurrentTagContext();
this.statsAttributes = statsAttributes;

this.attemptLevelNoStreaming = statsRecorder.newMeasureMap();
this.attemptLevelWithStreaming = statsRecorder.newMeasureMap();
this.operationLevelNoStreaming = statsRecorder.newMeasureMap();
this.operationLevelWithStreaming = statsRecorder.newMeasureMap();
}

public void recordAttemptLevelWithoutStreaming(
String status, String tableId, String zone, String cluster) {
TagContextBuilder tagCtx =
newTagContextBuilder(tableId, zone, cluster)
.putLocal(BuiltinMeasureConstants.STATUS, TagValue.create(status));

attemptLevelNoStreaming.record(tagCtx.build());
}

public void recordAttemptLevelWithStreaming(
String status, String tableId, String zone, String cluster) {
TagContextBuilder tagCtx =
newTagContextBuilder(tableId, zone, cluster)
.putLocal(BuiltinMeasureConstants.STATUS, TagValue.create(status));

if (operationType == OperationType.ServerStreaming
&& spanName.getMethodName().equals("ReadRows")) {
tagCtx.putLocal(BuiltinMeasureConstants.STREAMING, TagValue.create("true"));
} else {
tagCtx.putLocal(BuiltinMeasureConstants.STREAMING, TagValue.create("false"));
}

attemptLevelWithStreaming.record(tagCtx.build());
}

public void recordOperationLevelWithoutStreaming(
String status, String tableId, String zone, String cluster) {
TagContextBuilder tagCtx =
newTagContextBuilder(tableId, zone, cluster)
.putLocal(BuiltinMeasureConstants.STATUS, TagValue.create(status));

operationLevelNoStreaming.record(tagCtx.build());
}

public void recordOperationLevelWithStreaming(
String status, String tableId, String zone, String cluster) {
TagContextBuilder tagCtx =
newTagContextBuilder(tableId, zone, cluster)
.putLocal(BuiltinMeasureConstants.STATUS, TagValue.create(status));

if (operationType == OperationType.ServerStreaming
&& spanName.getMethodName().equals("ReadRows")) {
tagCtx.putLocal(BuiltinMeasureConstants.STREAMING, TagValue.create("true"));
} else {
tagCtx.putLocal(BuiltinMeasureConstants.STREAMING, TagValue.create("false"));
}

operationLevelWithStreaming.record(tagCtx.build());
}

public void recordOperationLatencies(long operationLatency) {
operationLevelWithStreaming.put(BuiltinMeasureConstants.OPERATION_LATENCIES, operationLatency);
}

public void recordAttemptLatency(long attemptLatency) {
attemptLevelWithStreaming.put(BuiltinMeasureConstants.ATTEMPT_LATENCIES, attemptLatency);
}

public void recordRetryCount(int attemptCount) {
operationLevelNoStreaming.put(BuiltinMeasureConstants.RETRY_COUNT, attemptCount);
}

public void recordApplicationLatency(long applicationLatency) {
operationLevelWithStreaming.put(
BuiltinMeasureConstants.APPLICATION_LATENCIES, applicationLatency);
}

public void recordFirstResponseLatency(long firstResponseLatency) {
operationLevelNoStreaming.put(
BuiltinMeasureConstants.FIRST_RESPONSE_LATENCIES, firstResponseLatency);
}

public void recordGfeLatencies(long serverLatency) {
attemptLevelWithStreaming.put(BuiltinMeasureConstants.SERVER_LATENCIES, serverLatency);
}

public void recordGfeMissingHeaders(long connectivityErrors) {
attemptLevelNoStreaming.put(
BuiltinMeasureConstants.CONNECTIVITY_ERROR_COUNT, connectivityErrors);
}

public void recordBatchRequestThrottled(
long throttledTimeMs, String tableId, String zone, String cluster) {
MeasureMap measures =
statsRecorder
.newMeasureMap()
.put(BuiltinMeasureConstants.THROTTLING_LATENCIES, throttledTimeMs);
measures.record(newTagContextBuilder(tableId, zone, cluster).build());
}

private TagContextBuilder newTagContextBuilder(String tableId, String zone, String cluster) {
TagContextBuilder tagContextBuilder =
tagger
.toBuilder(parentContext)
.putLocal(BuiltinMeasureConstants.CLIENT_NAME, TagValue.create("bigtable-java"))
.putLocal(BuiltinMeasureConstants.METHOD, TagValue.create(spanName.toString()))
.putLocal(BuiltinMeasureConstants.TABLE, TagValue.create(tableId))
.putLocal(BuiltinMeasureConstants.ZONE, TagValue.create(zone))
.putLocal(BuiltinMeasureConstants.CLUSTER, TagValue.create(cluster));
for (Map.Entry<String, String> entry : statsAttributes.entrySet()) {
tagContextBuilder.putLocal(TagKey.create(entry.getKey()), TagValue.create(entry.getValue()));
}
return tagContextBuilder;
}
}
Loading

0 comments on commit 0e91197

Please sign in to comment.